diff --git a/android/GKI_VERSION b/android/GKI_VERSION index 3ecf1f8c3cd4..1514165b9a1f 100644 --- a/android/GKI_VERSION +++ b/android/GKI_VERSION @@ -1 +1 @@ -LTS_5.4.147_983a7e7c981b +LTS_5.4.147_f2bbf1b7baa5 diff --git a/arch/arm/configs/vendor/sdxnightjar-debug.config b/arch/arm/configs/vendor/sdxnightjar-debug.config index 9fa546194d67..3f49f7e71786 100644 --- a/arch/arm/configs/vendor/sdxnightjar-debug.config +++ b/arch/arm/configs/vendor/sdxnightjar-debug.config @@ -810,9 +810,7 @@ CONFIG_SND_DYNAMIC_MINORS=y # CONFIG_SND_AUDIO_GRAPH_CARD is not set # CONFIG_HID_PRODIKEYS is not set # CONFIG_USB_CONFIGFS_F_AUDIO_SRC is not set -# CONFIG_USB_CONFIGFS_F_UAC1 is not set # CONFIG_USB_CONFIGFS_F_UAC1_LEGACY is not set -# CONFIG_USB_CONFIGFS_F_UAC2 is not set # CONFIG_USB_CONFIGFS_F_MIDI is not set CONFIG_PINCTRL_QCOM_SPMI_PMIC=y CONFIG_MFD_SPMI_PMIC=y diff --git a/arch/arm/configs/vendor/sdxnightjar.config b/arch/arm/configs/vendor/sdxnightjar.config index 6b5bf80c596f..86a9b16bd507 100644 --- a/arch/arm/configs/vendor/sdxnightjar.config +++ b/arch/arm/configs/vendor/sdxnightjar.config @@ -850,12 +850,12 @@ CONFIG_SND_DYNAMIC_MINORS=y # CONFIG_SND_AUDIO_GRAPH_CARD is not set # CONFIG_HID_PRODIKEYS is not set # CONFIG_USB_CONFIGFS_F_AUDIO_SRC is not set -# CONFIG_USB_CONFIGFS_F_UAC1 is not set # CONFIG_USB_CONFIGFS_F_UAC1_LEGACY is not set -# CONFIG_USB_CONFIGFS_F_UAC2 is not set # CONFIG_USB_CONFIGFS_F_MIDI is not set # CONFIG_MEDIA_SUPPORT is not set # CONFIG_SND_SOC_TLV320AIC3X is not set +CONFIG_USB_CONFIGFS_F_UAC1=y +CONFIG_USB_CONFIGFS_F_UAC2=y CONFIG_PINCTRL_QCOM_SPMI_PMIC=y CONFIG_MFD_SPMI_PMIC=y # CONFIG_QCOM_COINCELL is not set diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c index 6fa42e9c4e6f..234998f196d4 100644 --- a/arch/x86/pci/mmconfig-shared.c +++ b/arch/x86/pci/mmconfig-shared.c @@ -425,7 +425,7 @@ static acpi_status find_mboard_resource(acpi_handle handle, u32 lvl, return AE_OK; } -static bool is_acpi_reserved(u64 start, u64 end, unsigned not_used) +static bool is_acpi_reserved(u64 start, u64 end, enum e820_type not_used) { struct resource mcfg_res; @@ -442,7 +442,7 @@ static bool is_acpi_reserved(u64 start, u64 end, unsigned not_used) return mcfg_res.flags; } -typedef bool (*check_reserved_t)(u64 start, u64 end, unsigned type); +typedef bool (*check_reserved_t)(u64 start, u64 end, enum e820_type type); static bool __ref is_mmconf_reserved(check_reserved_t is_reserved, struct pci_mmcfg_region *cfg, diff --git a/drivers/base/power/wakeup_stats.c b/drivers/base/power/wakeup_stats.c index c7734914d914..5568e25d7c9c 100644 --- a/drivers/base/power/wakeup_stats.c +++ b/drivers/base/power/wakeup_stats.c @@ -42,7 +42,7 @@ static ssize_t active_time_ms_show(struct device *dev, ktime_t active_time = ws->active ? ktime_sub(ktime_get(), ws->last_time) : 0; - return sprintf(buf, "%lld\n", ktime_to_ms(active_time)); + return sysfs_emit(buf, "%lld\n", ktime_to_ms(active_time)); } static DEVICE_ATTR_RO(active_time_ms); @@ -57,7 +57,7 @@ static ssize_t total_time_ms_show(struct device *dev, active_time = ktime_sub(ktime_get(), ws->last_time); total_time = ktime_add(total_time, active_time); } - return sprintf(buf, "%lld\n", ktime_to_ms(total_time)); + return sysfs_emit(buf, "%lld\n", ktime_to_ms(total_time)); } static DEVICE_ATTR_RO(total_time_ms); @@ -73,7 +73,7 @@ static ssize_t max_time_ms_show(struct device *dev, if (active_time > max_time) max_time = active_time; } - return sprintf(buf, "%lld\n", ktime_to_ms(max_time)); + return sysfs_emit(buf, "%lld\n", ktime_to_ms(max_time)); } static DEVICE_ATTR_RO(max_time_ms); @@ -82,7 +82,7 @@ static ssize_t last_change_ms_show(struct device *dev, { struct wakeup_source *ws = dev_get_drvdata(dev); - return sprintf(buf, "%lld\n", ktime_to_ms(ws->last_time)); + return sysfs_emit(buf, "%lld\n", ktime_to_ms(ws->last_time)); } static DEVICE_ATTR_RO(last_change_ms); @@ -91,7 +91,7 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr, { struct wakeup_source *ws = dev_get_drvdata(dev); - return sprintf(buf, "%s\n", ws->name); + return sysfs_emit(buf, "%s\n", ws->name); } static DEVICE_ATTR_RO(name); @@ -106,7 +106,7 @@ static ssize_t prevent_suspend_time_ms_show(struct device *dev, prevent_sleep_time = ktime_add(prevent_sleep_time, ktime_sub(ktime_get(), ws->start_prevent_time)); } - return sprintf(buf, "%lld\n", ktime_to_ms(prevent_sleep_time)); + return sysfs_emit(buf, "%lld\n", ktime_to_ms(prevent_sleep_time)); } static DEVICE_ATTR_RO(prevent_suspend_time_ms); diff --git a/drivers/bus/mhi/core/mhi_boot.c b/drivers/bus/mhi/core/mhi_boot.c index fa55209e409d..3f7d2a84748c 100644 --- a/drivers/bus/mhi/core/mhi_boot.c +++ b/drivers/bus/mhi/core/mhi_boot.c @@ -143,6 +143,17 @@ bool mhi_scan_rddm_cookie(struct mhi_controller *mhi_cntrl, u32 cookie) { int ret; u32 val; + int i; + bool result = false; + struct { + char *name; + u32 offset; + } error_reg[] = { + { "ERROR_DBG1", BHI_ERRDBG1 }, + { "ERROR_DBG2", BHI_ERRDBG2 }, + { "ERROR_DBG3", BHI_ERRDBG3 }, + { NULL }, + }; if (!mhi_cntrl->rddm_supported || !cookie) return false; @@ -152,15 +163,23 @@ bool mhi_scan_rddm_cookie(struct mhi_controller *mhi_cntrl, u32 cookie) if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) return false; - ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_ERRDBG2, &val); - if (ret) - return false; + /* look for an RDDM cookie match in any of the error debug registers */ + for (i = 0; error_reg[i].name; i++) { + ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, + error_reg[i].offset, &val); + if (ret) + break; + MHI_CNTRL_LOG("reg:%s value:0x%x\n", error_reg[i].name, val); - MHI_CNTRL_LOG("BHI_ERRDBG2 value:0x%x\n", val); - if (val == cookie) - return true; + if (!(val ^ cookie)) { + MHI_CNTRL_ERR("RDDM cookie found in %s\n", + error_reg[i].name); + return true; + } + } - return false; + MHI_CNTRL_ERR("RDDM cookie not found\n"); + return result; } EXPORT_SYMBOL(mhi_scan_rddm_cookie); diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index 05cfe33e6987..0fe92ba12568 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -5350,6 +5350,7 @@ static ssize_t fastrpc_debugfs_read(struct file *filp, char __user *buffer, len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, "%s%s%s%s%s\n", single_line, single_line, single_line, single_line, single_line); + spin_lock(&me->hlock); hlist_for_each_entry_safe(gmaps, n, &me->maps, hn) { len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, "%-20d|0x%-18llX|0x%-18X|0x%-20lX\n\n", @@ -5357,18 +5358,21 @@ static ssize_t fastrpc_debugfs_read(struct file *filp, char __user *buffer, (uint32_t)gmaps->size, gmaps->va); } + spin_unlock(&me->hlock); len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, "%-20s|%-20s|%-20s|%-20s\n", "len", "refs", "raddr", "flags"); len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, "%s%s%s%s%s\n", single_line, single_line, single_line, single_line, single_line); + spin_lock(&me->hlock); hlist_for_each_entry_safe(gmaps, n, &me->maps, hn) { len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, "0x%-18X|%-20d|%-20lu|%-20u\n", (uint32_t)gmaps->len, gmaps->refs, gmaps->raddr, gmaps->flags); } + spin_unlock(&me->hlock); } else { len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, "\n%s %13s %d\n", "cid", ":", fl->cid); @@ -5410,12 +5414,14 @@ static ssize_t fastrpc_debugfs_read(struct file *filp, char __user *buffer, "%s%s%s%s%s\n", single_line, single_line, single_line, single_line, single_line); + mutex_lock(&fl->map_mutex); hlist_for_each_entry_safe(map, n, &fl->maps, hn) { len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, "0x%-20lX|0x%-20llX|0x%-20zu\n\n", map->va, map->phys, map->size); } + mutex_unlock(&fl->map_mutex); len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, "%-20s|%-20s|%-20s|%-20s\n", "len", "refs", @@ -5424,24 +5430,27 @@ static ssize_t fastrpc_debugfs_read(struct file *filp, char __user *buffer, "%s%s%s%s%s\n", single_line, single_line, single_line, single_line, single_line); + mutex_lock(&fl->map_mutex); hlist_for_each_entry_safe(map, n, &fl->maps, hn) { len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, "%-20zu|%-20d|0x%-20lX|%-20d\n\n", map->len, map->refs, map->raddr, map->uncached); } + mutex_unlock(&fl->map_mutex); len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, "%-20s|%-20s\n", "secure", "attr"); len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, "%s%s%s%s%s\n", single_line, single_line, single_line, single_line, single_line); + mutex_lock(&fl->map_mutex); hlist_for_each_entry_safe(map, n, &fl->maps, hn) { len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, "%-20d|0x%-20lX\n\n", map->secure, map->attr); } - + mutex_unlock(&fl->map_mutex); len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, "\n======%s %s %s======\n", title, " LIST OF BUFS ", title); diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index 57e6a2611567..50564ec5ef3f 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -776,7 +776,7 @@ static int cpu_power_select(struct cpuidle_device *dev, } done_select: - trace_cpu_power_select(best_level, sleep_us, latency_us, 0); + trace_cpu_power_select(best_level, sleep_us, latency_us, cpu->bias); trace_cpu_pred_select(idx_restrict_time ? 2 : (ipi_predicted ? 3 : (predicted ? 1 : 0)), predicted, htime); diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 4507ad717509..08a54d3d0478 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2002,2007-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. */ #include #include @@ -3131,12 +3132,14 @@ int adreno_gmu_fenced_write(struct adreno_device *adreno_dev, unsigned int status, i; const struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); unsigned int reg_offset = gpudev->reg_offsets[offset]; + u64 ts1, ts2; adreno_writereg(adreno_dev, offset, val); if (!gmu_core_isenabled(KGSL_DEVICE(adreno_dev))) return 0; + ts1 = gpudev->read_alwayson(adreno_dev); for (i = 0; i < GMU_CORE_LONG_WAKEUP_RETRY_LIMIT; i++) { /* * Make sure the previous register write is posted before @@ -3165,10 +3168,10 @@ int adreno_gmu_fenced_write(struct adreno_device *adreno_dev, return 0; if (i == GMU_CORE_LONG_WAKEUP_RETRY_LIMIT) { + ts2 = gpudev->read_alwayson(adreno_dev); dev_err(adreno_dev->dev.dev, - "Timed out waiting %d usecs to write fenced register 0x%x\n", - i * GMU_CORE_WAKEUP_DELAY_US, - reg_offset); + "Timed out waiting %d usecs to write fenced register 0x%x, timestamps: %llx %llx\n", + i * GMU_CORE_WAKEUP_DELAY_US, reg_offset, ts1, ts2); return -ETIMEDOUT; } diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index f932ae24d64c..20ce446f7391 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2008-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -2517,6 +2518,15 @@ static int memdesc_sg_virt(struct kgsl_memdesc *memdesc, unsigned long useraddr) ret = sg_alloc_table_from_pages(memdesc->sgt, pages, npages, 0, memdesc->size, GFP_KERNEL); + + if (ret) + goto out; + + ret = kgsl_cache_range_op(memdesc, 0, memdesc->size, + KGSL_CACHE_OP_FLUSH); + + if (ret) + sg_free_table(memdesc->sgt); out: if (ret) { for (i = 0; i < npages; i++) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 337b62f3998c..19114337368a 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -890,7 +891,12 @@ static int haptics_get_status_data(struct haptics_chip *chip, mod_sel_val[0] = sel & 0xff; mod_sel_val[1] = (sel >> 8) & 0xff; rc = haptics_write(chip, chip->cfg_addr_base, - HAP_CFG_MOD_STATUS_SEL_REG, mod_sel_val, 2); + HAP_CFG_MOD_STATUS_XT_V2_REG, &mod_sel_val[1], 1); + if (rc < 0) + return rc; + + rc = haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_MOD_STATUS_SEL_REG, mod_sel_val, 1); if (rc < 0) return rc; @@ -1919,10 +1925,17 @@ static int haptics_toggle_module_enable(struct haptics_chip *chip) { int rc; + /* + * Updating HAPTICS_EN would vote hBoost enable status. Add 100us + * delay before updating HAPTICS_EN for hBoost to have enough time + * to handle its power transition. + */ + usleep_range(100, 101); rc = haptics_module_enable(chip, false); if (rc < 0) return rc; + usleep_range(100, 101); return haptics_module_enable(chip, true); } @@ -2064,22 +2077,11 @@ static int haptics_load_predefined_effect(struct haptics_chip *chip, return -EINVAL; play->effect = effect; - if (play->pattern_src == FIFO) { - /* Toggle HAPTICS_EN for a clear start point of FIFO playing */ - rc = haptics_toggle_module_enable(chip); - if (rc < 0) - return rc; - } - /* Clamp VMAX for different vibration strength */ rc = haptics_set_vmax_mv(chip, play->vmax_mv); if (rc < 0) return rc; - rc = haptics_enable_autores(chip, !play->effect->auto_res_disable); - if (rc < 0) - return rc; - play->pattern_src = play->effect->src; if (play->pattern_src != PATTERN1 && play->pattern_src != PATTERN2 && @@ -2089,6 +2091,17 @@ static int haptics_load_predefined_effect(struct haptics_chip *chip, return -EINVAL; } + if (play->pattern_src == FIFO) { + /* Toggle HAPTICS_EN for a clear start point of FIFO playing */ + rc = haptics_toggle_module_enable(chip); + if (rc < 0) + return rc; + } + + rc = haptics_enable_autores(chip, !play->effect->auto_res_disable); + if (rc < 0) + return rc; + play->brake = play->effect->brake; /* Config brake settings if it's necessary */ if (play->brake) { @@ -2252,12 +2265,12 @@ static int haptics_load_custom_effect(struct haptics_chip *chip, play->effect = chip->custom_effect; play->brake = NULL; play->vmax_mv = (magnitude * chip->custom_effect->vmax_mv) / 0x7fff; - /* Toggle HAPTICS_EN for a clear start point of FIFO playing */ - rc = haptics_toggle_module_enable(chip); + rc = haptics_set_vmax_mv(chip, play->vmax_mv); if (rc < 0) goto cleanup; - rc = haptics_set_vmax_mv(chip, play->vmax_mv); + /* Toggle HAPTICS_EN for a clear start point of FIFO playing */ + rc = haptics_toggle_module_enable(chip); if (rc < 0) goto cleanup; @@ -2374,6 +2387,46 @@ static u8 get_direct_play_max_amplitude(struct haptics_chip *chip) return (u8)amplitude; } +static int haptics_stop_fifo_play(struct haptics_chip *chip) +{ + int rc; + u8 val; + + if (atomic_read(&chip->play.fifo_status.is_busy) == 0) { + dev_dbg(chip->dev, "FIFO playing is not in progress\n"); + return 0; + } + + rc = haptics_enable_play(chip, false); + if (rc < 0) + return rc; + + /* restore FIFO play rate back to T_LRA */ + rc = haptics_set_fifo_playrate(chip, T_LRA); + if (rc < 0) + return rc; + + haptics_fifo_empty_irq_config(chip, false); + kvfree(chip->custom_effect->fifo->samples); + chip->custom_effect->fifo->samples = NULL; + + atomic_set(&chip->play.fifo_status.is_busy, 0); + + /* + * All other playing modes would use AUTO mode RC + * calibration except FIFO streaming mode, so restore + * back to AUTO RC calibration after FIFO playing. + */ + val = CAL_RC_CLK_AUTO_VAL << CAL_RC_CLK_SHIFT; + rc = haptics_masked_write(chip, chip->cfg_addr_base, + HAP_CFG_CAL_EN_REG, CAL_RC_CLK_MASK, val); + if (rc < 0) + return rc; + + dev_dbg(chip->dev, "stopped FIFO playing successfully\n"); + return 0; +} + static int haptics_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old) { @@ -2443,46 +2496,21 @@ static int haptics_upload_effect(struct input_dev *dev, return rc; } - return haptics_wait_hboost_ready(chip); -} - -static int haptics_stop_fifo_play(struct haptics_chip *chip) -{ - int rc; - u8 val; - - if (atomic_read(&chip->play.fifo_status.is_busy) == 0) { - dev_dbg(chip->dev, "FIFO playing is not in progress\n"); - return 0; + rc = haptics_wait_hboost_ready(chip); + if (rc < 0 && chip->play.pattern_src == FIFO) { + /* + * Call haptics_stop_fifo_play(chip) explicitly if hBoost is + * not ready for the FIFO play. This drops current FIFO play + * but it restores the SW back to initial status so that the + * following FIFO play requests can still be served. + */ + dev_dbg(chip->dev, "stop FIFO play explicitly to restore SW status\n"); + mutex_lock(&chip->play.lock); + haptics_stop_fifo_play(chip); + mutex_unlock(&chip->play.lock); + return rc; } - rc = haptics_enable_play(chip, false); - if (rc < 0) - return rc; - - /* restore FIFO play rate back to T_LRA */ - rc = haptics_set_fifo_playrate(chip, T_LRA); - if (rc < 0) - return rc; - - haptics_fifo_empty_irq_config(chip, false); - kvfree(chip->custom_effect->fifo->samples); - chip->custom_effect->fifo->samples = NULL; - - atomic_set(&chip->play.fifo_status.is_busy, 0); - - /* - * All other playing modes would use AUTO mode RC - * calibration except FIFO streaming mode, so restore - * back to AUTO RC calibration after FIFO playing. - */ - val = CAL_RC_CLK_AUTO_VAL << CAL_RC_CLK_SHIFT; - rc = haptics_masked_write(chip, chip->cfg_addr_base, - HAP_CFG_CAL_EN_REG, CAL_RC_CLK_MASK, val); - if (rc < 0) - return rc; - - dev_dbg(chip->dev, "stopped FIFO playing successfully\n"); return 0; } diff --git a/drivers/mtd/ubi/attach.c b/drivers/mtd/ubi/attach.c index 10b2459f8951..d1f036393798 100644 --- a/drivers/mtd/ubi/attach.c +++ b/drivers/mtd/ubi/attach.c @@ -821,9 +821,22 @@ struct ubi_ainf_peb *ubi_early_get_peb(struct ubi_device *ubi, int err = 0; struct ubi_ainf_peb *aeb, *tmp_aeb; - if (!list_empty(&ai->free)) { - aeb = list_entry(ai->free.next, struct ubi_ainf_peb, u.list); + list_for_each_entry_safe(aeb, tmp_aeb, &ai->free, u.list) { + list_del(&aeb->u.list); + if (aeb->ec == UBI_UNKNOWN) { + ubi_err(ubi, "PEB %d in freelist has unknown EC", + aeb->pnum); + aeb->ec = ai->mean_ec; + } + err = early_erase_peb(ubi, ai, aeb->pnum, aeb->ec+1); + if (err) { + ubi_err(ubi, "Erase failed for PEB %d in freelist", + aeb->pnum); + list_add(&aeb->u.list, &ai->erase); + continue; + } + aeb->ec += 1; dbg_bld("return free PEB %d, EC %d", aeb->pnum, aeb->ec); return aeb; } diff --git a/drivers/mtd/ubi/block.c b/drivers/mtd/ubi/block.c index e1a2ae21dfd3..ec476a5d0e37 100644 --- a/drivers/mtd/ubi/block.c +++ b/drivers/mtd/ubi/block.c @@ -121,7 +121,7 @@ static int __init ubiblock_set_param(const char *val, return -EINVAL; } - strcpy(buf, val); + strlcpy(buf, val, sizeof(buf)); /* Get rid of the final newline */ if (buf[len - 1] == '\n') @@ -141,12 +141,12 @@ static int __init ubiblock_set_param(const char *val, ret = kstrtoint(tokens[1], 10, ¶m->vol_id); if (ret < 0) { param->vol_id = -1; - strcpy(param->name, tokens[1]); + strlcpy(param->name, tokens[1], UBIBLOCK_PARAM_LEN+1); } } else { /* One parameter: must be device path */ - strcpy(param->name, tokens[0]); + strlcpy(param->name, tokens[0], UBIBLOCK_PARAM_LEN+1); param->ubi_num = -1; param->vol_id = -1; } @@ -412,7 +412,8 @@ int ubiblock_create(struct ubi_volume_info *vi) goto out_put_disk; } gd->private_data = dev; - sprintf(gd->disk_name, "ubiblock%d_%d", dev->ubi_num, dev->vol_id); + scnprintf(gd->disk_name, DISK_NAME_LEN, "ubiblock%d_%d", + dev->ubi_num, dev->vol_id); set_capacity(gd, disk_capacity); dev->gd = gd; diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index d636bbe214cb..971c3b7a51c3 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -95,7 +95,7 @@ static DEFINE_SPINLOCK(ubi_devices_lock); static ssize_t version_show(struct class *class, struct class_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", UBI_VERSION); + return scnprintf(buf, sizeof(int), "%d\n", UBI_VERSION); } static CLASS_ATTR_RO(version); @@ -114,6 +114,9 @@ struct class ubi_class = { static ssize_t dev_attribute_show(struct device *dev, struct device_attribute *attr, char *buf); +static ssize_t dev_attribute_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); /* UBI device attributes (correspond to files in '//class/ubi/ubiX') */ static struct device_attribute dev_eraseblock_size = @@ -140,6 +143,13 @@ static struct device_attribute dev_mtd_num = __ATTR(mtd_num, S_IRUGO, dev_attribute_show, NULL); static struct device_attribute dev_ro_mode = __ATTR(ro_mode, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_mtd_trigger_scrub = + __ATTR(scrub_all, 0644, + dev_attribute_show, dev_attribute_store); +static struct device_attribute dev_mtd_max_scrub_sqnum = + __ATTR(scrub_max_sqnum, 0444, dev_attribute_show, NULL); +static struct device_attribute dev_mtd_min_scrub_sqnum = + __ATTR(scrub_min_sqnum, 0444, dev_attribute_show, NULL); /** * ubi_volume_notify - send a volume change notification. @@ -332,6 +342,17 @@ int ubi_major2num(int major) return ubi_num; } +static unsigned long long get_max_sqnum(struct ubi_device *ubi) +{ + unsigned long long max_sqnum; + + spin_lock(&ubi->ltree_lock); + max_sqnum = ubi->global_sqnum - 1; + spin_unlock(&ubi->ltree_lock); + + return max_sqnum; +} + /* "Show" method for files in '//class/ubi/ubiX/' */ static ssize_t dev_attribute_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -355,29 +376,50 @@ static ssize_t dev_attribute_show(struct device *dev, return -ENODEV; if (attr == &dev_eraseblock_size) - ret = sprintf(buf, "%d\n", ubi->leb_size); + ret = scnprintf(buf, sizeof(int), "%d\n", + ubi->leb_size); else if (attr == &dev_avail_eraseblocks) - ret = sprintf(buf, "%d\n", ubi->avail_pebs); + ret = scnprintf(buf, sizeof(int), "%d\n", + ubi->avail_pebs); else if (attr == &dev_total_eraseblocks) - ret = sprintf(buf, "%d\n", ubi->good_peb_count); + ret = scnprintf(buf, sizeof(int), "%d\n", + ubi->good_peb_count); else if (attr == &dev_volumes_count) - ret = sprintf(buf, "%d\n", ubi->vol_count - UBI_INT_VOL_COUNT); + ret = scnprintf(buf, sizeof(int), "%d\n", + ubi->vol_count - UBI_INT_VOL_COUNT); else if (attr == &dev_max_ec) - ret = sprintf(buf, "%d\n", ubi->max_ec); + ret = scnprintf(buf, sizeof(int), "%d\n", + ubi->max_ec); else if (attr == &dev_reserved_for_bad) - ret = sprintf(buf, "%d\n", ubi->beb_rsvd_pebs); + ret = scnprintf(buf, sizeof(int), "%d\n", + ubi->beb_rsvd_pebs); else if (attr == &dev_bad_peb_count) - ret = sprintf(buf, "%d\n", ubi->bad_peb_count); + ret = scnprintf(buf, sizeof(int), "%d\n", + ubi->bad_peb_count); else if (attr == &dev_max_vol_count) - ret = sprintf(buf, "%d\n", ubi->vtbl_slots); + ret = scnprintf(buf, sizeof(int), "%d\n", + ubi->vtbl_slots); else if (attr == &dev_min_io_size) - ret = sprintf(buf, "%d\n", ubi->min_io_size); + ret = scnprintf(buf, sizeof(int), "%d\n", + ubi->min_io_size); else if (attr == &dev_bgt_enabled) - ret = sprintf(buf, "%d\n", ubi->thread_enabled); + ret = scnprintf(buf, sizeof(int), "%d\n", + ubi->thread_enabled); else if (attr == &dev_mtd_num) - ret = sprintf(buf, "%d\n", ubi->mtd->index); + ret = scnprintf(buf, sizeof(int), "%d\n", + ubi->mtd->index); else if (attr == &dev_ro_mode) - ret = sprintf(buf, "%d\n", ubi->ro_mode); + ret = scnprintf(buf, sizeof(int), "%d\n", + ubi->ro_mode); + else if (attr == &dev_mtd_trigger_scrub) + ret = scnprintf(buf, sizeof(int), "%d\n", + atomic_read(&ubi->scrub_work_count)); + else if (attr == &dev_mtd_max_scrub_sqnum) + ret = scnprintf(buf, sizeof(unsigned long long), "%llu\n", + get_max_sqnum(ubi)); + else if (attr == &dev_mtd_min_scrub_sqnum) + ret = scnprintf(buf, sizeof(unsigned long long), "%llu\n", + ubi_wl_scrub_get_min_sqnum(ubi)); else ret = -EINVAL; @@ -398,10 +440,46 @@ static struct attribute *ubi_dev_attrs[] = { &dev_bgt_enabled.attr, &dev_mtd_num.attr, &dev_ro_mode.attr, + &dev_mtd_trigger_scrub.attr, + &dev_mtd_max_scrub_sqnum.attr, + &dev_mtd_min_scrub_sqnum.attr, NULL }; ATTRIBUTE_GROUPS(ubi_dev); +static ssize_t dev_attribute_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = 0; + struct ubi_device *ubi; + unsigned long long scrub_sqnum; + + ubi = container_of(dev, struct ubi_device, dev); + ubi = ubi_get_device(ubi->ubi_num); + if (!ubi) + return -ENODEV; + + if (attr == &dev_mtd_trigger_scrub) { + if (kstrtoull(buf, 10, &scrub_sqnum)) { + ret = -EINVAL; + goto out; + } + if (!ubi->lookuptbl) { + pr_err("lookuptbl is null\n"); + ret = -ENOENT; + goto out; + } + ret = ubi_wl_scrub_all(ubi, scrub_sqnum); + if (ret == 0) + ret = count; + } + +out: + ubi_put_device(ubi); + return ret; +} + static void dev_release(struct device *dev) { struct ubi_device *ubi = container_of(dev, struct ubi_device, dev); @@ -438,7 +516,8 @@ static int uif_init(struct ubi_device *ubi) int i, err; dev_t dev; - sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num); + scnprintf(ubi->ubi_name, sizeof(UBI_NAME_STR) + 5, + UBI_NAME_STR "%d", ubi->ubi_num); /* * Major numbers for the UBI character devices are allocated @@ -1376,7 +1455,7 @@ static int ubi_mtd_param_parse(const char *val, const struct kernel_param *kp) return 0; } - strcpy(buf, val); + strlcpy(buf, val, sizeof(buf)); /* Get rid of the final newline */ if (buf[len - 1] == '\n') @@ -1391,7 +1470,7 @@ static int ubi_mtd_param_parse(const char *val, const struct kernel_param *kp) } p = &mtd_dev_param[mtd_devs]; - strcpy(&p->name[0], tokens[0]); + strlcpy(&p->name[0], tokens[0], MTD_PARAM_LEN_MAX); token = tokens[1]; if (token) { diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index b57b84fb97d0..851d26e6f0e1 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -1056,6 +1056,15 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, dbg_io("write VID header to PEB %d", pnum); ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + /* + * Re-erase the PEB before using it. This should minimize any issues + * from decay of charge in this block. + */ + if (ubi->wl_is_inited) { + err = ubi_wl_re_erase_peb(ubi, pnum); + if (err) + return err; + } err = self_check_peb_ec_hdr(ubi, pnum); if (err) return err; @@ -1074,6 +1083,8 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, err = ubi_io_write(ubi, p, pnum, ubi->vid_hdr_aloffset, ubi->vid_hdr_alsize); + if (!err && ubi->wl_is_inited) + ubi_wl_update_peb_sqnum(ubi, pnum, vid_hdr); return err; } diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index a173eb707bdd..911fd3987537 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -170,6 +170,8 @@ struct ubi_vid_io_buf { * @u.list: link in the protection queue * @ec: erase counter * @pnum: physical eraseblock number + * @tagged_scrub_all: if the entry is tagged for scrub all + * @sqnum: The sequence number of the vol header. * * This data structure is used in the WL sub-system. Each physical eraseblock * has a corresponding &struct wl_entry object which may be kept in different @@ -182,6 +184,8 @@ struct ubi_wl_entry { } u; int ec; int pnum; + unsigned int tagged_scrub_all:1; + unsigned long long sqnum; }; /** @@ -625,6 +629,9 @@ struct ubi_device { struct task_struct *bgt_thread; int thread_enabled; char bgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2]; + bool scrub_in_progress; + atomic_t scrub_work_count; + int wl_is_inited; /* I/O sub-system's stuff */ long long flash_size; @@ -919,7 +926,12 @@ int ubi_is_erase_work(struct ubi_work *wrk); void ubi_refill_pools(struct ubi_device *ubi); int ubi_ensure_anchor_pebs(struct ubi_device *ubi); int ubi_bitflip_check(struct ubi_device *ubi, int pnum, int force_scrub); - +ssize_t ubi_wl_scrub_all(struct ubi_device *ubi, + unsigned long long scrub_sqnum); +void ubi_wl_update_peb_sqnum(struct ubi_device *ubi, int pnum, + struct ubi_vid_hdr *vid_hdr); +unsigned long long ubi_wl_scrub_get_min_sqnum(struct ubi_device *ubi); +int ubi_wl_re_erase_peb(struct ubi_device *ubi, int pnum); /* io.c */ int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset, int len); diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 139ee132bfbc..327f369d9fd0 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -54,7 +54,7 @@ static struct device_attribute attr_vol_upd_marker = static ssize_t vol_attribute_show(struct device *dev, struct device_attribute *attr, char *buf) { - int ret; + int ret, size; struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev); struct ubi_device *ubi; @@ -73,27 +73,36 @@ static ssize_t vol_attribute_show(struct device *dev, spin_unlock(&ubi->volumes_lock); if (attr == &attr_vol_reserved_ebs) - ret = sprintf(buf, "%d\n", vol->reserved_pebs); + ret = scnprintf(buf, sizeof(int), "%d\n", vol->reserved_pebs); else if (attr == &attr_vol_type) { const char *tp; - if (vol->vol_type == UBI_DYNAMIC_VOLUME) + if (vol->vol_type == UBI_DYNAMIC_VOLUME) { tp = "dynamic"; - else + size = 8; + } else { tp = "static"; - ret = sprintf(buf, "%s\n", tp); + size = 7; + } + ret = scnprintf(buf, size, "%s\n", tp); } else if (attr == &attr_vol_name) - ret = sprintf(buf, "%s\n", vol->name); + ret = scnprintf(buf, vol->name_len + 1, "%s\n", + vol->name); else if (attr == &attr_vol_corrupted) - ret = sprintf(buf, "%d\n", vol->corrupted); + ret = scnprintf(buf, sizeof(int), "%d\n", + vol->corrupted); else if (attr == &attr_vol_alignment) - ret = sprintf(buf, "%d\n", vol->alignment); + ret = scnprintf(buf, sizeof(int), "%d\n", + vol->alignment); else if (attr == &attr_vol_usable_eb_size) - ret = sprintf(buf, "%d\n", vol->usable_leb_size); + ret = scnprintf(buf, sizeof(int), "%d\n", + vol->usable_leb_size); else if (attr == &attr_vol_data_bytes) - ret = sprintf(buf, "%lld\n", vol->used_bytes); + ret = scnprintf(buf, sizeof(unsigned long long), "%lld\n", + vol->used_bytes); else if (attr == &attr_vol_upd_marker) - ret = sprintf(buf, "%d\n", vol->upd_marker); + ret = scnprintf(buf, sizeof(int), "%d\n", + vol->upd_marker); else /* This must be a bug */ ret = -EINVAL; diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 7def041bbe48..bc0b9ff9512e 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -89,6 +89,7 @@ #include #include #include +#include #include "ubi.h" #include "wl.h" @@ -488,6 +489,42 @@ out_free: return err; } +int ubi_wl_re_erase_peb(struct ubi_device *ubi, int pnum) +{ + int err; + struct ubi_wl_entry *e; + struct ubi_ec_hdr *ec_hdr; + + spin_lock(&ubi->wl_lock); + e = ubi->lookuptbl[pnum]; + spin_unlock(&ubi->wl_lock); + + dbg_wl("Re-erase PEB %d, EC %u", e->pnum, e->ec); + + err = self_check_ec(ubi, e->pnum, e->ec); + if (err) + return -EINVAL; + + ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_NOFS); + if (!ec_hdr) + return -ENOMEM; + + err = ubi_io_sync_erase(ubi, e->pnum, 0); + if (err < 0) + goto out_free; + + dbg_wl("re-erased PEB %d, EC %u", e->pnum, e->ec); + + ec_hdr->ec = cpu_to_be64(e->ec); + + err = ubi_io_write_ec_hdr(ubi, e->pnum, ec_hdr); + if (err) + goto out_free; +out_free: + kfree(ec_hdr); + return err; +} + /** * serve_prot_queue - check if it is time to stop protecting PEBs. * @ubi: UBI device description object @@ -863,9 +900,19 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, } /* The PEB has been successfully moved */ - if (scrubbing) - ubi_msg(ubi, "scrubbed PEB %d (LEB %d:%d), data moved to PEB %d", - e1->pnum, vol_id, lnum, e2->pnum); + if (scrubbing) { + spin_lock(&ubi->wl_lock); + if (e1->tagged_scrub_all) { + WARN_ON(atomic_read(&ubi->scrub_work_count) <= 0); + atomic_dec(&ubi->scrub_work_count); + e1->tagged_scrub_all = 0; + e2->tagged_scrub_all = 0; + } else { + ubi_msg(ubi, "scrubbed PEB %d (LEB %d:%d), data moved to PEB %d", + e1->pnum, vol_id, lnum, e2->pnum); + } + spin_unlock(&ubi->wl_lock); + } ubi_free_vid_buf(vidb); spin_lock(&ubi->wl_lock); @@ -1233,6 +1280,7 @@ int ubi_wl_put_peb(struct ubi_device *ubi, int vol_id, int lnum, retry: spin_lock(&ubi->wl_lock); e = ubi->lookuptbl[pnum]; + e->sqnum = UBI_UNKNOWN; if (e == ubi->move_from) { /* * User is putting the physical eraseblock which was selected to @@ -1269,6 +1317,20 @@ retry: } else if (in_wl_tree(e, &ubi->scrub)) { self_check_in_wl_tree(ubi, e, &ubi->scrub); rb_erase(&e->u.rb, &ubi->scrub); + + /* + * Since this PEB has been put we dont need to worry + * about it anymore + */ + if (e->tagged_scrub_all) { + int wrk_count; + + wrk_count = atomic_read(&ubi->scrub_work_count); + WARN_ON(wrk_count <= 0); + + atomic_dec(&ubi->scrub_work_count); + e->tagged_scrub_all = 0; + } } else if (in_wl_tree(e, &ubi->erroneous)) { self_check_in_wl_tree(ubi, e, &ubi->erroneous); rb_erase(&e->u.rb, &ubi->erroneous); @@ -1300,6 +1362,197 @@ retry: return err; } +/** + * ubi_wl_scrub_get_min_sqnum - Return the minimum sqnum of the used/pq/scrub. + * @ubi: UBI device description object + * + * This function returns the minimum sqnum of the PEB that are currently in use. + * + * Return the min sqnum if there are any used PEB's otherwise return ~(0) + * + */ +unsigned long long ubi_wl_scrub_get_min_sqnum(struct ubi_device *ubi) +{ + int i; + struct ubi_wl_entry *e, *tmp; + struct rb_node *node; + unsigned long long min_sqnum = ~0; + + spin_lock(&ubi->wl_lock); + + /* Go through the pq list */ + for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i) { + list_for_each_entry_safe(e, tmp, &ubi->pq[i], u.list) { + if (e->sqnum < min_sqnum) + min_sqnum = e->sqnum; + } + } + + /* Go through used PEB tree */ + for (node = rb_first(&ubi->used); node; node = rb_next(node)) { + e = rb_entry(node, struct ubi_wl_entry, u.rb); + self_check_in_wl_tree(ubi, e, &ubi->used); + if (e->sqnum < min_sqnum) + min_sqnum = e->sqnum; + } + /* Go through scrub PEB tree */ + for (node = rb_first(&ubi->scrub); node; node = rb_next(node)) { + e = rb_entry(node, struct ubi_wl_entry, u.rb); + self_check_in_wl_tree(ubi, e, &ubi->scrub); + if (e->sqnum < min_sqnum) + min_sqnum = e->sqnum; + } + spin_unlock(&ubi->wl_lock); + return min_sqnum; +} + +/** + * ubi_wl_update_peb_sqnum - Update the vol hdr sqnum of the PEB. + * @pnum: The PEB number. + * @vid_hdr: The vol hdr being written to the PEB. + * + */ +void ubi_wl_update_peb_sqnum(struct ubi_device *ubi, int pnum, + struct ubi_vid_hdr *vid_hdr) +{ + struct ubi_wl_entry *e; + + spin_lock(&ubi->wl_lock); + e = ubi->lookuptbl[pnum]; + e->sqnum = be64_to_cpu(vid_hdr->sqnum); + e->tagged_scrub_all = 0; + spin_unlock(&ubi->wl_lock); +} + +static int is_ubi_readonly(struct ubi_device *ubi) +{ + int is_readonly = 0; + + spin_lock(&ubi->wl_lock); + if (ubi->ro_mode || !ubi->thread_enabled || + ubi_dbg_is_bgt_disabled(ubi)) + is_readonly = 1; + spin_unlock(&ubi->wl_lock); + + return is_readonly; +} + +/** + * ubi_wl_scan_all - Scan all PEB's + * @ubi: UBI device description object + * @scrub_sqnum: The max seqnum of the PEB to scrub from the used/pq lists + * + * This function schedules all device PEBs for scrubbing if the sqnum of the + * vol hdr is less than the sqnum in the trigger. + * + * Return 0 in case of success, (negative) error code otherwise + * + */ +ssize_t ubi_wl_scrub_all(struct ubi_device *ubi, unsigned long long scrub_sqnum) +{ + struct rb_node *node; + struct ubi_wl_entry *e, *tmp; + int scrub_count = 0; + int total_scrub_count = 0; + int err, i; + + if (!ubi->lookuptbl) { + ubi_err(ubi, "lookuptbl is null"); + return -ENOENT; + } + + if (is_ubi_readonly(ubi)) { + ubi_err(ubi, "Cannot *Initiate* scrub:background thread disabled or readonly!"); + return -EROFS; + } + + /* Wait for all currently running work to be done! */ + down_write(&ubi->work_sem); + spin_lock(&ubi->wl_lock); + ubi_msg(ubi, "Scrub triggered sqnum = %llu!", scrub_sqnum); + + if (ubi->scrub_in_progress) { + ubi_err(ubi, "Scrub already in progress, ignoring the trigger"); + spin_unlock(&ubi->wl_lock); + up_write(&ubi->work_sem); /* Allow new work to start. */ + return -EBUSY; + } + ubi->scrub_in_progress = true; + + /* Go through scrub PEB tree and count pending */ + for (node = rb_first(&ubi->scrub); node; node = rb_next(node)) { + e = rb_entry(node, struct ubi_wl_entry, u.rb); + self_check_in_wl_tree(ubi, e, &ubi->scrub); + e->tagged_scrub_all = 1; + total_scrub_count++; + } + + /* Move all used pebs to scrub tree */ + node = rb_first(&ubi->used); + while (node != NULL) { + e = rb_entry(node, struct ubi_wl_entry, u.rb); + self_check_in_wl_tree(ubi, e, &ubi->used); + node = rb_next(node); + + if (e->sqnum > scrub_sqnum) + continue; + rb_erase(&e->u.rb, &ubi->used); + wl_tree_add(e, &ubi->scrub); + e->tagged_scrub_all = 1; + scrub_count++; + total_scrub_count++; + } + + /* Move all protected pebs to scrub tree */ + for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i) { + list_for_each_entry_safe(e, tmp, &ubi->pq[i], u.list) { + + if (e->sqnum > scrub_sqnum) + continue; + + list_del(&e->u.list); + wl_tree_add(e, &ubi->scrub); + e->tagged_scrub_all = 1; + scrub_count++; + total_scrub_count++; + } + } + + atomic_set(&ubi->scrub_work_count, total_scrub_count); + spin_unlock(&ubi->wl_lock); + up_write(&ubi->work_sem); /* Allow new work to start. */ + + /* + * Technically scrubbing is the same as wear-leveling, so it is done + * by the WL worker. + */ + err = ensure_wear_leveling(ubi, 0); + if (err) { + ubi_err(ubi, "Failed to start the WL worker err =%d", err); + return err; + } + ubi_msg(ubi, "Scheduled %d PEB's for scrubbing!", scrub_count); + ubi_msg(ubi, "Total PEB's for scrub = %d", total_scrub_count); + + /* Wait for scrub to finish */ + while (atomic_read(&ubi->scrub_work_count) > 0) { + /* Poll every second to check if the scrub work is done */ + msleep(1000); + + if (is_ubi_readonly(ubi)) { + ubi_err(ubi, "Cannot *Complete* scrub:background thread disabled or readonly!"); + return -EROFS; + } + wake_up_process(ubi->bgt_thread); + } + + spin_lock(&ubi->wl_lock); + ubi->scrub_in_progress = false; + spin_unlock(&ubi->wl_lock); + ubi_msg(ubi, "Done scrubbing %d PEB's!", scrub_count); + return 0; +} + /** * ubi_wl_scrub_peb - schedule a physical eraseblock for scrubbing. * @ubi: UBI device description object @@ -1753,7 +2006,8 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) ubi->max_ec = ai->max_ec; INIT_LIST_HEAD(&ubi->works); - sprintf(ubi->bgt_name, UBI_BGT_NAME_PATTERN, ubi->ubi_num); + scnprintf(ubi->bgt_name, sizeof(UBI_BGT_NAME_PATTERN)+2, + UBI_BGT_NAME_PATTERN, ubi->ubi_num); err = -ENOMEM; ubi->lookuptbl = kcalloc(ubi->peb_count, sizeof(void *), GFP_KERNEL); @@ -1786,6 +2040,8 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) e->pnum = aeb->pnum; e->ec = aeb->ec; + e->tagged_scrub_all = 0; + e->sqnum = aeb->sqnum; ubi_assert(e->ec >= 0); wl_tree_add(e, &ubi->free); @@ -1808,6 +2064,8 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) e->pnum = aeb->pnum; e->ec = aeb->ec; + e->tagged_scrub_all = 0; + e->sqnum = aeb->sqnum; ubi->lookuptbl[e->pnum] = e; if (!aeb->scrub) { @@ -1891,6 +2149,7 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) #ifdef CONFIG_MTD_UBI_FASTMAP ubi_ensure_anchor_pebs(ubi); #endif + ubi->wl_is_inited = 1; return 0; out_free: diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl2_fw.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl2_fw.c index 59991ae6839a..ef8aed24a729 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl2_fw.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl2_fw.c @@ -973,6 +973,15 @@ int atl2_fw_init(struct atl_hw *hw) atl2_get_fw_version(hw); + /* Warn if FW is lower than 1.3.1 */ + if (((mcp->fw_rev >> 24) < 1) || + (((mcp->fw_rev >> 16) & 0xFFU) < 3) || + ((mcp->fw_rev & 0xFFFFU) < 1)) + atl_dev_warn("FW_version: %u.%u.%u, is not stable version", + (mcp->fw_rev >> 24), + ((mcp->fw_rev >> 16) & 0xFFU), + (mcp->fw_rev & 0xFFFFU)); + mcp->ops = &atl2_fw_ops; atl_dev_dbg("Detect ATL2FW %x\n", mcp->fw_rev); diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_common.h b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_common.h index cd0814608d29..292c4295fa2b 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_common.h +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_common.h @@ -20,7 +20,7 @@ #include #include -#define ATL_VERSION "1.1.18" +#define ATL_VERSION "1.1.23" struct atl_nic; @@ -32,8 +32,6 @@ struct atl_nic; #define ATL_MAX_QUEUES 8 -#include "atl_fwd.h" - struct atl_ptp; enum { @@ -414,6 +412,7 @@ void atl_update_global_stats(struct atl_nic *nic); void atl_set_loopback(struct atl_nic *nic, int idx, bool on); void atl_set_intr_mod(struct atl_nic *nic); void atl_update_ntuple_flt(struct atl_nic *nic, int idx); +int atl_vlan_promisc_status(struct net_device *ndev); void atl_set_vlan_promisc(struct atl_hw *hw, int promisc); int atl_hwsem_get(struct atl_hw *hw, int idx); void atl_hwsem_put(struct atl_hw *hw, int idx); @@ -424,18 +423,6 @@ int atl_msm_write(struct atl_hw *hw, uint32_t addr, uint32_t val); int atl_update_eth_stats(struct atl_nic *nic); void atl_adjust_eth_stats(struct atl_ether_stats *stats, struct atl_ether_stats *base, bool add); -void atl_fwd_release_rings(struct atl_nic *nic); -#if IS_ENABLED(CONFIG_ATLFWD_FWD) -enum atl_fwd_notify; -int atl_fwd_suspend_rings(struct atl_nic *nic); -int atl_fwd_resume_rings(struct atl_nic *nic); -void atl_fwd_notify(struct atl_nic *nic, enum atl_fwd_notify notif, void *data); -#else -static inline int atl_fwd_suspend_rings(struct atl_nic *nic) { return 0; } -static inline int atl_fwd_resume_rings(struct atl_nic *nic) { return 0; } -static inline void atl_fwd_notify(struct atl_nic *nic, - enum atl_fwd_notify notif, void *data) {} -#endif int atl_get_lpi_timer(struct atl_nic *nic, uint32_t *lpi_delay); void atl_refresh_rxfs(struct atl_nic *nic); void atl_schedule_work(struct atl_nic *nic); diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump.h b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump.h new file mode 100644 index 000000000000..f4a415dea82c --- /dev/null +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump.h @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Atlantic Network Driver + * + * Copyright (C) 2021 Marvell International Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _ATL_DUMP_H_ +#define _ATL_DUMP_H_ + +enum atl_crash_dump_types { + atl_crash_dump_type_regs = 0, + atl_crash_dump_type_fwiface = 1, + atl_crash_dump_type_act_res = 2, + atl_crash_dump_type_ring = 3, +}; + +/** Some of the registers we can not read safely from the device. + * Those will not be read, with value 0xFFFFFFFF stored instead. + */ +struct atl_crash_dump_regs { + u32 type; + u32 length; + union { + u32 regs_data[0x9000/4]; + struct{ + u32 mif[0x1000/4]; + u32 pci[0x1000/4]; + u32 itr[0x1000/4]; + u32 com[0x1000/4]; + u32 mac_phy[0x1000/4]; + union { + u32 rx[0x2000/4]; + struct{ + u32 res1[0x700/4]; + u32 pb_ctrl; + u32 res2; + u32 pb_status; + u32 res3; + /* 0x5710 */ + struct{ + u32 pb_reg1; + u32 pb_reg2; + u32 pb_reg3; + } pb[8]; + u32 res4[0x100]; /* TODO */ + /* 0x5B00 */ + struct{ + u32 base_lo; + u32 base_hi; + u32 ctrl; + u32 head; + u32 tail; + u32 status; + u32 size; + u32 threshold; + } dma_desc[32]; + } layout; + }; + u32 tx[0x2000/4]; + } layout; + }; +}; + +struct atl_crash_dump_fwiface { + u32 type; + u32 length; + /* struct fw_interface_in */ + u32 fw_interface_in[0x1000/4]; + /* struct fw_interface_out */ + u32 fw_interface_out[0x1000/4]; +}; + +/* Record table size of 128 with each entry holding (tag, mask, action) */ +#define ATL_ACT_RES_TABLE_SIZE 384 +struct atl_crash_dump_act_res { + u32 type; + u32 length; + u32 act_res_data[ATL_ACT_RES_TABLE_SIZE]; +}; + +/* max_size = 'ring_size * (tx + rx) * 16 byte raw descriptor data' */ +#define ATL_MAX_RING_DESC_SIZE ATL_MAX_RING_SIZE * 32 +struct atl_crash_dump_ring { + u32 type; + u32 length; + u32 index; + u32 rx_head; + u32 rx_tail; + u32 tx_head; + u32 tx_tail; + u32 rx_ring_size; + u32 tx_ring_size; + u8 ring_data[ATL_MAX_RING_DESC_SIZE]; +}; + +/* This is an extensible structure to collect various debug data from + * AQC device + */ +struct atl_crash_dump { + u32 length; /* total crash structure length, in bytes */ + u32 sections_count; /* number of sections of type atl_crash_dump* following */ + u8 drv_version[16]; + u8 fw_version[16]; + union { + struct { + struct atl_crash_dump_regs regs; + struct atl_crash_dump_fwiface fwiface; + struct atl_crash_dump_act_res act_res; + struct atl_crash_dump_ring ring[ATL_MAX_QUEUES]; + } antigua; + struct { + struct atl_crash_dump_regs regs; + struct atl_crash_dump_ring ring[ATL_MAX_QUEUES]; + } atlantic; + }; +}; + + +#endif diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump_parser/README b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump_parser/README new file mode 100644 index 000000000000..fbf0aeec4ccf --- /dev/null +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump_parser/README @@ -0,0 +1,21 @@ +The tool atl_dump_parser is used to parse the register/device dump collected +using "ethtool -d" command. Parser accepts dump in the binary format, hence the +dump must be collected using 'raw' option i.e., 'ethtool -d raw on' + +Use gcc to build the binary from sources, + gcc -o atl_dump_parser atl_dump_parser.c + +Example: +-------- +1. Collect ethtool dump from SUT machine + #ethtool -d enp1s0 raw on > ethd_dump + +2. Parse the dump + #ls + atl_dump_parser ethd_dump + #./atl_dump_parser ethd_dump + Parsing is completed successfully + #ls ethd_dump_parsed + action_resolver.txt atl_dump_parser ethd_dump fw_interface.txt registers.txt rings.txt + # + Parsed data will be copied to the text files under _parsed folder. diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump_parser/atl_dump_parser.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump_parser/atl_dump_parser.c new file mode 100644 index 000000000000..ebad73ccec84 --- /dev/null +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump_parser/atl_dump_parser.c @@ -0,0 +1,306 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Atlantic Network Driver + * + * Copyright (C) 2021 Marvell International Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "atl_dump_parser.h" +#include "../atl_dump.h" + +#define DEV_FILE "device.txt" +#define REG_FILE "registers.txt" +#define FW_IFACE_FILE "fw_interface.txt" +#define ACT_RES_FILE "action_resolver.txt" +#define RING_FILE "rings.txt" + +struct section_header { + u32 type; + u32 length; +}; + +char out_folder[64]; + +int dump_device_info(struct atl_crash_dump *crash_dump) +{ + char file_name[64]; + FILE *file; + + snprintf(file_name, 64, "%s/%s", out_folder, DEV_FILE); + file = fopen(file_name, "w"); + if (file == NULL) { + printf("Error opening file %s\n", file_name); + return -1; + } + + fprintf(file, "Driver version: %s\n", crash_dump->drv_version); + fprintf(file, "FW version: %s\n", crash_dump->fw_version); + + fclose(file); + + return 0; +} + +int dump_registers(struct atl_crash_dump_regs *reg) +{ + int block = 0x1000/4, i, offset; + char file_name[64]; + FILE *file; + + snprintf(file_name, 64, "%s/%s", out_folder, REG_FILE); + file = fopen(file_name, "w"); + if (file == NULL) { + printf("Error opening file %s\n", file_name); + return -1; + } + + /* Skip MIF block */ + + offset = block * 4; + fprintf(file, "PCI Registers\n"); + fprintf(file, "=============\n"); + for (i = 0; i < block; i++) + fprintf(file, "0x%08x: 0x%08x\n", offset + i * 4, reg->layout.pci[i]); + + offset += block * 4; + fprintf(file, "\n\nInterrupt Registers\n"); + fprintf(file, "=====================\n"); + for (i = 0; i < block; i++) + fprintf(file, "0x%08x: 0x%08x\n", offset + i * 4, reg->layout.itr[i]); + + offset += block * 4; + fprintf(file, "\n\nCOM Registers\n"); + fprintf(file, "===============\n"); + for (i = 0; i < block; i++) + fprintf(file, "0x%08x: 0x%08x\n", offset + i * 4, reg->layout.com[i]); + + offset += block * 4; + fprintf(file, "\n\nMac Phy Registers\n"); + fprintf(file, "=====================\n"); + for (i = 0; i < block; i++) + fprintf(file, "0x%08x: 0x%08x\n", offset + i * 4, reg->layout.mac_phy[i]); + + offset += block * 4; + fprintf(file, "\n\nRx Registers\n"); + fprintf(file, "===============\n"); + /* Rx block size is 0x2000 */ + for (i = 0; i < block * 2; i++) + fprintf(file, "0x%08x: 0x%08x\n", offset + i * 4, reg->layout.rx[i]); + + offset += block * 2 * 4; + fprintf(file, "\n\nTx Registers\n"); + fprintf(file, "===============\n"); + /* Tx block size is 0x2000 */ + for (i = 0; i < block * 2; i++) + fprintf(file, "0x%08x: 0x%08x\n", offset + i * 4, reg->layout.tx[i]); + + fclose(file); + + return 0; +} + +int dump_fwiface(struct atl_crash_dump_fwiface *fwiface) +{ + int block = 0x1000/4, i; + char file_name[64]; + FILE *file; + + snprintf(file_name, 64, "%s/%s", out_folder, FW_IFACE_FILE); + file = fopen(file_name, "w"); + if (file == NULL) { + printf("Error opening file %s\n", file_name); + return -1; + } + + fprintf(file, "FW input interface\n"); + fprintf(file, "==================\n"); + for (i = 0; i < block; i += 4) + fprintf(file, "0x%08x:\t0x%08x 0x%08x 0x%08x 0x%08x\n", i, + fwiface->fw_interface_in[i], fwiface->fw_interface_in[i + 1], + fwiface->fw_interface_in[i + 2], fwiface->fw_interface_in[i + 3]); + + fprintf(file, "\n\nFW output interface\n"); + fprintf(file, "=======================\n"); + for (i = 0; i < block; i += 4) + fprintf(file, "0x%08x:\t0x%08x 0x%08x 0x%08x 0x%08x\n", i, + fwiface->fw_interface_out[i], fwiface->fw_interface_out[i + 1], + fwiface->fw_interface_out[i + 2], fwiface->fw_interface_out[i + 3]); + + fclose(file); + + return 0; +} + +int dump_act_res(struct atl_crash_dump_act_res *act_res) +{ + char file_name[64]; + int i, idx; + FILE *file; + + snprintf(file_name, 64, "%s/%s", out_folder, ACT_RES_FILE); + file = fopen(file_name, "w"); + if (file == NULL) { + printf("Error opening file %s\n", file_name); + return -1; + } + + fprintf(file, "RECORD RESOLVER TAG TAG Mask Action\n"); + fprintf(file, "==================================================\n"); + for (i = 0, idx = 0; i < ATL_ACT_RES_TABLE_SIZE / 3; i++) { + fprintf(file, "%d\t0x%08x\t0x%08x\t0x%08x\n", i, act_res->act_res_data[idx++], + act_res->act_res_data[idx++], act_res->act_res_data[idx++]); + } + + fclose(file); + + return 0; +} + +int dump_ring(struct atl_crash_dump_ring *ring) +{ + char file_name[64]; + FILE *file; + u32 *data; + int i; + + /* Assumption - file 'rings.txt' shouldn't exists */ + snprintf(file_name, 64, "%s/%s", out_folder, RING_FILE); + file = fopen(file_name, "a+"); + if (file == NULL) { + printf("Error opening file %s\n", file_name); + return -1; + } + + fprintf(file, "Ring Index %d\n", ring->index); + fprintf(file, "==============\n"); + fprintf(file, "Rx head = 0x%08x tail = 0x%08x\n", ring->rx_head, ring->rx_tail); + fprintf(file, "Tx head = 0x%08x tail = 0x%08x\n", ring->tx_head, ring->tx_tail); + fprintf(file, "HW Ring rx_size = %d tx_size = %d\n", ring->rx_ring_size, ring->tx_ring_size); + fprintf(file, "Rx Ring descriptor:\n"); + fprintf(file, "-------------------\n"); + data = (u32 *)ring->ring_data; + for (i = 0; i < ring->rx_ring_size; i += 4) + fprintf(file, "0x%08x:\t0x%08x 0x%08x 0x%08x 0x%08x\n", i, data[i], data[i + 1], + data[i + 2], data[i + 3]); + fprintf(file, "Tx Ring descriptor:\n"); + fprintf(file, "-------------------\n"); + data = (u32 *)ring->ring_data + ring->rx_ring_size; + for (i = 0; i < ring->tx_ring_size; i += 4) + fprintf(file, "0x%08x:\t0x%08x 0x%08x 0x%08x 0x%08x\n", i, data[i], data[i + 1], + data[i + 2], data[i + 3]); + fprintf(file, "\n"); + + fclose(file); + + return 0; + +} + +int main(int argc, char *argv[]) +{ + struct atl_crash_dump *crash_dump; + struct section_header *header; + int i, offset, ret = 0; + time_t t = time(NULL); + struct tm *tm_val; + FILE *input_file; + struct stat st; + char *buffer; + + if (argc < 2) { + printf("No intput file\n"); + return -1; + } + + if (stat(argv[1], &st) == -1) { + printf("File stat error\n"); + return -1; + } + + buffer = malloc(st.st_size); + if (!buffer) { + printf("Mem alloc error\n"); + return -1; + } + /* open the source file for reading */ + input_file = fopen(argv[1],"rb"); + if (input_file == NULL) { + ret = -1; + printf("Error opening file %s\n", argv[1]); + goto err; + } + + fread(buffer, sizeof(char), st.st_size, input_file); + fclose(input_file); + + tm_val = localtime(&t); + tm_val = NULL; + if (tm_val) + snprintf(out_folder, 64, "%s_%02d-%02d-%02d_%02d-%02d-%02d", argv[1], + tm_val->tm_mon + 1, (int) tm_val->tm_mday, 1900 + tm_val->tm_year, + tm_val->tm_hour, tm_val->tm_min, tm_val->tm_sec); + else + snprintf(out_folder, 64, "%s_parsed", argv[1]); + if (stat(out_folder, &st) == -1) { + if (mkdir(out_folder, 0644) == -1) { + ret = -1; + printf("Failed to creat folder` %s\n", out_folder); + goto err; + } + } + + crash_dump = (struct atl_crash_dump *)buffer; + ret = dump_device_info(crash_dump); + if (ret) + goto err; + + offset = offsetof(struct atl_crash_dump, antigua); + for (i = 0; i < crash_dump->sections_count; i++) { + header = (struct section_header *)(buffer + offset); + switch (header->type) { + case atl_crash_dump_type_regs: + ret = dump_registers((struct atl_crash_dump_regs *)(buffer + offset)); + if (ret) + goto err; + break; + case atl_crash_dump_type_fwiface: + ret = dump_fwiface((struct atl_crash_dump_fwiface *)(buffer + offset)); + if (ret) + goto err; + break; + case atl_crash_dump_type_act_res: + ret = dump_act_res((struct atl_crash_dump_act_res *)(buffer + offset)); + if (ret) + goto err; + break; + case atl_crash_dump_type_ring: + ret = dump_ring((struct atl_crash_dump_ring *)(buffer + offset)); + if (ret) + goto err; + break; + default: + printf("Parsing Error - invalid section\n"); + goto err; + } + offset += header->length; + } + +err: + free(buffer); + if (ret) + printf("Failed to parse the dump\n"); + else + printf("Parsing is completed successfully\n"); + + return 0; +} diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump_parser/atl_dump_parser.h b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump_parser/atl_dump_parser.h new file mode 100644 index 000000000000..10ef5279f4cd --- /dev/null +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump_parser/atl_dump_parser.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Atlantic Network Driver + * + * Copyright (C) 2021 Marvell International Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __ATL_DUMP_PARSER +#define __ATL_DUMP_PARSER + +#define ATL_MAX_RING_SIZE (8192 - 8) +#define ATL_MAX_QUEUES 8 + +typedef unsigned int u32; +typedef unsigned char u8; + +#endif diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ethtool.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ethtool.c index a365d2a9cc10..3c0fb9f6df03 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ethtool.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ethtool.c @@ -20,6 +20,7 @@ #include "atl_fwdnl.h" #include "atl_macsec.h" #include "atl_ptp.h" +#include "atl_fwd.h" static uint32_t atl_ethtool_get_link(struct net_device *ndev) { @@ -633,8 +634,8 @@ static const struct atl_stat_desc eth_stat_descs[] = { ATL_ETH_STAT(tx_pause, tx_pause), ATL_ETH_STAT(tx_ether_pkts, tx_ether_pkts), ATL_ETH_STAT(tx_ether_octets, tx_ether_octets), + ATL_ETH_STAT(tx_errors, tx_errors), ATL_ETH_STAT(rx_pause, rx_pause), - ATL_ETH_STAT(rx_ether_drops, rx_ether_drops), ATL_ETH_STAT(rx_ether_octets, rx_ether_octets), ATL_ETH_STAT(rx_ether_pkts, rx_ether_pkts), ATL_ETH_STAT(rx_ether_broacasts, rx_ether_broacasts), @@ -642,6 +643,13 @@ static const struct atl_stat_desc eth_stat_descs[] = { ATL_ETH_STAT(rx_ether_crc_align_errs, rx_ether_crc_align_errs), ATL_ETH_STAT(rx_filter_host, rx_filter_host), ATL_ETH_STAT(rx_filter_lost, rx_filter_lost), + ATL_ETH_STAT(rx_errors, rx_errors), + ATL_ETH_STAT(rx_drops, rx_drops), + ATL_ETH_STAT(rx_dma_packets, rx_dma_packets), + ATL_ETH_STAT(rx_dma_octets, rx_dma_octets), + ATL_ETH_STAT(rx_dma_drops, rx_dma_drops), + ATL_ETH_STAT(tx_dma_packets, tx_dma_packets), + ATL_ETH_STAT(tx_dma_octets, tx_dma_octets), }; #define ATL_PRIV_FLAG(_name, _bit) \ @@ -1173,8 +1181,15 @@ static int atl_set_priv_flags(struct net_device *ndev, uint32_t flags) return 0; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)) +static int atl_get_coalesce(struct net_device *ndev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kec, + struct netlink_ext_ack *extack) +#else static int atl_get_coalesce(struct net_device *ndev, struct ethtool_coalesce *ec) +#endif { struct atl_nic *nic = netdev_priv(ndev); @@ -1185,8 +1200,15 @@ static int atl_get_coalesce(struct net_device *ndev, return 0; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) +static int atl_set_coalesce(struct net_device *ndev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kec, + struct netlink_ext_ack *extack) +#else static int atl_set_coalesce(struct net_device *ndev, struct ethtool_coalesce *ec) +#endif { struct atl_nic *nic = netdev_priv(ndev); @@ -2711,9 +2733,7 @@ static void atl_refresh_rxf_desc(struct atl_nic *nic, atl_for_each_rxf_idx(desc, idx) desc->update_rxf(nic, idx); - atl_set_vlan_promisc(&nic->hw, (nic->ndev->flags & IFF_PROMISC) || - nic->rxf_vlan.promisc_count || - !nic->rxf_vlan.vlans_active); + atl_set_vlan_promisc(&nic->hw, atl_vlan_promisc_status(nic->ndev)); } void atl_refresh_rxfs(struct atl_nic *nic) @@ -2723,9 +2743,7 @@ void atl_refresh_rxfs(struct atl_nic *nic) atl_for_each_rxf_desc(desc) atl_refresh_rxf_desc(nic, desc); - atl_set_vlan_promisc(&nic->hw, (nic->ndev->flags & IFF_PROMISC) || - nic->rxf_vlan.promisc_count || - !nic->rxf_vlan.vlans_active); + atl_set_vlan_promisc(&nic->hw, atl_vlan_promisc_status(nic->ndev)); } static bool atl_vlan_pull_from_promisc(struct atl_nic *nic, uint32_t idx) @@ -2765,9 +2783,8 @@ static bool atl_vlan_pull_from_promisc(struct atl_nic *nic, uint32_t idx) } while (idx & ATL_VIDX_FREE); kfree(map); - atl_set_vlan_promisc(&nic->hw, (nic->ndev->flags & IFF_PROMISC) || - vlan->promisc_count || - !vlan->vlans_active); + atl_set_vlan_promisc(&nic->hw, atl_vlan_promisc_status(nic->ndev)); + return true; } @@ -2955,9 +2972,7 @@ int atl_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid) vlan->promisc_count++; if (pm_runtime_active(&nic->hw.pdev->dev)) atl_set_vlan_promisc(&nic->hw, - (ndev->flags & IFF_PROMISC) || - vlan->promisc_count || - !vlan->vlans_active); + atl_vlan_promisc_status(nic->ndev)); return 0; } @@ -2984,9 +2999,8 @@ update_vlan: atl_rxf_update_vlan(nic, idx); if (pm_runtime_active(&nic->hw.pdev->dev)) atl_set_vlan_promisc(&nic->hw, - (nic->ndev->flags & IFF_PROMISC) || - vlan->promisc_count || - !vlan->vlans_active); + atl_vlan_promisc_status(nic->ndev)); + return 0; } @@ -3027,9 +3041,9 @@ int atl_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vid) update_vlan_promisc: if (pm_runtime_active(&nic->hw.pdev->dev)) - atl_set_vlan_promisc(&nic->hw, (ndev->flags & IFF_PROMISC) || - vlan->promisc_count || - !vlan->vlans_active); + atl_set_vlan_promisc(&nic->hw, + atl_vlan_promisc_status(nic->ndev)); + return 0; } @@ -3086,6 +3100,18 @@ static void atl_ethtool_complete(struct net_device *ndev) pm_runtime_put(&nic->hw.pdev->dev); } +static int atl_ethool_get_regs_len(struct net_device *ndev) +{ + return atl_get_crash_dump(ndev, NULL); +} + +static void atl_ethool_get_regs(struct net_device *ndev, struct ethtool_regs *regs, void *buf) +{ + regs->version = 0; + memset(buf, 0, regs->len); + atl_get_crash_dump(ndev, buf); +} + const struct ethtool_ops atl_ethtool_ops = { #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0) .supported_coalesce_params = ETHTOOL_COALESCE_USECS | @@ -3128,4 +3154,6 @@ const struct ethtool_ops atl_ethtool_ops = { .set_wol = atl_set_wol, .begin = atl_ethtool_begin, .complete = atl_ethtool_complete, + .get_regs_len = atl_ethool_get_regs_len, + .get_regs = atl_ethool_get_regs, }; diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fw.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fw.c index cafd661c4dd8..7c86311a6df3 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fw.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fw.c @@ -1172,6 +1172,39 @@ int atl_update_thermal_flag(struct atl_hw *hw, int bit, bool val) static unsigned int atl_wdog_period = 1100; module_param_named(wdog_period, atl_wdog_period, uint, 0644); +static int atl_fw_rev_check_and_warn(int fw_rev) +{ + int major = (fw_rev >> 24) & 0xFFU; + bool ret = true; + + switch (major) { + case 2: + if ((((fw_rev >> 16) & 0xFFU) < 12) && + ((fw_rev & 0xFFFFU) < 12)) + ret = false; + break; + + case 3: + if ((((fw_rev >> 16) & 0xFFU) < 1) && + ((fw_rev & 0xFFFFU) < 102)) + ret = false; + + break; + + case 4: + if ((((fw_rev >> 16) & 0xFFU) < 2) && + ((fw_rev & 0xFFFFU) < 37)) + ret = false; + + break; + + default: + ret = false; + } + + return ret; +} + int atl_fw_init(struct atl_hw *hw) { uint32_t tries, reg, major; @@ -1186,6 +1219,13 @@ int atl_fw_init(struct atl_hw *hw) atl_dev_dbg("FW startup took %d ms\n", tries); major = (reg >> 24) & 0xff; + + /* Warn if FW is lower than 2.12.12, 3.1.102 and 4.2.37 */ + if (!atl_fw_rev_check_and_warn(reg)) + atl_dev_warn("FW_version: %u.%u.%u, is not stable version", + (reg >> 24), ((reg >> 16) & 0xFFU), + (reg & 0xFFFFU)); + if (!major || major > 3) { atl_dev_err("Unsupported FW major version: %u\n", major); return -EINVAL; diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwd.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwd.c index 2b5e899c6bf8..d46dfa2d9463 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwd.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwd.c @@ -12,6 +12,46 @@ #include #include "atl_common.h" #include "atl_desc.h" +#include "atl_fwd.h" +#include "atl_ring.h" + +static u32 atl_skip_reglist[] = { + /* PCIE */ + 0x1030, 0x1200, 0x1514, 0x1F00, 0x1F04, 0x1F20, 0x1F24, + + /* INTR Registers */ + 0x21A0, 0x21A4, 0x21A8, + + /* COM Registers */ + 0x30A0, 0x30A8, 0x3900, 0x3920, + + /* MAC PHY Registers */ + 0x4058, 0x4060, 0x4064, 0x40B0, 0x40B4, 0x40B8, 0x40BC, + 0x4120, 0x4140, 0x4144, 0x4190, 0x419C, 0x41A0, 0x41A4, + 0x41A8, 0x4270, 0x4288, 0x428C, 0x4290, 0x4294, 0x42E8, + 0x42EC, 0x434C, 0x4378, 0x4380, 0x4384, 0x4440, 0x4444, + 0x4970, 0x4B40, 0x4B44, 0x4B48, 0x4B4C, + + /* RX Registers */ + 0x5108, 0x54CC, 0x55B0, 0x55B4, 0x55C0, 0x55E0, 0x55E4, + 0x5640, 0x5718, 0x5728, 0x5738, 0x5748, 0x5758, 0x5768, + 0x5778, 0x5788, 0x5808, 0x580C, 0x5A10, 0x5B14, 0x5B34, + 0x5B54, 0x5B74, 0x5B94, 0x5BB4, 0x5BD4, 0x5BF4, 0x5C14, + 0x5C34, 0x5C54, 0x5C74, 0x5C94, 0x5CB4, 0x5CD4, 0x5CF4, + 0x5D14, 0x5D34, 0x5D54, 0x5D74, 0x5D94, 0x5DB4, 0x5DD4, + 0x5DF4, 0x5E14, 0x5E34, 0x5E54, 0x5E74, 0x5E94, 0x5EB4, + 0x5ED4, 0x5EF4, 0x6900, 0x6F00, 0x6F20, + + /* TX Registers */ + 0x7808, 0x7848, 0x784C, 0x78B0, 0x78B4, 0x7918, 0x7928, + 0x7938, 0x7948, 0x7958, 0x7968, 0x7978, 0x7988, 0x7A34, + 0x7B10, 0x7B14, 0x7B18, 0x7C14, 0x7C54, 0x7C94, 0x7CD4, + 0x7D14, 0x7D54, 0x7D94, 0x7DD4, 0x7E14, 0x7E54, 0x7E94, + 0x7ED4, 0x7F14, 0x7F54, 0x7F94, 0x7FD4, 0x8014, 0x8054, + 0x8094, 0x80D4, 0x8114, 0x8154, 0x8194, 0x81D4, 0x8214, + 0x8254, 0x8294, 0x82D4, 0x8314, 0x8354, 0x8394, 0x83D4, + 0x8900, 0x8904, 0x8F00, 0x8F20, +}; static const char *atl_fwd_dir_str(struct atl_fwd_ring *ring) { @@ -903,3 +943,178 @@ int atl_fwd_resume_rings(struct atl_nic *nic) err: return ret; } + +int atl_get_ext_stats(struct net_device *ndev, struct atl_ext_stats *stats) +{ + struct atl_nic *nic = netdev_priv(ndev); + + if (!stats) + return -EINVAL; + + atl_update_eth_stats(nic); + atl_update_global_stats(nic); + + memcpy(&stats->rx, &nic->stats.rx, sizeof(stats->rx)); + memcpy(&stats->tx, &nic->stats.tx, sizeof(stats->tx)); + memcpy(&stats->rx_fwd, &nic->stats.rx_fwd, sizeof(stats->rx_fwd)); + memcpy(&stats->eth, &nic->stats.eth, sizeof(stats->eth)); + + return 0; +} +EXPORT_SYMBOL(atl_get_ext_stats); + +static bool atl_skip_register(u32 reg) +{ + int i, count; + + count = sizeof(atl_skip_reglist) / sizeof(u32); + for (i = 0; i < count; i++) + if (atl_skip_reglist[i] == reg) + return true; + + return false; +} + +static int atl_get_crash_dump_regs(struct atl_hw *hw, struct atl_crash_dump_regs *section) +{ + int addr, index; + + section->type = atl_crash_dump_type_regs; + section->length = sizeof(struct atl_crash_dump_regs); + + /* prefill with 'skip' value */ + for (addr = 0; addr < sizeof(section->regs_data); addr++) + section->regs_data[addr] = 0xFFFFFFFF; + + for (addr = 0x1000, index = 0x1000 / 4; addr < 0x9000; addr += 4, index++) { + if (atl_skip_register(addr)) + continue; + section->regs_data[index] = atl_read(hw, addr); + } + + return section->length; +} + +static int atl_get_crash_dump_fwiface(struct atl_hw *hw, struct atl_crash_dump_fwiface *section) +{ + int i; + + section->type = atl_crash_dump_type_fwiface; + section->length = sizeof(struct atl_crash_dump_fwiface); + + for (i = 0; i < ARRAY_SIZE(section->fw_interface_in); i++) + section->fw_interface_in[i] = atl_read(hw, ATL2_MIF_SHARED_BUFFER_IN(i)); + + for (i = 0; i < ARRAY_SIZE(section->fw_interface_out); i++) + section->fw_interface_out[i] = atl_read(hw, ATL2_MIF_SHARED_BUFFER_OUT(i)); + + return section->length; +} + +static int atl_get_crash_dump_act_res(struct atl_hw *hw, struct atl_crash_dump_act_res *section) +{ + int i, idx, err; + + section->type = atl_crash_dump_type_act_res; + section->length = sizeof(struct atl_crash_dump_act_res); + + err = atl_hwsem_get(hw, ATL2_MCP_SEM_ACT_RSLVR); + if (err) { + printk("Failed to aquire act_res semaphore\n"); + goto ret; + } + + for (i = 0, idx = 0; i < ATL_ACT_RES_TABLE_SIZE / 3; i++) { + section->act_res_data[idx++] = atl_read(hw, ATL2_RPF_ACT_RSLVR_REQ_TAG(i)); + section->act_res_data[idx++] = atl_read(hw, ATL2_RPF_ACT_RSLVR_TAG_MASK(i)); + section->act_res_data[idx++] = atl_read(hw, ATL2_RPF_ACT_RSLVR_ACTN(i)); + } + + atl_hwsem_put(hw, ATL2_MCP_SEM_ACT_RSLVR); + +ret: + return section->length; +} + +static int atl_get_crash_dump_ring(struct atl_nic *nic, int idx, + struct atl_crash_dump_ring *section) +{ + int size = sizeof(struct atl_crash_dump_ring), offset; + struct atl_queue_vec *qvec = &nic->qvecs[idx]; + struct atl_hw_ring *hwring; + + if (!test_bit(ATL_ST_UP, &nic->hw.state)) { + memset(section, 0, size); + goto ret; + } + + section->type = atl_crash_dump_type_ring; + section->index = qvec->idx; + section->length = size; + section->rx_head = qvec->rx.head; + section->rx_tail = qvec->rx.tail; + section->tx_head = qvec->tx.head; + section->tx_tail = qvec->tx.tail; + hwring = &qvec->rx.hw; + section->rx_ring_size = hwring->size; + memcpy(section->ring_data, hwring->descs, hwring->size * sizeof(*hwring->descs)); + offset = hwring->size * sizeof(*hwring->descs); + hwring = &qvec->tx.hw; + section->tx_ring_size = hwring->size; + memcpy(section->ring_data + offset, hwring->descs, hwring->size * sizeof(*hwring->descs)); + +ret: + return size; +} + + +int atl_get_crash_dump(struct net_device *ndev, struct atl_crash_dump *crash_dump) +{ + struct atl_nic *nic = netdev_priv(ndev); + u32 fw_rev = nic->hw.mcp.fw_rev; + int recorded_sz = 0, i, nvecs; + u8 *section; + + if (!crash_dump) + return sizeof(struct atl_crash_dump); + + crash_dump->length = sizeof(*crash_dump); + crash_dump->sections_count = 0; + + strlcpy(crash_dump->drv_version, ATL_VERSION, sizeof(crash_dump->drv_version)); + snprintf(crash_dump->fw_version, sizeof(crash_dump->fw_version), + "%d.%d.%d", fw_rev >> 24, fw_rev >> 16 & 0xff, fw_rev & 0xffff); + + if (nic->hw.chip_id == ATL_ANTIGUA) + section = (void *)&crash_dump->antigua.regs; + else + section = (void *)&crash_dump->atlantic.regs; + + recorded_sz = atl_get_crash_dump_regs(&nic->hw, (void *)section); + crash_dump->sections_count++; + crash_dump->length += recorded_sz; + section += recorded_sz; + + if (nic->hw.chip_id == ATL_ANTIGUA) { + recorded_sz = atl_get_crash_dump_fwiface(&nic->hw, (void *)section); + crash_dump->sections_count++; + crash_dump->length += recorded_sz; + section += recorded_sz; + + recorded_sz = atl_get_crash_dump_act_res(&nic->hw, (void *)section); + crash_dump->sections_count++; + crash_dump->length += recorded_sz; + section += recorded_sz; + } + + nvecs = min_t(int, nic->nvecs, ATL_MAX_QUEUES); + for (i = 0; i < nvecs; i++) { + recorded_sz = atl_get_crash_dump_ring(nic, i, (void *)section); + crash_dump->sections_count++; + crash_dump->length += recorded_sz; + section += recorded_sz; + } + + return crash_dump->length; +} +EXPORT_SYMBOL(atl_get_crash_dump); diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwd.h b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwd.h index 57b31320be5a..61b2aca5174c 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwd.h +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwd.h @@ -13,9 +13,10 @@ #define _ATL_FWD_H_ #include "atl_common.h" +#include "atl_dump.h" /* Each incompatible API change bumps the API version */ -#define ATL_FWD_API_VERSION 3 +#define ATL_FWD_API_VERSION 4 struct atl_fwd_event; @@ -405,4 +406,40 @@ enum atl_fwd_ring_state { ATL_FWR_ST_EVT_ENABLED = BIT(1), }; +#if IS_ENABLED(CONFIG_ATLFWD_FWD) +enum atl_fwd_notify; +void atl_fwd_release_rings(struct atl_nic *nic); +int atl_fwd_suspend_rings(struct atl_nic *nic); +int atl_fwd_resume_rings(struct atl_nic *nic); +void atl_fwd_notify(struct atl_nic *nic, enum atl_fwd_notify notif, void *data); +#else +static inline void atl_fwd_release_rings(struct atl_nic *nic) {} +static inline int atl_fwd_suspend_rings(struct atl_nic *nic) { return 0; } +static inline int atl_fwd_resume_rings(struct atl_nic *nic) { return 0; } +static inline void atl_fwd_notify(struct atl_nic *nic, + enum atl_fwd_notify notif, void *data) {} +#endif + +struct atl_ext_stats { + struct atl_rx_ring_stats rx; + struct atl_tx_ring_stats tx; + struct atl_rx_fwd_ring_stats rx_fwd; + + struct atl_ether_stats eth; +}; + +/** + * Direct statistics fetch call for the device + */ +int atl_get_ext_stats(struct net_device *ndev, struct atl_ext_stats *stats); + +/** + * Get the register and other crash related information from the device + * If @crash_dump is NULL, function will return the requested length of the buffer to + * store dump, in bytes + * if @crash_dump is not null, function will fill the buffer with crashdump + * sections filled, up to crash_dump->length + */ +int atl_get_crash_dump(struct net_device *ndev, struct atl_crash_dump *crash_dump); + #endif diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwdnl.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwdnl.c index b9e86c4a300d..a11c42f6eec2 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwdnl.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwdnl.c @@ -21,6 +21,7 @@ #include "atl_fwdnl.h" #include "atl_common.h" +#include "atl_fwd.h" #include "atl_fwdnl_params.h" #include "atl_ring.h" #include "atl_trace.h" @@ -1742,7 +1743,9 @@ static const struct nla_policy atlfwd_nl_policy[NUM_ATL_FWD_ATTR] = { [ATL_FWD_ATTR_QUEUE_INDEX] = { .type = NLA_S32 }, }; -#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0) + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0) && \ + RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(8, 0) #define ATLFWD_NL_OP_POLICY(op_policy) .policy = op_policy #else #define ATLFWD_NL_OP_POLICY(op_policy) diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwdnl_params.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwdnl_params.c index 73b01c15e442..5da85ab5ccf3 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwdnl_params.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwdnl_params.c @@ -13,6 +13,7 @@ #include #include "atl_common.h" +#include "atl_fwd.h" unsigned int atlfwd_nl_tx_clean_threshold_msec = 200; unsigned int atlfwd_nl_tx_clean_threshold_frac = 8; diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_hw.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_hw.c index 8f019ee9efac..e0635888b97b 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_hw.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_hw.c @@ -114,6 +114,16 @@ static void atl_set_promisc(struct atl_hw *hw, bool enabled, bool allmulti) } } +int atl_vlan_promisc_status(struct net_device *ndev) +{ + struct atl_nic *nic = netdev_priv(ndev); + + return ndev->flags & IFF_PROMISC || + nic->rxf_vlan.promisc_count || + !nic->rxf_vlan.vlans_active || + !(ndev->features & NETIF_F_HW_VLAN_CTAG_FILTER); +} + void atl_set_vlan_promisc(struct atl_hw *hw, int promisc) { atl_write_bit(hw, ATL_RX_VLAN_FLT_CTRL1, 1, !!promisc); @@ -746,7 +756,7 @@ void atl_start_hw_global(struct atl_nic *nic) (tpb_size * 32 * 66 / 100) << 16 | (tpb_size * 32 * 50 / 100)); /* 4-TC | Enable TPB */ - atl_set_bits(hw, ATL_TX_PBUF_CTRL1, BIT(8) | BIT(0)); + atl_set_bits(hw, ATL_TX_PBUF_CTRL1, BIT(8) | BIT(2) | BIT(0)); /* TX Buffer clk gate off */ if (hw->chip_id == ATL_ANTIGUA) atl_clear_bits(hw, ATL_TX_PBUF_CTRL1, BIT(5)); @@ -910,9 +920,7 @@ void atl_set_rx_mode(struct net_device *ndev) /* Enable promisc VLAN mode if IFF_PROMISC explicitly * requested or too many VIDs registered */ - atl_set_vlan_promisc(hw, - ndev->flags & IFF_PROMISC || nic->rxf_vlan.promisc_count || - !nic->rxf_vlan.vlans_active); + atl_set_vlan_promisc(hw, atl_vlan_promisc_status(ndev)); atl_set_promisc(hw, promisc_needed, is_multicast_enabled && all_multi_needed); @@ -1319,6 +1327,14 @@ static int __atl_fetch_msm2_stats(struct atl_hw *hw, __READ_MSM_OR_GOTO(ret, hw, ATL_MSM2_CTR_TX_OCTETS_HI, ®2, hwsem_put); stats->tx_ether_octets = ((uint64_t)reg2 << 32) | reg; + __READ_MSM_OR_GOTO(ret, hw, ATL_MSM2_CTR_TX_FCS_ERRS_CNTR_LO, ®, hwsem_put); + __READ_MSM_OR_GOTO(ret, hw, ATL_MSM2_CTR_TX_FCS_ERRS_CNTR_HI, ®2, hwsem_put); + stats->tx_errors = ((uint64_t)reg2 << 32) | reg; + + __READ_MSM_OR_GOTO(ret, hw, ATL_MSM2_CTR_TX_FIFO_ERRS_CNTR_LO, ®, hwsem_put); + __READ_MSM_OR_GOTO(ret, hw, ATL_MSM2_CTR_TX_FIFO_ERRS_CNTR_HI, ®2, hwsem_put); + stats->tx_errors += ((uint64_t)reg2 << 32) | reg; + __READ_MSM_OR_GOTO(ret, hw, ATL_MSM2_CTR_RX_PAUSE_LO, ®, hwsem_put); __READ_MSM_OR_GOTO(ret, hw, ATL_MSM2_CTR_RX_PAUSE_HI, ®2, hwsem_put); stats->rx_pause = ((uint64_t)reg2 << 32) | reg; @@ -1346,6 +1362,13 @@ static int __atl_fetch_msm2_stats(struct atl_hw *hw, __READ_MSM_OR_GOTO(ret, hw, ATL_MSM2_CTR_RX_ALIGN_ERRS_HI, ®2, hwsem_put); stats->rx_ether_crc_align_errs += ((uint64_t)reg2 << 32) | reg; + __READ_MSM_OR_GOTO(ret, hw, ATL_MSM2_CTR_RX_ERRS_CNTR_LO, ®, hwsem_put); + __READ_MSM_OR_GOTO(ret, hw, ATL_MSM2_CTR_RX_ERRS_CNTR_HI, ®2, hwsem_put); + stats->rx_errors = ((uint64_t)reg2 << 32) | reg; + + __READ_MSM_OR_GOTO(ret, hw, ATL_MSM2_CTR_RX_DROP_CNTR_LO, ®, hwsem_put); + __READ_MSM_OR_GOTO(ret, hw, ATL_MSM2_CTR_RX_DROP_CNTR_HI, ®2, hwsem_put); + stats->rx_drops = ((uint64_t)reg2 << 32) | reg; hwsem_put: atl_hwsem_put(hw, ATL_MCP_SEM_MSM); return ret; @@ -1373,7 +1396,19 @@ int atl_update_eth_stats(struct atl_nic *nic) if (ret) goto unlock_fw; - stats.rx_ether_drops = atl_read(hw, ATL_RX_DMA_STATS_CNT7); + stats.rx_dma_drops = atl_read(hw, ATL_RX_DMA_STATS_CNT7); + + stats.rx_dma_packets = atl_read(hw, ATL_RX_DMA_STATS_CNT1); + stats.rx_dma_packets += (uint64_t)atl_read(hw, ATL_RX_DMA_STATS_CNT2) << 32; + + stats.rx_dma_octets = atl_read(hw, ATL_RX_DMA_STATS_CNT3); + stats.rx_dma_octets += (uint64_t)atl_read(hw, ATL_RX_DMA_STATS_CNT4) << 32; + + stats.tx_dma_packets = atl_read(hw, ATL_TX_DMA_STATS_CNT1); + stats.tx_dma_packets += (uint64_t)atl_read(hw, ATL_RX_DMA_STATS_CNT2) << 32; + + stats.tx_dma_octets = atl_read(hw, ATL_TX_DMA_STATS_CNT3); + stats.tx_dma_octets += (uint64_t)atl_read(hw, ATL_TX_DMA_STATS_CNT4) << 32; /* capture debug counters*/ atl_write_bit(hw, ATL_RX_RPF_DBG_CNT_CTRL, 0x1f, 1); @@ -1685,4 +1720,3 @@ static void atl2_hw_new_rx_filter_promisc(struct atl_hw *hw, bool promisc, off_action); } - diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_hw_ptp.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_hw_ptp.c index fd2f0bd549b7..7f43a5ba5956 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_hw_ptp.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_hw_ptp.c @@ -241,6 +241,8 @@ void hw_atl_extract_hwts(struct atl_hw *hw, struct atl_rx_desc_hwts_wb *hwts_wb, sec += tmp; tmp = (u64)((hwts_wb->sec_hw >> 22) & 0x3ff) << 38; sec += tmp; + if (sec == 0xffffffffffff && hwts_wb->ns == 0xffffffff) + return; ns = sec * NSEC_PER_SEC + hwts_wb->ns; if (timestamp) *timestamp = ns + hw->ptp_clk_offset; diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_macsec.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_macsec.c index fea067b78f20..e9b1c4f9330e 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_macsec.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_macsec.c @@ -12,6 +12,7 @@ #include "atl_macsec.h" #if IS_ENABLED(CONFIG_MACSEC) && defined(NETIF_F_HW_MACSEC) #include "atl_common.h" +#include "atl_fwd.h" #include #include "macsec/macsec_api.h" diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_main.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_main.c index c4a2be5768ba..b14b788bbdc0 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_main.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_main.c @@ -17,6 +17,7 @@ #include #include "atl_macsec.h" #include "atl_ptp.h" +#include "atl_fwd.h" const char atl_driver_name[] = "atlantic-fwd"; @@ -384,7 +385,7 @@ int atl_do_reset(struct atl_nic *nic) hw->mcp.ops->dump_cfg(hw); - atl_stop(nic, true); + atl_stop(nic, false); atl_fwd_suspend_rings(nic); @@ -765,9 +766,7 @@ static void atl_remove(struct pci_dev *pdev) atl_ptp_unregister(nic); unregister_netdev(nic->ndev); -#if IS_ENABLED(CONFIG_ATLFWD_FWD) atl_fwd_release_rings(nic); -#endif atl_clear_datapath(nic); @@ -948,6 +947,47 @@ static int atl_pm_runtime_idle(struct device *dev) return -EBUSY; } +#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 13, 0)) +static void atl_reset_prepare(struct pci_dev *pdev) +{ + struct atl_nic *nic = pci_get_drvdata(pdev); + + pm_runtime_get_sync(&nic->hw.pdev->dev); + rtnl_lock(); + + if (netif_running(nic->ndev)) + atl_stop(nic, false); + + rtnl_unlock(); + pm_runtime_put_sync(&nic->hw.pdev->dev); +} + +static void atl_reset_done(struct pci_dev *pdev) +{ + struct atl_nic *nic = pci_get_drvdata(pdev); + + if (!nic) { + dev_err(&pdev->dev, "%s failed, device is unrecoverable\n", + __func__); + return; + } + + pm_runtime_get_sync(&nic->hw.pdev->dev); + rtnl_lock(); + + if (netif_running(nic->ndev)) + atl_start(nic); + + rtnl_unlock(); + pm_runtime_put_sync(&nic->hw.pdev->dev); +} + +static const struct pci_error_handlers atl_err_handlers = { + .reset_prepare = atl_reset_prepare, + .reset_done = atl_reset_done, +}; +#endif + const struct dev_pm_ops atl_pm_ops = { .suspend = atl_suspend_poweroff, .poweroff = atl_suspend_poweroff, @@ -968,6 +1008,11 @@ static struct pci_driver atl_pci_ops = { #ifdef CONFIG_PM .driver.pm = &atl_pm_ops, #endif + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 13, 0)) + .err_handler = &atl_err_handlers, +#endif + }; struct atl_thermal atl_def_thermal; diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ptp.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ptp.c index 622ba980a20f..ee00257e66f9 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ptp.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ptp.c @@ -893,8 +893,13 @@ void atl_ptp_tx_hwtstamp(struct atl_nic *nic, u64 timestamp) return; } - timestamp += atomic_read(&ptp->offset_egress); - atl_ptp_convert_to_hwtstamp(&hwtstamp, timestamp); + if (!timestamp) { + atl_nic_err("ptp timestamp all-F from FW\n"); + memset(&hwtstamp, 0, sizeof(hwtstamp)); + } else { + timestamp += atomic_read(&ptp->offset_egress); + atl_ptp_convert_to_hwtstamp(&hwtstamp, timestamp); + } do { skb_tstamp_tx(skb, &hwtstamp); dev_kfree_skb_any(skb); diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_regs.h b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_regs.h index cbe8fbf5e174..c3969a613256 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_regs.h +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_regs.h @@ -176,8 +176,17 @@ enum mcp_scratchpad { #define ATL_RX_RING_BUF_SIZE(ring) ATL_RING_OFFT(ring, 0x18) #define ATL_RX_RING_THRESH(ring) ATL_RING_OFFT(ring, 0x1c) +#define ATL_RX_DMA_STATS_CNT1 0x6800 +#define ATL_RX_DMA_STATS_CNT2 0x6804 +#define ATL_RX_DMA_STATS_CNT3 0x6808 +#define ATL_RX_DMA_STATS_CNT4 0x680C #define ATL_RX_DMA_STATS_CNT7 0x6818 +#define ATL_TX_DMA_STATS_CNT1 0x8800 +#define ATL_TX_DMA_STATS_CNT2 0x8804 +#define ATL_TX_DMA_STATS_CNT3 0x8808 +#define ATL_TX_DMA_STATS_CNT4 0x880C + /* TX @ 0x7000 */ #define ATL_TX_CTRL1 0x7000 #define ATL_TX_PO_CTRL1 0x7800 @@ -224,6 +233,10 @@ enum mcp_scratchpad { #define ATL_MSM_CTR_RX_ERRS 0x120 +#define ATL_MSM2_CTR_RX_DROP_CNTR_LO 0x158 +#define ATL_MSM2_CTR_RX_DROP_CNTR_HI 0x15C +#define ATL_MSM2_CTR_RX_ERRS_CNTR_LO 0x138 +#define ATL_MSM2_CTR_RX_ERRS_CNTR_HI 0x13C #define ATL_MSM2_CTR_RX_OCTETS_LO 0x100 #define ATL_MSM2_CTR_RX_OCTETS_HI 0x104 #define ATL_MSM2_CTR_RX_ALIGN_ERRS_LO 0x110 @@ -245,5 +258,9 @@ enum mcp_scratchpad { #define ATL_MSM2_CTR_TX_PKTS_LO 0x220 #define ATL_MSM2_CTR_TX_PKTS_HI 0x224 +#define ATL_MSM2_CTR_TX_FCS_ERRS_CNTR_LO 0x228 +#define ATL_MSM2_CTR_TX_FCS_ERRS_CNTR_HI 0x22C +#define ATL_MSM2_CTR_TX_FIFO_ERRS_CNTR_LO 0x238 +#define ATL_MSM2_CTR_TX_FIFO_ERRS_CNTR_HI 0x23C #endif diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ring.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ring.c index b30a04c44ffd..685103c4d544 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ring.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ring.c @@ -244,6 +244,14 @@ static uint32_t atl_insert_context(struct atl_txbuf *txbuf, return tx_cmd; } +static bool atl_ptp_udp(struct sk_buff *skb) +{ + return (ip_hdr(skb)->version == 4) && + (ip_hdr(skb)->protocol == IPPROTO_UDP) && + ((udp_hdr(skb)->dest == htons(319)) || + (udp_hdr(skb)->dest == htons(320))); +} + netdev_tx_t atl_start_xmit(struct sk_buff *skb, struct net_device *ndev) { struct atl_nic *nic = netdev_priv(ndev); @@ -257,12 +265,9 @@ netdev_tx_t atl_start_xmit(struct sk_buff *skb, struct net_device *ndev) * and hardware PTP design of the chip. Otherwise ptp stream * will fail to sync */ - if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) || - unlikely((ip_hdr(skb)->version == 4) && - (ip_hdr(skb)->protocol == IPPROTO_UDP) && - ((udp_hdr(skb)->dest == htons(319)) || - (udp_hdr(skb)->dest == htons(320)))) || - unlikely(eth_hdr(skb)->h_proto == htons(ETH_P_1588))) + if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) || + atl_ptp_udp(skb) || + (eth_hdr(skb)->h_proto == htons(ETH_P_1588)))) return atl_ptp_start_xmit(nic, skb); } @@ -448,7 +453,7 @@ static bool atl_checksum_workaround(struct sk_buff *skb, if (((desc->pkt_type & atl_rx_pkt_type_vlan_msk) == atl_rx_pkt_type_vlan) && - !(desc->rx_estat & atl_rx_estat_vlan_stripped)) + !(desc->rx_estat & atl_rx_estat_vlan_stripped)) ip_header_offset += sizeof(struct vlan_hdr); if ((desc->pkt_type & atl_rx_pkt_type_vlan_msk) == @@ -773,6 +778,9 @@ static int atl_fill_hwts_rx(struct atl_desc_ring *ring, uint32_t count, bool ato count--; } + wmb(); + atl_write(ring_hw(ring), ATL_RX_RING_TAIL(ring), ring->tail); + return 0; } @@ -1208,7 +1216,7 @@ int atl_clean_hwts_rx(struct atl_desc_ring *ring, int budget) uint32_t space = ring_space(ring); struct atl_rx_desc_hwts_wb *wb; struct atl_rxbuf *rxbuf; - u64 ns; + u64 ns = 0; DECLARE_SCRATCH_DESC(scratch); if (space >= refill_batch) @@ -1389,7 +1397,11 @@ static void atl_calc_affinities(struct atl_nic *nic) int i; unsigned int cpu; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) + cpus_read_lock(); +#else get_online_cpus(); +#endif cpu = cpumask_first(cpu_online_mask); for (i = 0; i < nic->nvecs; i++) { @@ -1406,7 +1418,11 @@ static void atl_calc_affinities(struct atl_nic *nic) cpumask_set_cpu(cpu, cpumask); cpu = cpumask_next(cpu, cpu_online_mask); } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) + cpus_read_unlock(); +#else put_online_cpus(); +#endif } void atl_init_qvec(struct atl_nic *nic, struct atl_queue_vec *qvec, int idx) @@ -2104,9 +2120,15 @@ void atl_stop_rings(struct atl_nic *nic) int atl_set_features(struct net_device *ndev, netdev_features_t features) { netdev_features_t changed = ndev->features ^ features; + struct atl_nic *nic = netdev_priv(ndev); ndev->features = features; + if (changed & NETIF_F_HW_VLAN_CTAG_FILTER) { + atl_set_vlan_promisc(&nic->hw, + atl_vlan_promisc_status(ndev)); + } + if (changed & NETIF_F_LRO) atl_set_lro(netdev_priv(ndev)); diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_stats.h b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_stats.h index 070d078ed61f..5adfc9feed2b 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_stats.h +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_stats.h @@ -39,7 +39,7 @@ struct atl_rx_fwd_ring_stats { struct atl_tx_ring_stats { uint64_t packets; uint64_t bytes; - uint64_t tx_busy; + uint64_t tx_busy; /* number of times ring was full and tx failed */ uint64_t tx_restart; uint64_t dma_map_failed; }; @@ -55,8 +55,8 @@ struct atl_ether_stats { uint64_t tx_pause; uint64_t tx_ether_pkts; uint64_t tx_ether_octets; + uint64_t tx_errors; /* from MSM block */ uint64_t rx_pause; - uint64_t rx_ether_drops; uint64_t rx_ether_octets; uint64_t rx_ether_pkts; uint64_t rx_ether_broacasts; @@ -64,6 +64,13 @@ struct atl_ether_stats { uint64_t rx_ether_crc_align_errs; uint64_t rx_filter_host; uint64_t rx_filter_lost; + uint64_t rx_errors; /* from MSM block */ + uint64_t rx_drops; /* from MSM block */ + uint64_t rx_dma_packets; + uint64_t rx_dma_octets; + uint64_t rx_dma_drops; /* DMA level RX drops */ + uint64_t tx_dma_packets; + uint64_t tx_dma_octets; }; struct atl_global_stats { diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/release_notes.txt b/drivers/net/ethernet/aquantia/atlantic-fwd/release_notes.txt index a15f3635f4e4..50c5e78a7362 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/release_notes.txt +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/release_notes.txt @@ -1,3 +1,45 @@ +Version 1.1.23 +Date 02/11/2021 +============== +- Add License headers to atl_dump parser files +- Fixed cpus_read lock/unlock apis for latest kernel +- Fixed the coalescing APIs for the latest kernel + +Version 1.1.22 +Date 11/10/2021 +============== +- Fixed header file and warning complains + +Version 1.1.21 +Date 29/09/2021 +============== +- Atl-fwd: Fix name string for mac_phy stats in ethtool-dump +- Atl-fwd: Minor additions to ethtool dump implementation +- Atl-fwd: Add ethtool-dump parser +- ATLDRV-1776: Fix for link flip during IPA ring reconfig +- Atl-fwd: Update crash-dump structure to fixed size structure + +Version 1.1.20 +Date 23/08/2021 +============== +- Atl-fwd: Correct driver README file and export couple of APIs +- Atl-fwd: Populate Action resolve table and Ring data in the ethtool-dump +- ATLDRV-1766: Fix to update HWTS ring tail value to HW +- Fix error in populating the extended stats +- AQC113 register dump support +- stats+crashdump implementation draft +- Statistics and crash dump API proposal +- factor out fwd stuff to drop headers dependencies + +Version 1.1.19 +Date 12/07/2021 +============== +[ATLDRV-1744] - Add FW version check in Atl-fwd driver +- Move check for PTP UDP packets to separate function, Enable TX pad insert and Handle all FFs in ptp tx TS +[ATLDRV-1582] - Link not Up after FLR reset +[ATLDRV-1653] - Packet with vlan id 1 doesn't get received when rx vlan filter is disabled +[ATLDRV-1659] - Failed to compile with CONFIG_ATLFWD_FWD=y flag on RHEL 8.3 kernel + Version 1.1.18 Date 21/02/2021 ============== diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c index fd99b1dc2700..10c25a806f05 100644 --- a/drivers/net/wireless/cnss2/main.c +++ b/drivers/net/wireless/cnss2/main.c @@ -1421,7 +1421,6 @@ void cnss_schedule_recovery(struct device *dev, { struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); struct cnss_recovery_data *data; - int gfp = GFP_KERNEL; if (!test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state)) cnss_bus_update_status(plat_priv, CNSS_FW_DOWN); @@ -1432,10 +1431,11 @@ void cnss_schedule_recovery(struct device *dev, return; } - if (in_interrupt() || irqs_disabled()) - gfp = GFP_ATOMIC; - - data = kzalloc(sizeof(*data), gfp); + /* Allocating memory always with GFP_ATOMIC flag inside + * cnss_schedule_recovery(). Because there is a chance for + * this api to be invoked in atomic context on some platforms. + */ + data = kzalloc(sizeof(*data), GFP_ATOMIC); if (!data) return; diff --git a/drivers/pinctrl/qcom/pinctrl-blair.c b/drivers/pinctrl/qcom/pinctrl-blair.c index 8d4b6a3f83ff..39ee6e4674c0 100644 --- a/drivers/pinctrl/qcom/pinctrl-blair.c +++ b/drivers/pinctrl/qcom/pinctrl-blair.c @@ -1392,7 +1392,7 @@ static const struct msm_pingroup blair_groups[] = { [26] = PINGROUP(26, qup13_f1, qup13_f2, NA, NA, NA, NA, NA, NA, NA, 0, -1), [27] = PINGROUP(27, qup11_f1, qup11_f2, MDP_VSYNC, PLL_BIST, NA, qdss_gpio14, NA, NA, NA, 0x9C010, 0), - [28] = PINGROUP(28, qup11_f2, qup11_f2, MDP_VSYNC, NA, qdss_gpio15, NA, NA, + [28] = PINGROUP(28, qup11_f1, qup11_f2, MDP_VSYNC, NA, qdss_gpio15, NA, NA, NA, NA, 0x9C010, 1), [29] = PINGROUP(29, cam_mclk, NA, NA, NA, NA, NA, NA, NA, NA, 0, -1), [30] = PINGROUP(30, cam_mclk, NA, NA, NA, NA, NA, NA, NA, NA, 0, -1), diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_phy.c b/drivers/platform/msm/ep_pcie/ep_pcie_phy.c index 7cb3c8832288..6741b0cced46 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_phy.c +++ b/drivers/platform/msm/ep_pcie/ep_pcie_phy.c @@ -31,6 +31,11 @@ void ep_pcie_phy_init(struct ep_pcie_dev_t *dev) "PCIe V%d: PHY V%d: Initializing 7nm QMP phy - 100MHz\n", dev->rev, dev->phy_rev); break; + case 7: + EP_PCIE_DBG(dev, + "PCIe V%d: PHY V%d: Initializing 5nm QMP phy - 100MHz\n", + dev->rev, dev->phy_rev); + break; default: EP_PCIE_ERR(dev, "PCIe V%d: Unexpected phy version %d is caught\n", diff --git a/drivers/platform/msm/ipa_fmwk/ipa_fmwk.c b/drivers/platform/msm/ipa_fmwk/ipa_fmwk.c index 5bcd361a4a9d..418181b6fca8 100644 --- a/drivers/platform/msm/ipa_fmwk/ipa_fmwk.c +++ b/drivers/platform/msm/ipa_fmwk/ipa_fmwk.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -318,6 +319,35 @@ struct ipa_fmwk_contex { bool (*ipa_wdi_is_tx1_used)(void); + int (*ipa_wdi_get_capabilities)(struct ipa_wdi_capabilities_out_params *out); + + int (*ipa_wdi_init_per_inst)(struct ipa_wdi_init_in_params *in, + struct ipa_wdi_init_out_params *out); + + int (*ipa_wdi_cleanup_per_inst)(u32 hdl); + + int (*ipa_wdi_reg_intf_per_inst)( + struct ipa_wdi_reg_intf_in_params *in); + + int (*ipa_wdi_dereg_intf_per_inst)(const char *netdev_name, u32 hdl); + + int (*ipa_wdi_conn_pipes_per_inst)(struct ipa_wdi_conn_in_params *in, + struct ipa_wdi_conn_out_params *out); + + int (*ipa_wdi_disconn_pipes_per_inst)(u32 hdl); + + int (*ipa_wdi_enable_pipes_per_inst)(u32 hdl); + + int (*ipa_wdi_disable_pipes_per_inst)(u32 hdl); + + int (*ipa_wdi_set_perf_profile_per_inst)(u32 hdl, struct ipa_wdi_perf_profile *profile); + + int (*ipa_wdi_create_smmu_mapping_per_inst)(u32 hdl, u32 num_buffers, + struct ipa_wdi_buffer_info *info); + + int (*ipa_wdi_release_smmu_mapping_per_inst)(u32 hdl, u32 num_buffers, + struct ipa_wdi_buffer_info *info); + /* ipa_gsb APIs*/ int (*ipa_bridge_init)(struct ipa_bridge_init_params *params, u32 *hdl); @@ -1248,7 +1278,19 @@ int ipa_fmwk_register_ipa_wdi3(const struct ipa_wdi3_data *in) || ipa_fmwk_ctx->ipa_wdi_get_stats || ipa_fmwk_ctx->ipa_get_wdi_version || ipa_fmwk_ctx->ipa_wdi_sw_stats - || ipa_fmwk_ctx->ipa_wdi_is_tx1_used) { + || ipa_fmwk_ctx->ipa_wdi_is_tx1_used + || ipa_fmwk_ctx->ipa_wdi_get_capabilities + || ipa_fmwk_ctx->ipa_wdi_init_per_inst + || ipa_fmwk_ctx->ipa_wdi_cleanup_per_inst + || ipa_fmwk_ctx->ipa_wdi_reg_intf_per_inst + || ipa_fmwk_ctx->ipa_wdi_dereg_intf_per_inst + || ipa_fmwk_ctx->ipa_wdi_conn_pipes_per_inst + || ipa_fmwk_ctx->ipa_wdi_disconn_pipes_per_inst + || ipa_fmwk_ctx->ipa_wdi_enable_pipes_per_inst + || ipa_fmwk_ctx->ipa_wdi_disable_pipes_per_inst + || ipa_fmwk_ctx->ipa_wdi_set_perf_profile_per_inst + || ipa_fmwk_ctx->ipa_wdi_create_smmu_mapping_per_inst + || ipa_fmwk_ctx->ipa_wdi_release_smmu_mapping_per_inst) { pr_err("ipa_wdi3 APIs were already initialized\n"); return -EPERM; } @@ -1271,7 +1313,26 @@ int ipa_fmwk_register_ipa_wdi3(const struct ipa_wdi3_data *in) ipa_fmwk_ctx->ipa_wdi_sw_stats = in->ipa_wdi_sw_stats; ipa_fmwk_ctx->ipa_get_wdi_version = in->ipa_get_wdi_version; ipa_fmwk_ctx->ipa_wdi_is_tx1_used = in->ipa_wdi_is_tx1_used; - + ipa_fmwk_ctx->ipa_wdi_get_capabilities = in->ipa_wdi_get_capabilities; + ipa_fmwk_ctx->ipa_wdi_init_per_inst = in->ipa_wdi_init_per_inst; + ipa_fmwk_ctx->ipa_wdi_cleanup_per_inst = in->ipa_wdi_cleanup_per_inst; + ipa_fmwk_ctx->ipa_wdi_reg_intf_per_inst = in->ipa_wdi_reg_intf_per_inst; + ipa_fmwk_ctx->ipa_wdi_dereg_intf_per_inst = + in->ipa_wdi_dereg_intf_per_inst; + ipa_fmwk_ctx->ipa_wdi_conn_pipes_per_inst = + in->ipa_wdi_conn_pipes_per_inst; + ipa_fmwk_ctx->ipa_wdi_disconn_pipes_per_inst = + in->ipa_wdi_disconn_pipes_per_inst; + ipa_fmwk_ctx->ipa_wdi_enable_pipes_per_inst = + in->ipa_wdi_enable_pipes_per_inst; + ipa_fmwk_ctx->ipa_wdi_disable_pipes_per_inst = + in->ipa_wdi_disable_pipes_per_inst; + ipa_fmwk_ctx->ipa_wdi_set_perf_profile_per_inst = + in->ipa_wdi_set_perf_profile_per_inst; + ipa_fmwk_ctx->ipa_wdi_create_smmu_mapping_per_inst = + in->ipa_wdi_create_smmu_mapping_per_inst; + ipa_fmwk_ctx->ipa_wdi_release_smmu_mapping_per_inst = + in->ipa_wdi_release_smmu_mapping_per_inst; pr_info("ipa_wdi3 registered successfully\n"); return 0; @@ -1411,6 +1472,139 @@ int ipa_wdi_release_smmu_mapping(u32 num_buffers, } EXPORT_SYMBOL(ipa_wdi_release_smmu_mapping); +int ipa_wdi_get_capabilities(struct ipa_wdi_capabilities_out_params *out) +{ + int ret; + + IPA_FMWK_DISPATCH_RETURN(ipa_wdi_get_capabilities, out); + + return ret; +} +EXPORT_SYMBOL(ipa_wdi_get_capabilities); + +int ipa_wdi_init_per_inst(struct ipa_wdi_init_in_params *in, + struct ipa_wdi_init_out_params *out) +{ + int ret; + + IPA_FMWK_DISPATCH_RETURN(ipa_wdi_init_per_inst, + in, out); + + return ret; +} +EXPORT_SYMBOL(ipa_wdi_init_per_inst); + +int ipa_wdi_cleanup_per_inst(u32 hdl) +{ + int ret; + + IPA_FMWK_DISPATCH_RETURN(ipa_wdi_cleanup_per_inst, hdl); + + return ret; +} +EXPORT_SYMBOL(ipa_wdi_cleanup_per_inst); + +int ipa_wdi_reg_intf_per_inst( + struct ipa_wdi_reg_intf_in_params *in) +{ + int ret; + + IPA_FMWK_DISPATCH_RETURN(ipa_wdi_reg_intf_per_inst, + in); + + return ret; +} +EXPORT_SYMBOL(ipa_wdi_reg_intf_per_inst); + +int ipa_wdi_dereg_intf_per_inst(const char *netdev_name, u32 hdl) +{ + int ret; + + IPA_FMWK_DISPATCH_RETURN(ipa_wdi_dereg_intf_per_inst, + netdev_name, hdl); + + return ret; +} +EXPORT_SYMBOL(ipa_wdi_dereg_intf_per_inst); + +int ipa_wdi_conn_pipes_per_inst(struct ipa_wdi_conn_in_params *in, + struct ipa_wdi_conn_out_params *out) +{ + int ret; + + IPA_FMWK_DISPATCH_RETURN(ipa_wdi_conn_pipes_per_inst, + in, out); + + return ret; +} +EXPORT_SYMBOL(ipa_wdi_conn_pipes_per_inst); + +int ipa_wdi_disconn_pipes_per_inst(u32 hdl) +{ + int ret; + + IPA_FMWK_DISPATCH_RETURN(ipa_wdi_disconn_pipes_per_inst, hdl); + + return ret; +} +EXPORT_SYMBOL(ipa_wdi_disconn_pipes_per_inst); + +int ipa_wdi_enable_pipes_per_inst(u32 hdl) +{ + int ret; + + IPA_FMWK_DISPATCH_RETURN(ipa_wdi_enable_pipes_per_inst, hdl); + + return ret; +} +EXPORT_SYMBOL(ipa_wdi_enable_pipes_per_inst); + +int ipa_wdi_disable_pipes_per_inst(u32 hdl) +{ + int ret; + + IPA_FMWK_DISPATCH_RETURN(ipa_wdi_disable_pipes_per_inst, hdl); + + return ret; +} +EXPORT_SYMBOL(ipa_wdi_disable_pipes_per_inst); + +int ipa_wdi_set_perf_profile_per_inst(u32 hdl, + struct ipa_wdi_perf_profile *profile) +{ + int ret; + + IPA_FMWK_DISPATCH_RETURN(ipa_wdi_set_perf_profile_per_inst, + hdl, profile); + + return ret; +} +EXPORT_SYMBOL(ipa_wdi_set_perf_profile_per_inst); + +int ipa_wdi_create_smmu_mapping_per_inst(u32 hdl, u32 num_buffers, + struct ipa_wdi_buffer_info *info) +{ + int ret; + + IPA_FMWK_DISPATCH_RETURN_DP(ipa_wdi_create_smmu_mapping_per_inst, + hdl, num_buffers, info); + + return ret; +} +EXPORT_SYMBOL(ipa_wdi_create_smmu_mapping_per_inst); + +int ipa_wdi_release_smmu_mapping_per_inst(u32 hdl, u32 num_buffers, + struct ipa_wdi_buffer_info *info) +{ + int ret; + + IPA_FMWK_DISPATCH_RETURN_DP(ipa_wdi_release_smmu_mapping_per_inst, + hdl, num_buffers, info); + + return ret; +} +EXPORT_SYMBOL(ipa_wdi_release_smmu_mapping_per_inst); + int ipa_wdi_get_stats(struct IpaHwStatsWDIInfoData_t *stats) { int ret; diff --git a/drivers/platform/msm/mhi_dev/mhi.c b/drivers/platform/msm/mhi_dev/mhi.c index a6e5f1c52797..67a7fc97a699 100644 --- a/drivers/platform/msm/mhi_dev/mhi.c +++ b/drivers/platform/msm/mhi_dev/mhi.c @@ -372,8 +372,8 @@ static void mhi_dev_event_msi_cb(void *req) ch->pend_flush_cnt = 0; mhi = ch->ring->mhi_dev; - mhi_log(MHI_MSG_VERBOSE, "MSI completed for flush req %d\n", - ereq->flush_num); + mhi_log(MHI_MSG_VERBOSE, "MSI completed for %s flush req %d\n", + ereq->is_stale ? "stale" : "", ereq->flush_num); /* Add back the flushed events space to the event buffer */ ch->evt_buf_wp = ereq->start + ereq->num_events; @@ -383,8 +383,11 @@ static void mhi_dev_event_msi_cb(void *req) spin_lock_irqsave(&mhi->lock, flags); if (ch->curr_ereq == NULL) ch->curr_ereq = ereq; - else + else { + if (ereq->is_stale) + ereq->is_stale = false; list_add_tail(&ereq->list, &ch->event_req_buffers); + } spin_unlock_irqrestore(&mhi->lock, flags); } @@ -3256,6 +3259,7 @@ void mhi_dev_close_channel(struct mhi_dev_client *handle) struct mhi_dev_channel *ch; int count = 0; int rc = 0; + struct event_req *itr, *tmp; if (!handle) { mhi_log(MHI_MSG_ERROR, "Invalid channel access:%d\n", -ENODEV); return; @@ -3291,20 +3295,14 @@ void mhi_dev_close_channel(struct mhi_dev_client *handle) "Trying to close an active channel (%d)\n", ch->ch_id); + if (!list_empty(&ch->flush_event_req_buffers)) { + list_for_each_entry_safe(itr, tmp, &ch->flush_event_req_buffers, list) { + itr->is_stale = true; + } + } + ch->state = MHI_DEV_CH_CLOSED; ch->active_client = NULL; - kfree(ch->ereqs); - mhi_log(MHI_MSG_INFO, - "MEM_DEALLOC:ch:%d size:%d EREQ\n", - ch->ch_id, ch->evt_req_size); - kfree(ch->tr_events); - mhi_log(MHI_MSG_INFO, - "MEM_DEALLOC:ch:%d size:%d TR_EVENTS\n", - ch->ch_id, ch->evt_buf_size); - ch->evt_buf_size = 0; - ch->evt_req_size = 0; - ch->ereqs = NULL; - ch->tr_events = NULL; kfree(handle); mhi_log(MHI_MSG_INFO, "MEM_ALLOC:ch:%d size:%d CLNT_HANDLE\n", @@ -3535,13 +3533,16 @@ int mhi_dev_write_channel(struct mhi_req *wreq) * Expected usage is when there is a write * to the MHI core -> notify SM. */ + mutex_lock(&mhi_ctx->mhi_lock); mhi_log(MHI_MSG_CRITICAL, "Wakeup by chan:%d\n", ch->ch_id); rc = mhi_dev_notify_sm_event(MHI_DEV_EVENT_CORE_WAKEUP); if (rc) { pr_err("error sending core wakeup event\n"); + mutex_unlock(&mhi_ctx->mhi_lock); mutex_unlock(&mhi_ctx->mhi_write_test); return rc; } + mutex_unlock(&mhi_ctx->mhi_lock); } while (atomic_read(&mhi_ctx->is_suspended) && diff --git a/drivers/platform/msm/mhi_dev/mhi.h b/drivers/platform/msm/mhi_dev/mhi.h index c5ce1410328b..a7a34668a04f 100644 --- a/drivers/platform/msm/mhi_dev/mhi.h +++ b/drivers/platform/msm/mhi_dev/mhi.h @@ -452,6 +452,7 @@ struct event_req { struct list_head list; u32 flush_num; bool is_cmd_cpl; + bool is_stale; }; struct mhi_cmd_cmpl_ctx { diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c index bce3fe6bf168..152d0b4263e9 100644 --- a/drivers/platform/msm/usb_bam.c +++ b/drivers/platform/msm/usb_bam.c @@ -334,9 +334,15 @@ static int usb_bam_alloc_buffer(struct usb_bam_pipe_connect *pipe_connect) memset_io(data_buf->base, 0, data_buf->size); data_buf->iova = dma_map_resource(dev, data_buf->phys_base, data_buf->size, DMA_BIDIRECTIONAL, 0); - if (dma_mapping_error(dev, data_buf->iova)) + if (dma_mapping_error(dev, data_buf->iova)) { log_event_err("%s(): oci_mem: err mapping data_buf\n", __func__); + iounmap(data_buf->base); + data_buf->base = NULL; + ret = -ENOMEM; + goto err_exit; + } + log_event_dbg("%s: data_buf:%s virt:%pK, phys:%lx, iova:%lx\n", __func__, dev_name(dev), data_buf->base, (unsigned long)data_buf->phys_base, data_buf->iova); @@ -348,7 +354,11 @@ static int usb_bam_alloc_buffer(struct usb_bam_pipe_connect *pipe_connect) if (!desc_buf->base) { log_event_err("%s: ioremap failed for desc fifo\n", __func__); + dma_unmap_resource(dev, data_buf->iova, + data_buf->size, + DMA_BIDIRECTIONAL, 0); iounmap(data_buf->base); + data_buf->base = NULL; ret = -ENOMEM; goto err_exit; } @@ -356,9 +366,19 @@ static int usb_bam_alloc_buffer(struct usb_bam_pipe_connect *pipe_connect) desc_buf->iova = dma_map_resource(dev, desc_buf->phys_base, desc_buf->size, DMA_BIDIRECTIONAL, 0); - if (dma_mapping_error(dev, desc_buf->iova)) + if (dma_mapping_error(dev, desc_buf->iova)) { log_event_err("%s(): oci_mem: err mapping desc_buf\n", __func__); + dma_unmap_resource(dev, data_buf->iova, + data_buf->size, + DMA_BIDIRECTIONAL, 0); + iounmap(data_buf->base); + data_buf->base = NULL; + iounmap(desc_buf->base); + desc_buf->base = NULL; + ret = -ENOMEM; + goto err_exit; + } log_event_dbg("%s: desc_buf:%s virt:%pK, phys:%lx, iova:%lx\n", __func__, dev_name(dev), desc_buf->base, diff --git a/drivers/power/reset/qcom-dload-mode.c b/drivers/power/reset/qcom-dload-mode.c index 9348723900ef..113db59c2aab 100644 --- a/drivers/power/reset/qcom-dload-mode.c +++ b/drivers/power/reset/qcom-dload-mode.c @@ -39,6 +39,7 @@ static bool enable_dump = IS_ENABLED(CONFIG_POWER_RESET_QCOM_DOWNLOAD_MODE_DEFAULT); static enum qcom_download_mode current_download_mode = QCOM_DOWNLOAD_NODUMP; static enum qcom_download_mode dump_mode = QCOM_DOWNLOAD_FULLDUMP; +static bool early_pcie_init_enable; static int set_download_mode(enum qcom_download_mode mode) { @@ -262,8 +263,10 @@ static int qcom_dload_reboot(struct notifier_block *this, unsigned long event, set_download_mode(QCOM_DOWNLOAD_NODUMP); if (cmd) { - if (!strcmp(cmd, "edl")) - set_download_mode(QCOM_DOWNLOAD_EDL); + if (!strcmp(cmd, "edl")) { + early_pcie_init_enable ? set_download_mode(QCOM_EDLOAD_PCI_MODE) + : set_download_mode(QCOM_DOWNLOAD_EDL); + } else if (!strcmp(cmd, "qcom_dload")) msm_enable_dump_mode(true); } @@ -311,6 +314,33 @@ static void store_kaslr_offset(void) static void store_kaslr_offset(void) {} #endif /* CONFIG_RANDOMIZE_BASE */ +static void check_pci_edl(struct device_node *np) +{ + void __iomem *mem; + uint32_t read_val; + int ret_l, ret_h, l, h, mask_value; + + mem = of_iomap(np, 0); + if (!mem) { + pr_info("Unable to map memory for DT property: %s\n", np->name); + return; + } + + read_val = __raw_readl(mem); + ret_l = of_property_read_u32_index(np, "qcom,boot-config-shift", 0, &l); + ret_h = of_property_read_u32_index(np, "qcom,boot-config-shift", 1, &h); + + if (!ret_l && !ret_h) { + mask_value = (read_val >> l) & GENMASK(h - l, 0); + if (mask_value == 5 || mask_value == 7) { + early_pcie_init_enable = true; + pr_info("Setting up EDL mode to PCIE\n"); + } + } + + iounmap(mem); +} + static int qcom_dload_probe(struct platform_device *pdev) { struct qcom_dload *poweroff; @@ -340,6 +370,7 @@ static int qcom_dload_probe(struct platform_device *pdev) poweroff->dload_dest_addr = map_prop_mem("qcom,msm-imem-dload-type"); store_kaslr_offset(); + check_pci_edl(pdev->dev.of_node); msm_enable_dump_mode(enable_dump); if (!enable_dump) diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 065bf21d95eb..fefa017ff8a6 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -1778,14 +1778,17 @@ static int msm_serial_probe(struct platform_device *pdev) struct uart_port *port; const struct of_device_id *id; int irq, line; + bool flag = false; if (pdev->dev.of_node) line = of_alias_get_id(pdev->dev.of_node, "serial"); else line = pdev->id; - if (line < 0) + if (line < 0) { + flag = true; line = atomic_inc_return(&msm_uart_next_id) - 1; + } if (unlikely(line < 0 || line >= UART_NR)) return -ENXIO; @@ -1803,26 +1806,40 @@ static int msm_serial_probe(struct platform_device *pdev) msm_port->is_uartdm = 0; msm_port->clk = devm_clk_get(&pdev->dev, "core"); - if (IS_ERR(msm_port->clk)) + if (IS_ERR(msm_port->clk)) { + if (flag) + atomic_dec(&msm_uart_next_id); return PTR_ERR(msm_port->clk); + } if (msm_port->is_uartdm) { msm_port->pclk = devm_clk_get(&pdev->dev, "iface"); - if (IS_ERR(msm_port->pclk)) + if (IS_ERR(msm_port->pclk)) { + if (flag) + atomic_dec(&msm_uart_next_id); return PTR_ERR(msm_port->pclk); + } } port->uartclk = clk_get_rate(msm_port->clk); dev_info(&pdev->dev, "uartclk = %d\n", port->uartclk); resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (unlikely(!resource)) + if (unlikely(!resource)) { + if (flag) + atomic_dec(&msm_uart_next_id); return -ENXIO; + } + port->mapbase = resource->start; irq = platform_get_irq(pdev, 0); - if (unlikely(irq < 0)) + if (unlikely(irq < 0)) { + if (flag) + atomic_dec(&msm_uart_next_id); return -ENXIO; + } + port->irq = irq; platform_set_drvdata(pdev, port); diff --git a/drivers/usb/gadget/function/u_qdss.c b/drivers/usb/gadget/function/u_qdss.c index 5556ed2d992c..37cb678e0eef 100644 --- a/drivers/usb/gadget/function/u_qdss.c +++ b/drivers/usb/gadget/function/u_qdss.c @@ -78,7 +78,16 @@ int set_qdss_data_connection(struct f_qdss *qdss, int enable) return -ENOMEM; } - usb_bam_alloc_fifos(usb_bam_type, idx); + ret = usb_bam_alloc_fifos(usb_bam_type, idx); + if (ret) { + pr_err("%s: usb_bam_alloc_fifos failed(%d)\n", + __func__, ret); + dma_unmap_resource(dev->parent, bam_info.qdss_bam_iova, + bam_info.qdss_bam_size, + DMA_BIDIRECTIONAL, 0); + return ret; + } + bam_info.data_fifo = kzalloc(sizeof(struct sps_mem_buffer), GFP_KERNEL); if (!bam_info.data_fifo) { diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index 2b118f71748a..99f271551ecc 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -3623,8 +3623,10 @@ sm_done: spin_unlock_irqrestore(&pd->rx_lock, flags); /* requeue if there are any new/pending RX messages */ - if (!ret && !pd->sm_queued) + if (!ret) { + usbpd_dbg(&pd->dev, "Requeuing new/pending RX messages\n"); kick_sm(pd, 0); + } if (!pd->sm_queued) pm_relax(&pd->dev); diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h index 6009f14c9cfc..9e2ad3b81690 100644 --- a/include/linux/if_bridge.h +++ b/include/linux/if_bridge.h @@ -50,6 +50,11 @@ struct br_ip_list { #define BR_DEFAULT_AGEING_TIME (300 * HZ) +//Handle HYFI_BRIDGING hooks related stuffs, only if HYFI_BRIDGE_HOOKS is defined +#ifdef CONFIG_HYFI_BRIDGE_HOOKS +struct net_bridge_port; +#endif + extern void brioctl_set(int (*ioctl_hook)(struct net *, unsigned int, void __user *)); #ifdef CONFIG_ENABLE_SFE @@ -151,4 +156,11 @@ br_port_flag_is_set(const struct net_device *dev, unsigned long flag) } #endif +#ifdef CONFIG_HYFI_BRIDGE_HOOKS +typedef void (br_notify_hook_t)(int group, int event, const void *ptr); +extern br_notify_hook_t __rcu *br_notify_hook; +typedef int (br_multicast_handle_hook_t)(const struct net_bridge_port *src, + struct sk_buff *skb); +extern br_multicast_handle_hook_t __rcu *br_multicast_handle_hook; +#endif #endif diff --git a/include/linux/ipa_fmwk.h b/include/linux/ipa_fmwk.h index b286446c0430..6c66e4f6781d 100644 --- a/include/linux/ipa_fmwk.h +++ b/include/linux/ipa_fmwk.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2018 - 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef _IPA_FMWK_H_ @@ -207,6 +208,35 @@ struct ipa_wdi3_data { int (*ipa_get_wdi_version)(void); bool (*ipa_wdi_is_tx1_used)(void); + + int (*ipa_wdi_get_capabilities)(struct ipa_wdi_capabilities_out_params *out); + + int (*ipa_wdi_init_per_inst)(struct ipa_wdi_init_in_params *in, + struct ipa_wdi_init_out_params *out); + + int (*ipa_wdi_cleanup_per_inst)(u32 hdl); + + int (*ipa_wdi_reg_intf_per_inst)( + struct ipa_wdi_reg_intf_in_params *in); + + int (*ipa_wdi_dereg_intf_per_inst)(const char *netdev_name, u32 hdl); + + int (*ipa_wdi_conn_pipes_per_inst)(struct ipa_wdi_conn_in_params *in, + struct ipa_wdi_conn_out_params *out); + + int (*ipa_wdi_disconn_pipes_per_inst)(u32 hdl); + + int (*ipa_wdi_enable_pipes_per_inst)(u32 hdl); + + int (*ipa_wdi_disable_pipes_per_inst)(u32 hdl); + + int (*ipa_wdi_set_perf_profile_per_inst)(u32 hdl, struct ipa_wdi_perf_profile *profile); + + int (*ipa_wdi_create_smmu_mapping_per_inst)(u32 hdl, u32 num_buffers, + struct ipa_wdi_buffer_info *info); + + int (*ipa_wdi_release_smmu_mapping_per_inst)(u32 hdl, u32 num_buffers, + struct ipa_wdi_buffer_info *info); }; struct ipa_qdss_data { diff --git a/include/linux/ipa_wdi3.h b/include/linux/ipa_wdi3.h index ca07b74877a2..240ce576ae1b 100644 --- a/include/linux/ipa_wdi3.h +++ b/include/linux/ipa_wdi3.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2018 - 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef _IPA_WDI3_H_ @@ -19,15 +20,19 @@ #define IPA_WDI_MAX_SUPPORTED_SYS_PIPE 3 +typedef u32 ipa_wdi_hdl_t; + enum ipa_wdi_version { IPA_WDI_1, IPA_WDI_2, - IPA_WDI_3 + IPA_WDI_3, + IPA_WDI_VER_MAX }; #define IPA_WDI3_TX_DIR 1 #define IPA_WDI3_TX1_DIR 2 #define IPA_WDI3_RX_DIR 3 +#define IPA_WDI_INST_MAX (2) /** * struct ipa_wdi_init_in_params - wdi init input parameters @@ -43,6 +48,7 @@ struct ipa_wdi_init_in_params { #ifdef IPA_WAN_MSG_IPv6_ADDR_GW_LEN ipa_wdi_meter_notifier_cb wdi_notify; #endif + int inst_id; }; /** @@ -57,6 +63,7 @@ struct ipa_wdi_init_out_params { bool is_uC_ready; bool is_smmu_enabled; bool is_over_gsi; + ipa_wdi_hdl_t hdl; }; /** @@ -93,6 +100,7 @@ struct ipa_wdi_reg_intf_in_params { u32 meta_data; u32 meta_data_mask; u8 is_tx1_used; + ipa_wdi_hdl_t hdl; }; /** @@ -208,6 +216,7 @@ struct ipa_wdi_conn_in_params { struct ipa_wdi_pipe_setup_info tx; struct ipa_wdi_pipe_setup_info_smmu tx_smmu; } u_tx1; + ipa_wdi_hdl_t hdl; }; /** @@ -236,8 +245,30 @@ struct ipa_wdi_perf_profile { u32 max_supported_bw_mbps; }; + +/** + * struct ipa_wdi_capabilities - wdi capability parameters + * + * @num_of_instances: Number of WLAN instances supported. + */ +struct ipa_wdi_capabilities_out_params { + u8 num_of_instances; +}; + #if IS_ENABLED(CONFIG_IPA3) +/** + * ipa_wdi_get_capabilities - Client should call this function to + * know the WDI capabilities + * + * Note: Should not be called from atomic context and only + * after checking IPA readiness using ipa_register_ipa_ready_cb() + * + * @Return 0 on success, negative on failure + */ +int ipa_wdi_get_capabilities( + struct ipa_wdi_capabilities_out_params *out); + /** * ipa_wdi_init - Client should call this function to * init WDI IPA offload data path @@ -256,6 +287,18 @@ int ipa_wdi_init(struct ipa_wdi_init_in_params *in, */ bool ipa_wdi_is_tx1_used(void); +/** + * ipa_wdi_init_per_inst - Client should call this function to + * init WDI IPA offload data path + * + * Note: Should not be called from atomic context and only + * after checking IPA readiness using ipa_register_ipa_ready_cb() + * + * @Return 0 on success, negative on failure + */ +int ipa_wdi_init_per_inst(struct ipa_wdi_init_in_params *in, + struct ipa_wdi_init_out_params *out); + /** * ipa_wdi_cleanup - Client should call this function to * clean up WDI IPA offload data path @@ -264,6 +307,17 @@ bool ipa_wdi_is_tx1_used(void); */ int ipa_wdi_cleanup(void); +/** + * ipa_wdi_cleanup_per_inst - Client should call this function to + * clean up WDI IPA offload data path + * + * @hdl: hdl to wdi client + * + * @Return 0 on success, negative on failure + */ +int ipa_wdi_cleanup_per_inst(ipa_wdi_hdl_t hdl); + + /** * ipa_wdi_reg_intf - Client should call this function to * register interface @@ -275,6 +329,17 @@ int ipa_wdi_cleanup(void); int ipa_wdi_reg_intf( struct ipa_wdi_reg_intf_in_params *in); +/** + * ipa_wdi_reg_intf_per_inst - Client should call this function to + * register interface + * + * Note: Should not be called from atomic context + * + * @Return 0 on success, negative on failure + */ +int ipa_wdi_reg_intf_per_inst( + struct ipa_wdi_reg_intf_in_params *in); + /** * ipa_wdi_dereg_intf - Client Driver should call this * function to deregister before unload and after disconnect @@ -283,6 +348,14 @@ int ipa_wdi_reg_intf( */ int ipa_wdi_dereg_intf(const char *netdev_name); +/** + * ipa_wdi_dereg_intf_per_inst - Client Driver should call this + * function to deregister before unload and after disconnect + * + * @Return 0 on success, negative on failure + */ +int ipa_wdi_dereg_intf_per_inst(const char *netdev_name, ipa_wdi_hdl_t hdl); + /** * ipa_wdi_conn_pipes - Client should call this * function to connect pipes @@ -297,6 +370,20 @@ int ipa_wdi_dereg_intf(const char *netdev_name); int ipa_wdi_conn_pipes(struct ipa_wdi_conn_in_params *in, struct ipa_wdi_conn_out_params *out); +/** + * ipa_wdi_conn_pipes_per_inst - Client should call this + * function to connect pipes + * + * @in: [in] input parameters from client + * @out: [out] output params to client + * + * Note: Should not be called from atomic context + * + * @Return 0 on success, negative on failure + */ +int ipa_wdi_conn_pipes_per_inst(struct ipa_wdi_conn_in_params *in, + struct ipa_wdi_conn_out_params *out); + /** * ipa_wdi_disconn_pipes() - Client should call this * function to disconnect pipes @@ -307,6 +394,17 @@ int ipa_wdi_conn_pipes(struct ipa_wdi_conn_in_params *in, */ int ipa_wdi_disconn_pipes(void); +/** + * ipa_wdi_disconn_pipes_per_inst() - Client should call this + * function to disconnect pipes + * + * @hdl: hdl to wdi client + * Note: Should not be called from atomic context + * + * Returns: 0 on success, negative on failure + */ +int ipa_wdi_disconn_pipes_per_inst(ipa_wdi_hdl_t hdl); + /** * ipa_wdi_enable_pipes() - Client should call this * function to enable IPA offload data path @@ -317,6 +415,17 @@ int ipa_wdi_disconn_pipes(void); */ int ipa_wdi_enable_pipes(void); +/** + * ipa_wdi_enable_pipes_per_inst() - Client should call this + * function to enable IPA offload data path + * + * @hdl: hdl to wdi client + * Note: Should not be called from atomic context + * + * Returns: 0 on success, negative on failure + */ +int ipa_wdi_enable_pipes_per_inst(ipa_wdi_hdl_t hdl); + /** * ipa_wdi_disable_pipes() - Client should call this * function to disable IPA offload data path @@ -327,6 +436,17 @@ int ipa_wdi_enable_pipes(void); */ int ipa_wdi_disable_pipes(void); +/** + * ipa_wdi_disable_pipes_per_inst() - Client should call this + * function to disable IPA offload data path + * + * @hdl: hdl to wdi client + * Note: Should not be called from atomic context + * + * Returns: 0 on success, negative on failure + */ +int ipa_wdi_disable_pipes_per_inst(ipa_wdi_hdl_t hdl); + /** * ipa_wdi_set_perf_profile() - Client should call this function to * set IPA clock bandwidth based on data rates @@ -337,6 +457,18 @@ int ipa_wdi_disable_pipes(void); */ int ipa_wdi_set_perf_profile(struct ipa_wdi_perf_profile *profile); +/** + * ipa_wdi_set_perf_profile_per_inst() - Client should call this function to + * set IPA clock bandwidth based on data rates + * + * @hdl: hdl to wdi client + * @profile: [in] BandWidth profile to use + * + * Returns: 0 on success, negative on failure + */ +int ipa_wdi_set_perf_profile_per_inst(ipa_wdi_hdl_t hdl, + struct ipa_wdi_perf_profile *profile); + /** * ipa_wdi_create_smmu_mapping() - Create smmu mapping * @@ -347,6 +479,17 @@ int ipa_wdi_set_perf_profile(struct ipa_wdi_perf_profile *profile); int ipa_wdi_create_smmu_mapping(u32 num_buffers, struct ipa_wdi_buffer_info *info); +/** + * ipa_wdi_create_smmu_mapping_per_inst() - Create smmu mapping + * + * @hdl: hdl to wdi client + * @num_buffers: number of buffers + * @info: wdi buffer info + */ +int ipa_wdi_create_smmu_mapping_per_inst(ipa_wdi_hdl_t hdl, + u32 num_buffers, + struct ipa_wdi_buffer_info *info); + /** * ipa_wdi_release_smmu_mapping() - Release smmu mapping * @@ -357,6 +500,18 @@ int ipa_wdi_create_smmu_mapping(u32 num_buffers, int ipa_wdi_release_smmu_mapping(u32 num_buffers, struct ipa_wdi_buffer_info *info); +/** + * ipa_wdi_release_smmu_mapping_per_inst() - Release smmu mapping + * + * @hdl: hdl to wdi client + * @num_buffers: number of buffers + * + * @info: wdi buffer info + */ +int ipa_wdi_release_smmu_mapping_per_inst(ipa_wdi_hdl_t hdl, + u32 num_buffers, + struct ipa_wdi_buffer_info *info); + /** * ipa_wdi_get_stats() - Query WDI statistics * @stats: [inout] stats blob from client populated by driver @@ -394,12 +549,34 @@ int ipa_get_wdi_version(void); #else /* IS_ENABLED(CONFIG_IPA3) */ +/** + * ipa_wdi_get_capabilities - Client should call this function to + * know the WDI capabilities + * + * Note: Should not be called from atomic context and only + * after checking IPA readiness using ipa_register_ipa_ready_cb() + * + * @Return 0 on success, negative on failure + */ +int ipa_wdi_get_capabilities( + struct ipa_wdi_capabilities_out_params *out) +{ + return -EPERM; +} + static inline int ipa_wdi_init(struct ipa_wdi_init_in_params *in, struct ipa_wdi_init_out_params *out) { return -EPERM; } +static inline int ipa_wdi_init_per_inst( + struct ipa_wdi_init_in_params *in, + struct ipa_wdi_init_out_params *out) +{ + return -EPERM; +} + static inline int ipa_get_wdi_version(void) { return -EPERM; @@ -415,56 +592,119 @@ static inline int ipa_wdi_cleanup(void) return -EPERM; } +static inline int ipa_wdi_cleanup_per_inst(ipa_wdi_hdl_t hdl) +{ + return -EPERM; +} + static inline int ipa_wdi_reg_intf( struct ipa_wdi_reg_intf_in_params *in) { return -EPERM; } +static inline int ipa_wdi_reg_intf_per_inst( + struct ipa_wdi_reg_intf_in_params *in) +{ + return -EPERM; +} + static inline int ipa_wdi_dereg_intf(const char *netdev_name) { return -EPERM; } +static inline int ipa_wdi_dereg_intf_per_inst(const char *netdev_name, + ipa_wdi_hdl_t hdl) +{ + return -EPERM; +} + static inline int ipa_wdi_conn_pipes(struct ipa_wdi_conn_in_params *in, struct ipa_wdi_conn_out_params *out) { return -EPERM; } +static inline int ipa_wdi_conn_pipes_per_inst( + struct ipa_wdi_conn_in_params *in, + struct ipa_wdi_conn_out_params *out) +{ + return -EPERM; +} + static inline int ipa_wdi_disconn_pipes(void) { return -EPERM; } +static inline int ipa_wdi_disconn_pipes_per_inst(ipa_wdi_hdl_t hdl) +{ + return -EPERM; +} + + static inline int ipa_wdi_enable_pipes(void) { return -EPERM; } +static inline int ipa_wdi_enable_pipes_per_inst(ipa_wdi_hdl_t hdl) +{ + return -EPERM; +} + static inline int ipa_wdi_disable_pipes(void) { return -EPERM; } +static inline int ipa_wdi_disable_pipes_per_inst(ipa_wdi_hdl_t hdl) +{ + return -EPERM; +} + static inline int ipa_wdi_set_perf_profile( struct ipa_wdi_perf_profile *profile) { return -EPERM; } +static inline int ipa_wdi_set_perf_profile_per_inst( + ipa_wdi_hdl_t hdl, + struct ipa_wdi_perf_profile *profile) +{ + return -EPERM; +} + static inline int ipa_wdi_create_smmu_mapping(u32 num_buffers, struct ipa_wdi_buffer_info *info) { return -EPERM; } +static inline int ipa_wdi_create_smmu_mapping_per_inst( + ipa_wdi_hdl_t hdl, + u32 num_buffers, + struct ipa_wdi_buffer_info *info) +{ + return -EPERM; +} + static inline int ipa_wdi_release_smmu_mapping(u32 num_buffers, struct ipa_wdi_buffer_info *info) { return -EPERM; } +static inline int ipa_wdi_release_smmu_mapping_per_inst( + ipa_wdi_hdl_t hdl, + u32 num_buffers, + struct ipa_wdi_buffer_info *info) +{ + return -EPERM; +} + static inline int ipa_wdi_get_stats(struct IpaHwStatsWDIInfoData_t *stats) { return -EPERM; diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h index 6a55176b1b44..cebbaf5afd4a 100644 --- a/include/linux/netdev_features.h +++ b/include/linux/netdev_features.h @@ -83,6 +83,8 @@ enum { NETIF_F_HW_TLS_RECORD_BIT, /* Offload TLS record */ NETIF_F_GRO_FRAGLIST_BIT, /* Fraglist GRO */ + NETIF_F_HW_MACSEC_BIT, /* Offload MACsec operations */ + /* * Add your fresh new feature above and remember to update * netdev_features_strings[] in net/ethtool/common.c and maybe @@ -154,6 +156,7 @@ enum { #define NETIF_F_HW_TLS_RX __NETIF_F(HW_TLS_RX) #define NETIF_F_GRO_FRAGLIST __NETIF_F(GRO_FRAGLIST) #define NETIF_F_GSO_FRAGLIST __NETIF_F(GSO_FRAGLIST) +#define NETIF_F_HW_MACSEC __NETIF_F(HW_MACSEC) /* Finds the next feature with the highest number of the range of start till 0. */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 41af3dde4f79..a6c13ae981cb 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -54,6 +54,8 @@ struct netpoll_info; struct device; struct phy_device; struct dsa_port; +struct macsec_context; +struct macsec_ops; struct sfp_bus; /* 802.11 specific */ @@ -1809,6 +1811,8 @@ enum netdev_priv_flags { * * @wol_enabled: Wake-on-LAN is enabled * + * @macsec_ops: MACsec offloading ops + * * FIXME: cleanup struct net_device such that network protocol info * moves out. */ @@ -2100,6 +2104,11 @@ struct net_device { bool proto_down; unsigned wol_enabled:1; +#if IS_ENABLED(CONFIG_MACSEC) + /* MACsec management functions */ + const struct macsec_ops *macsec_ops; +#endif + ANDROID_KABI_RESERVE(1); ANDROID_KABI_RESERVE(2); ANDROID_KABI_RESERVE(3); diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h index f4d883f158fd..d4ce6f0c7989 100644 --- a/include/linux/qcom_scm.h +++ b/include/linux/qcom_scm.h @@ -17,6 +17,7 @@ enum qcom_download_mode { QCOM_DOWNLOAD_NODUMP = 0x00, QCOM_DOWNLOAD_EDL = 0x01, + QCOM_EDLOAD_PCI_MODE = 0X04, QCOM_DOWNLOAD_FULLDUMP = 0x10, QCOM_DOWNLOAD_MINIDUMP = 0x20, }; diff --git a/include/net/macsec.h b/include/net/macsec.h index 45e274e39f08..52874cdfe226 100644 --- a/include/net/macsec.h +++ b/include/net/macsec.h @@ -231,7 +231,10 @@ struct macsec_secy { * struct macsec_context - MACsec context for hardware offloading */ struct macsec_context { - struct phy_device *phydev; + union { + struct net_device *netdev; + struct phy_device *phydev; + }; enum macsec_offload offload; struct macsec_secy *secy; diff --git a/include/trace/events/trace_msm_low_power.h b/include/trace/events/trace_msm_low_power.h index d3f223a94b38..9b2ade049239 100644 --- a/include/trace/events/trace_msm_low_power.h +++ b/include/trace/events/trace_msm_low_power.h @@ -14,27 +14,27 @@ TRACE_EVENT(cpu_power_select, - TP_PROTO(int index, u32 sleep_us, u32 latency, u32 next_event_us), + TP_PROTO(int index, u32 sleep_us, u32 latency, u64 sched_bias), - TP_ARGS(index, sleep_us, latency, next_event_us), + TP_ARGS(index, sleep_us, latency, sched_bias), TP_STRUCT__entry( __field(int, index) __field(u32, sleep_us) __field(u32, latency) - __field(u32, next_event_us) + __field(u64, sched_bias) ), TP_fast_assign( __entry->index = index; __entry->sleep_us = sleep_us; __entry->latency = latency; - __entry->next_event_us = next_event_us; + __entry->sched_bias = sched_bias; ), - TP_printk("idx:%d sleep_time:%u latency:%u next_event:%u", + TP_printk("idx:%d sleep_time:%u latency:%u sched_bias:%lu", __entry->index, __entry->sleep_us, __entry->latency, - __entry->next_event_us) + __entry->sched_bias) ); TRACE_EVENT(cpu_pred_select, diff --git a/include/uapi/linux/ipa_qmi_service_v01.h b/include/uapi/linux/ipa_qmi_service_v01.h index 63dca0e5b116..55148fd4f494 100644 --- a/include/uapi/linux/ipa_qmi_service_v01.h +++ b/include/uapi/linux/ipa_qmi_service_v01.h @@ -2841,7 +2841,7 @@ struct ipa_move_nat_table_complt_ind_msg_v01 { #define QMI_IPA_MOVE_NAT_COMPLETE_IND_V01 0x0046 /* add for max length*/ -#define QMI_IPA_INIT_MODEM_DRIVER_REQ_MAX_MSG_LEN_V01 210 +#define QMI_IPA_INIT_MODEM_DRIVER_REQ_MAX_MSG_LEN_V01 197 #define QMI_IPA_INIT_MODEM_DRIVER_RESP_MAX_MSG_LEN_V01 25 #define QMI_IPA_INDICATION_REGISTER_REQ_MAX_MSG_LEN_V01 16 #define QMI_IPA_INDICATION_REGISTER_RESP_MAX_MSG_LEN_V01 7 diff --git a/net/bridge/Kconfig b/net/bridge/Kconfig index e4fb050e2078..a9302806d8f6 100644 --- a/net/bridge/Kconfig +++ b/net/bridge/Kconfig @@ -61,3 +61,15 @@ config BRIDGE_VLAN_FILTERING Say N to exclude this support and reduce the binary size. If unsure, say Y. + +config HYFI_BRIDGE_HOOKS + bool "Port Hy-Fi Bridging Hooks" + depends on BRIDGE + depends on QGKI + default y + help + If you say Y here, then the Port Hy-Fi Bridging Hooks will be enabled. + + Say N to exclude this support. + + If unsure, say Y. diff --git a/net/bridge/br.c b/net/bridge/br.c index cccbb9bf3ca4..17e7a0fd9592 100644 --- a/net/bridge/br.c +++ b/net/bridge/br.c @@ -394,6 +394,12 @@ static void __exit br_deinit(void) br_fdb_fini(); } +#ifdef CONFIG_HYFI_BRIDGE_HOOKS +/* Hook for bridge event notifications */ +br_notify_hook_t __rcu *br_notify_hook __read_mostly; +EXPORT_SYMBOL(br_notify_hook); +#endif + module_init(br_init) module_exit(br_deinit) MODULE_LICENSE("GPL"); diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 0dd8984a261d..d30c243c1442 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -81,6 +81,12 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) if (is_broadcast_ether_addr(dest)) { br_flood(br, skb, BR_PKT_BROADCAST, false, true); } else if (is_multicast_ether_addr(dest)) { +#ifdef CONFIG_HYFI_BRIDGE_HOOKS + br_multicast_handle_hook_t *multicast_handle_hook = + rcu_dereference(br_multicast_handle_hook); + if (!__br_get(multicast_handle_hook, true, NULL, skb)) + goto out; +#endif if (unlikely(netpoll_tx_running(dev))) { br_flood(br, skb, BR_PKT_MULTICAST, false, true); goto out; diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index b1d3248c0252..bf80a5deb087 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -147,6 +147,10 @@ struct net_bridge_fdb_entry *br_fdb_find_rcu(struct net_bridge *br, return fdb_find_rcu(&br->fdb_hash_tbl, addr, vid); } +#ifdef CONFIG_HYFI_BRIDGE_HOOKS +EXPORT_SYMBOL(br_fdb_find_rcu); +#endif + /* When a static FDB entry is added, the mac address from the entry is * added to the bridge private HW address list and all required ports * are then updated with the new information. @@ -709,6 +713,9 @@ static void fdb_notify(struct net_bridge *br, kfree_skb(skb); goto errout; } +#ifdef CONFIG_HYFI_BRIDGE_HOOKS + __br_notify(RTNLGRP_NEIGH, type, fdb); +#endif rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC); return; errout: diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index c30d4837d66d..4e7551358682 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -29,7 +29,13 @@ br_netif_receive_skb(struct net *net, struct sock *sk, struct sk_buff *skb) return netif_receive_skb(skb); } -static int br_pass_frame_up(struct sk_buff *skb) +#ifdef CONFIG_HYFI_BRIDGE_HOOKS +/* Hook for external Multicast handler */ +br_multicast_handle_hook_t __rcu *br_multicast_handle_hook __read_mostly; +EXPORT_SYMBOL(br_multicast_handle_hook); +#endif + +int br_pass_frame_up(struct sk_buff *skb) { struct net_device *indev, *brdev = BR_INPUT_SKB_CB(skb)->brdev; struct net_bridge *br = netdev_priv(brdev); @@ -66,6 +72,10 @@ static int br_pass_frame_up(struct sk_buff *skb) br_netif_receive_skb); } +#ifdef CONFIG_HYFI_BRIDGE_HOOKS +EXPORT_SYMBOL(br_pass_frame_up); +#endif + /* note: already called with rcu_read_lock */ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb) { @@ -75,6 +85,9 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb struct net_bridge_mdb_entry *mdst; bool local_rcv, mcast_hit = false; struct net_bridge *br; +#ifdef CONFIG_HYFI_BRIDGE_HOOKS + br_multicast_handle_hook_t *multicast_handle_hook; +#endif u16 vid = 0; if (!p || p->state == BR_STATE_DISABLED) @@ -128,6 +141,11 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb switch (pkt_type) { case BR_PKT_MULTICAST: +#ifdef CONFIG_HYFI_BRIDGE_HOOKS + multicast_handle_hook = rcu_dereference(br_multicast_handle_hook); + if (!__br_get(multicast_handle_hook, true, p, skb)) + goto out; +#endif mdst = br_mdb_get(br, skb, vid); if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) && br_multicast_querier_exists(br, eth_hdr(skb))) { diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 8a664148f57a..6e214587c049 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -499,6 +499,9 @@ void br_ifinfo_notify(int event, const struct net_bridge *br, kfree_skb(skb); goto errout; } +#ifdef CONFIG_HYFI_BRIDGE_HOOKS + __br_notify(RTNLGRP_LINK, event, port); +#endif rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); return; errout: diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index c83d3a954b5f..e10e5def08b9 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -18,6 +18,9 @@ #include #include #include +#ifdef CONFIG_HYFI_BRIDGE_HOOKS +#include +#endif #define BR_HASH_BITS 8 #define BR_HASH_SIZE (1 << BR_HASH_BITS) @@ -625,6 +628,9 @@ void br_manage_promisc(struct net_bridge *br); int nbp_backup_change(struct net_bridge_port *p, struct net_device *backup_dev); /* br_input.c */ +#ifdef CONFIG_HYFI_BRIDGE_HOOKS +int br_pass_frame_up(struct sk_buff *skb); +#endif int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb); rx_handler_result_t br_handle_frame(struct sk_buff **pskb); @@ -1266,4 +1272,17 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br, void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br, u16 vid, struct net_bridge_port *p, struct nd_msg *msg); struct nd_msg *br_is_nd_neigh_msg(struct sk_buff *skb, struct nd_msg *m); +#ifdef CONFIG_HYFI_BRIDGE_HOOKS +#define __br_get(__hook, __default, __args ...) \ + ({ typeof(__hook) HOOK_ = (__hook); \ + (HOOK_ ? (HOOK_(__args)) : (__default)); }) + +static inline void __br_notify(int group, int type, const void *data) +{ + br_notify_hook_t *notify_hook = rcu_dereference(br_notify_hook); + + if (notify_hook) + notify_hook(group, type, data); +} +#endif #endif diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 155ec9c0f724..cb47ad906ba5 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -112,6 +112,7 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] [NETIF_F_HW_TLS_RECORD_BIT] = "tls-hw-record", [NETIF_F_HW_TLS_TX_BIT] = "tls-hw-tx-offload", [NETIF_F_HW_TLS_RX_BIT] = "tls-hw-rx-offload", + [NETIF_F_HW_MACSEC_BIT] = "macsec-hw-offload", }; static const char