commit: e1690e779e5da272197f4dd5faa099b42be0f3d6 Author: Arisu Tachibana <alicef <AT> gentoo <DOT> org> AuthorDate: Fri Feb 20 02:25:21 2026 +0000 Commit: Arisu Tachibana <alicef <AT> gentoo <DOT> org> CommitDate: Fri Feb 20 02:25:21 2026 +0000 URL: https://gitweb.gentoo.org/proj/linux-patches.git/commit/?id=e1690e77
Linux patch 6.18.13 Signed-off-by: Arisu Tachibana <alicef <AT> gentoo.org> 0000_README | 4 + 1012_linux-6.18.13.patch | 3008 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 3012 insertions(+) diff --git a/0000_README b/0000_README index 02cc257a..2f876c29 100644 --- a/0000_README +++ b/0000_README @@ -91,6 +91,10 @@ Patch: 1011_linux-6.18.12.patch From: https://www.kernel.org Desc: Linux 6.18.12 +Patch: 1012_linux-6.18.13.patch +From: https://www.kernel.org +Desc: Linux 6.18.13 + Patch: 1510_fs-enable-link-security-restrictions-by-default.patch From: http://sources.debian.net/src/linux/3.16.7-ckt4-3/debian/patches/debian/fs-enable-link-security-restrictions-by-default.patch/ Desc: Enable link security restrictions by default. diff --git a/1012_linux-6.18.13.patch b/1012_linux-6.18.13.patch new file mode 100644 index 00000000..89a08f13 --- /dev/null +++ b/1012_linux-6.18.13.patch @@ -0,0 +1,3008 @@ +diff --git a/Makefile b/Makefile +index 09153bd3bc5d5e..c4b22ec262784b 100644 +--- a/Makefile ++++ b/Makefile +@@ -1,7 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0 + VERSION = 6 + PATCHLEVEL = 18 +-SUBLEVEL = 12 ++SUBLEVEL = 13 + EXTRAVERSION = + NAME = Baby Opossum Posse + +diff --git a/arch/arm64/boot/dts/mediatek/mt8183.dtsi b/arch/arm64/boot/dts/mediatek/mt8183.dtsi +index 960d8955d018c1..27ce4b31cc99de 100644 +--- a/arch/arm64/boot/dts/mediatek/mt8183.dtsi ++++ b/arch/arm64/boot/dts/mediatek/mt8183.dtsi +@@ -1812,15 +1812,23 @@ + #size-cells = <0>; + + port@0 { ++ #address-cells = <1>; ++ #size-cells = <0>; + reg = <0>; +- ovl_2l1_in: endpoint { ++ ++ ovl_2l1_in: endpoint@1 { ++ reg = <1>; + remote-endpoint = <&mmsys_ep_ext>; + }; + }; + + port@1 { ++ #address-cells = <1>; ++ #size-cells = <0>; + reg = <1>; +- ovl_2l1_out: endpoint { ++ ++ ovl_2l1_out: endpoint@1 { ++ reg = <1>; + remote-endpoint = <&rdma1_in>; + }; + }; +@@ -1872,15 +1880,23 @@ + #size-cells = <0>; + + port@0 { ++ #address-cells = <1>; ++ #size-cells = <0>; + reg = <0>; +- rdma1_in: endpoint { ++ ++ rdma1_in: endpoint@1 { ++ reg = <1>; + remote-endpoint = <&ovl_2l1_out>; + }; + }; + + port@1 { ++ #address-cells = <1>; ++ #size-cells = <0>; + reg = <1>; +- rdma1_out: endpoint { ++ ++ rdma1_out: endpoint@1 { ++ reg = <1>; + remote-endpoint = <&dpi_in>; + }; + }; +@@ -2076,15 +2092,24 @@ + #size-cells = <0>; + + port@0 { ++ #address-cells = <1>; ++ #size-cells = <0>; + reg = <0>; +- dpi_in: endpoint { ++ ++ dpi_in: endpoint@1 { ++ reg = <1>; + remote-endpoint = <&rdma1_out>; + }; + }; + + port@1 { ++ #address-cells = <1>; ++ #size-cells = <0>; + reg = <1>; +- dpi_out: endpoint { }; ++ ++ dpi_out: endpoint@1 { ++ reg = <1>; ++ }; + }; + }; + }; +diff --git a/arch/loongarch/mm/kasan_init.c b/arch/loongarch/mm/kasan_init.c +index 170da98ad4f551..0fc02ca0645738 100644 +--- a/arch/loongarch/mm/kasan_init.c ++++ b/arch/loongarch/mm/kasan_init.c +@@ -40,39 +40,43 @@ static pgd_t kasan_pg_dir[PTRS_PER_PGD] __initdata __aligned(PAGE_SIZE); + #define __pte_none(early, pte) (early ? pte_none(pte) : \ + ((pte_val(pte) & _PFN_MASK) == (unsigned long)__pa(kasan_early_shadow_page))) + +-void *kasan_mem_to_shadow(const void *addr) ++static void *mem_to_shadow(const void *addr) + { +- if (!kasan_enabled()) { ++ unsigned long offset = 0; ++ unsigned long maddr = (unsigned long)addr; ++ unsigned long xrange = (maddr >> XRANGE_SHIFT) & 0xffff; ++ ++ if (maddr >= FIXADDR_START) + return (void *)(kasan_early_shadow_page); +- } else { +- unsigned long maddr = (unsigned long)addr; +- unsigned long xrange = (maddr >> XRANGE_SHIFT) & 0xffff; +- unsigned long offset = 0; +- +- if (maddr >= FIXADDR_START) +- return (void *)(kasan_early_shadow_page); +- +- maddr &= XRANGE_SHADOW_MASK; +- switch (xrange) { +- case XKPRANGE_CC_SEG: +- offset = XKPRANGE_CC_SHADOW_OFFSET; +- break; +- case XKPRANGE_UC_SEG: +- offset = XKPRANGE_UC_SHADOW_OFFSET; +- break; +- case XKPRANGE_WC_SEG: +- offset = XKPRANGE_WC_SHADOW_OFFSET; +- break; +- case XKVRANGE_VC_SEG: +- offset = XKVRANGE_VC_SHADOW_OFFSET; +- break; +- default: +- WARN_ON(1); +- return NULL; +- } + +- return (void *)((maddr >> KASAN_SHADOW_SCALE_SHIFT) + offset); ++ maddr &= XRANGE_SHADOW_MASK; ++ switch (xrange) { ++ case XKPRANGE_CC_SEG: ++ offset = XKPRANGE_CC_SHADOW_OFFSET; ++ break; ++ case XKPRANGE_UC_SEG: ++ offset = XKPRANGE_UC_SHADOW_OFFSET; ++ break; ++ case XKPRANGE_WC_SEG: ++ offset = XKPRANGE_WC_SHADOW_OFFSET; ++ break; ++ case XKVRANGE_VC_SEG: ++ offset = XKVRANGE_VC_SHADOW_OFFSET; ++ break; ++ default: ++ WARN_ON(1); ++ return NULL; + } ++ ++ return (void *)((maddr >> KASAN_SHADOW_SCALE_SHIFT) + offset); ++} ++ ++void *kasan_mem_to_shadow(const void *addr) ++{ ++ if (kasan_enabled()) ++ return mem_to_shadow(addr); ++ else ++ return (void *)(kasan_early_shadow_page); + } + + const void *kasan_shadow_to_mem(const void *shadow_addr) +@@ -293,11 +297,8 @@ void __init kasan_init(void) + /* Maps everything to a single page of zeroes */ + kasan_pgd_populate(KASAN_SHADOW_START, KASAN_SHADOW_END, NUMA_NO_NODE, true); + +- kasan_populate_early_shadow(kasan_mem_to_shadow((void *)VMALLOC_START), +- kasan_mem_to_shadow((void *)KFENCE_AREA_END)); +- +- /* Enable KASAN here before kasan_mem_to_shadow(). */ +- kasan_init_generic(); ++ kasan_populate_early_shadow(mem_to_shadow((void *)VMALLOC_START), ++ mem_to_shadow((void *)KFENCE_AREA_END)); + + /* Populate the linear mapping */ + for_each_mem_range(i, &pa_start, &pa_end) { +@@ -307,13 +308,13 @@ void __init kasan_init(void) + if (start >= end) + break; + +- kasan_map_populate((unsigned long)kasan_mem_to_shadow(start), +- (unsigned long)kasan_mem_to_shadow(end), NUMA_NO_NODE); ++ kasan_map_populate((unsigned long)mem_to_shadow(start), ++ (unsigned long)mem_to_shadow(end), NUMA_NO_NODE); + } + + /* Populate modules mapping */ +- kasan_map_populate((unsigned long)kasan_mem_to_shadow((void *)MODULES_VADDR), +- (unsigned long)kasan_mem_to_shadow((void *)MODULES_END), NUMA_NO_NODE); ++ kasan_map_populate((unsigned long)mem_to_shadow((void *)MODULES_VADDR), ++ (unsigned long)mem_to_shadow((void *)MODULES_END), NUMA_NO_NODE); + /* + * KAsan may reuse the contents of kasan_early_shadow_pte directly, so we + * should make sure that it maps the zero page read-only. +@@ -328,4 +329,5 @@ void __init kasan_init(void) + + /* At this point kasan is fully initialized. Enable error messages */ + init_task.kasan_depth = 0; ++ kasan_init_generic(); + } +diff --git a/drivers/gpio/gpio-sprd.c b/drivers/gpio/gpio-sprd.c +index 413bcd0a424050..2cc8abe705cdb7 100644 +--- a/drivers/gpio/gpio-sprd.c ++++ b/drivers/gpio/gpio-sprd.c +@@ -35,7 +35,7 @@ + struct sprd_gpio { + struct gpio_chip chip; + void __iomem *base; +- spinlock_t lock; ++ raw_spinlock_t lock; + int irq; + }; + +@@ -54,7 +54,7 @@ static void sprd_gpio_update(struct gpio_chip *chip, unsigned int offset, + unsigned long flags; + u32 tmp; + +- spin_lock_irqsave(&sprd_gpio->lock, flags); ++ raw_spin_lock_irqsave(&sprd_gpio->lock, flags); + tmp = readl_relaxed(base + reg); + + if (val) +@@ -63,7 +63,7 @@ static void sprd_gpio_update(struct gpio_chip *chip, unsigned int offset, + tmp &= ~BIT(SPRD_GPIO_BIT(offset)); + + writel_relaxed(tmp, base + reg); +- spin_unlock_irqrestore(&sprd_gpio->lock, flags); ++ raw_spin_unlock_irqrestore(&sprd_gpio->lock, flags); + } + + static int sprd_gpio_read(struct gpio_chip *chip, unsigned int offset, u16 reg) +@@ -236,7 +236,7 @@ static int sprd_gpio_probe(struct platform_device *pdev) + if (IS_ERR(sprd_gpio->base)) + return PTR_ERR(sprd_gpio->base); + +- spin_lock_init(&sprd_gpio->lock); ++ raw_spin_lock_init(&sprd_gpio->lock); + + sprd_gpio->chip.label = dev_name(&pdev->dev); + sprd_gpio->chip.ngpio = SPRD_GPIO_NR; +diff --git a/drivers/gpio/gpiolib-acpi-core.c b/drivers/gpio/gpiolib-acpi-core.c +index e64e21fd6bbaaa..8110690ea69d0b 100644 +--- a/drivers/gpio/gpiolib-acpi-core.c ++++ b/drivers/gpio/gpiolib-acpi-core.c +@@ -1359,6 +1359,7 @@ static int acpi_gpio_package_count(const union acpi_object *obj) + while (element < end) { + switch (element->type) { + case ACPI_TYPE_LOCAL_REFERENCE: ++ case ACPI_TYPE_STRING: + element += 3; + fallthrough; + case ACPI_TYPE_INTEGER: +diff --git a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c +index a4f14b16564c2f..227aa8672d17ba 100644 +--- a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c ++++ b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c +@@ -105,9 +105,12 @@ void cm_helper_program_gamcor_xfer_func( + #define NUMBER_REGIONS 32 + #define NUMBER_SW_SEGMENTS 16 + +-bool cm3_helper_translate_curve_to_hw_format( +- const struct dc_transfer_func *output_tf, +- struct pwl_params *lut_params, bool fixpoint) ++#define DC_LOGGER \ ++ ctx->logger ++ ++bool cm3_helper_translate_curve_to_hw_format(struct dc_context *ctx, ++ const struct dc_transfer_func *output_tf, ++ struct pwl_params *lut_params, bool fixpoint) + { + struct curve_points3 *corner_points; + struct pwl_result_data *rgb_resulted; +@@ -251,6 +254,10 @@ bool cm3_helper_translate_curve_to_hw_format( + if (fixpoint == true) { + i = 1; + while (i != hw_points + 2) { ++ uint32_t red_clamp; ++ uint32_t green_clamp; ++ uint32_t blue_clamp; ++ + if (i >= hw_points) { + if (dc_fixpt_lt(rgb_plus_1->red, rgb->red)) + rgb_plus_1->red = dc_fixpt_add(rgb->red, +@@ -263,9 +270,20 @@ bool cm3_helper_translate_curve_to_hw_format( + rgb_minus_1->delta_blue); + } + +- rgb->delta_red_reg = dc_fixpt_clamp_u0d10(rgb->delta_red); +- rgb->delta_green_reg = dc_fixpt_clamp_u0d10(rgb->delta_green); +- rgb->delta_blue_reg = dc_fixpt_clamp_u0d10(rgb->delta_blue); ++ rgb->delta_red = dc_fixpt_sub(rgb_plus_1->red, rgb->red); ++ rgb->delta_green = dc_fixpt_sub(rgb_plus_1->green, rgb->green); ++ rgb->delta_blue = dc_fixpt_sub(rgb_plus_1->blue, rgb->blue); ++ ++ red_clamp = dc_fixpt_clamp_u0d14(rgb->delta_red); ++ green_clamp = dc_fixpt_clamp_u0d14(rgb->delta_green); ++ blue_clamp = dc_fixpt_clamp_u0d14(rgb->delta_blue); ++ ++ if (red_clamp >> 10 || green_clamp >> 10 || blue_clamp >> 10) ++ DC_LOG_ERROR("Losing delta precision while programming shaper LUT."); ++ ++ rgb->delta_red_reg = red_clamp & 0x3ff; ++ rgb->delta_green_reg = green_clamp & 0x3ff; ++ rgb->delta_blue_reg = blue_clamp & 0x3ff; + rgb->red_reg = dc_fixpt_clamp_u0d14(rgb->red); + rgb->green_reg = dc_fixpt_clamp_u0d14(rgb->green); + rgb->blue_reg = dc_fixpt_clamp_u0d14(rgb->blue); +diff --git a/drivers/gpu/drm/amd/display/dc/dwb/dcn30/dcn30_cm_common.h b/drivers/gpu/drm/amd/display/dc/dwb/dcn30/dcn30_cm_common.h +index b86347c9b0389d..95f9318a54efce 100644 +--- a/drivers/gpu/drm/amd/display/dc/dwb/dcn30/dcn30_cm_common.h ++++ b/drivers/gpu/drm/amd/display/dc/dwb/dcn30/dcn30_cm_common.h +@@ -59,7 +59,7 @@ void cm_helper_program_gamcor_xfer_func( + const struct pwl_params *params, + const struct dcn3_xfer_func_reg *reg); + +-bool cm3_helper_translate_curve_to_hw_format( ++bool cm3_helper_translate_curve_to_hw_format(struct dc_context *ctx, + const struct dc_transfer_func *output_tf, + struct pwl_params *lut_params, bool fixpoint); + +diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c +index e47ed5571dfdd8..731645a2ab9aa7 100644 +--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c ++++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c +@@ -238,7 +238,7 @@ bool dcn30_set_blend_lut( + if (plane_state->blend_tf.type == TF_TYPE_HWPWL) + blend_lut = &plane_state->blend_tf.pwl; + else if (plane_state->blend_tf.type == TF_TYPE_DISTRIBUTED_POINTS) { +- result = cm3_helper_translate_curve_to_hw_format( ++ result = cm3_helper_translate_curve_to_hw_format(plane_state->ctx, + &plane_state->blend_tf, &dpp_base->regamma_params, false); + if (!result) + return result; +@@ -333,8 +333,9 @@ bool dcn30_set_input_transfer_func(struct dc *dc, + if (plane_state->in_transfer_func.type == TF_TYPE_HWPWL) + params = &plane_state->in_transfer_func.pwl; + else if (plane_state->in_transfer_func.type == TF_TYPE_DISTRIBUTED_POINTS && +- cm3_helper_translate_curve_to_hw_format(&plane_state->in_transfer_func, +- &dpp_base->degamma_params, false)) ++ cm3_helper_translate_curve_to_hw_format(plane_state->ctx, ++ &plane_state->in_transfer_func, ++ &dpp_base->degamma_params, false)) + params = &dpp_base->degamma_params; + + result = dpp_base->funcs->dpp_program_gamcor_lut(dpp_base, params); +@@ -405,7 +406,7 @@ bool dcn30_set_output_transfer_func(struct dc *dc, + params = &stream->out_transfer_func.pwl; + else if (pipe_ctx->stream->out_transfer_func.type == + TF_TYPE_DISTRIBUTED_POINTS && +- cm3_helper_translate_curve_to_hw_format( ++ cm3_helper_translate_curve_to_hw_format(stream->ctx, + &stream->out_transfer_func, + &mpc->blender_params, false)) + params = &mpc->blender_params; +diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c +index f39292952702fa..c6fde355ac8235 100644 +--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c ++++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c +@@ -486,8 +486,9 @@ bool dcn32_set_mcm_luts( + if (plane_state->blend_tf.type == TF_TYPE_HWPWL) + lut_params = &plane_state->blend_tf.pwl; + else if (plane_state->blend_tf.type == TF_TYPE_DISTRIBUTED_POINTS) { +- result = cm3_helper_translate_curve_to_hw_format(&plane_state->blend_tf, +- &dpp_base->regamma_params, false); ++ result = cm3_helper_translate_curve_to_hw_format(plane_state->ctx, ++ &plane_state->blend_tf, ++ &dpp_base->regamma_params, false); + if (!result) + return result; + +@@ -501,9 +502,9 @@ bool dcn32_set_mcm_luts( + lut_params = &plane_state->in_shaper_func.pwl; + else if (plane_state->in_shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) { + // TODO: dpp_base replace +- ASSERT(false); +- cm3_helper_translate_curve_to_hw_format(&plane_state->in_shaper_func, +- &dpp_base->shaper_params, true); ++ cm3_helper_translate_curve_to_hw_format(plane_state->ctx, ++ &plane_state->in_shaper_func, ++ &dpp_base->shaper_params, true); + lut_params = &dpp_base->shaper_params; + } + +@@ -543,8 +544,9 @@ bool dcn32_set_input_transfer_func(struct dc *dc, + if (plane_state->in_transfer_func.type == TF_TYPE_HWPWL) + params = &plane_state->in_transfer_func.pwl; + else if (plane_state->in_transfer_func.type == TF_TYPE_DISTRIBUTED_POINTS && +- cm3_helper_translate_curve_to_hw_format(&plane_state->in_transfer_func, +- &dpp_base->degamma_params, false)) ++ cm3_helper_translate_curve_to_hw_format(plane_state->ctx, ++ &plane_state->in_transfer_func, ++ &dpp_base->degamma_params, false)) + params = &dpp_base->degamma_params; + + dpp_base->funcs->dpp_program_gamcor_lut(dpp_base, params); +@@ -575,7 +577,7 @@ bool dcn32_set_output_transfer_func(struct dc *dc, + params = &stream->out_transfer_func.pwl; + else if (pipe_ctx->stream->out_transfer_func.type == + TF_TYPE_DISTRIBUTED_POINTS && +- cm3_helper_translate_curve_to_hw_format( ++ cm3_helper_translate_curve_to_hw_format(stream->ctx, + &stream->out_transfer_func, + &mpc->blender_params, false)) + params = &mpc->blender_params; +diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c +index 68e48a2492c9ee..77cdd02a41bdd0 100644 +--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c ++++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c +@@ -427,7 +427,7 @@ void dcn401_populate_mcm_luts(struct dc *dc, + if (mcm_luts.lut1d_func->type == TF_TYPE_HWPWL) + m_lut_params.pwl = &mcm_luts.lut1d_func->pwl; + else if (mcm_luts.lut1d_func->type == TF_TYPE_DISTRIBUTED_POINTS) { +- rval = cm3_helper_translate_curve_to_hw_format( ++ rval = cm3_helper_translate_curve_to_hw_format(mpc->ctx, + mcm_luts.lut1d_func, + &dpp_base->regamma_params, false); + m_lut_params.pwl = rval ? &dpp_base->regamma_params : NULL; +@@ -447,7 +447,7 @@ void dcn401_populate_mcm_luts(struct dc *dc, + m_lut_params.pwl = &mcm_luts.shaper->pwl; + else if (mcm_luts.shaper->type == TF_TYPE_DISTRIBUTED_POINTS) { + ASSERT(false); +- rval = cm3_helper_translate_curve_to_hw_format( ++ rval = cm3_helper_translate_curve_to_hw_format(mpc->ctx, + mcm_luts.shaper, + &dpp_base->regamma_params, true); + m_lut_params.pwl = rval ? &dpp_base->regamma_params : NULL; +@@ -624,8 +624,9 @@ bool dcn401_set_mcm_luts(struct pipe_ctx *pipe_ctx, + if (plane_state->blend_tf.type == TF_TYPE_HWPWL) + lut_params = &plane_state->blend_tf.pwl; + else if (plane_state->blend_tf.type == TF_TYPE_DISTRIBUTED_POINTS) { +- rval = cm3_helper_translate_curve_to_hw_format(&plane_state->blend_tf, +- &dpp_base->regamma_params, false); ++ rval = cm3_helper_translate_curve_to_hw_format(plane_state->ctx, ++ &plane_state->blend_tf, ++ &dpp_base->regamma_params, false); + lut_params = rval ? &dpp_base->regamma_params : NULL; + } + result = mpc->funcs->program_1dlut(mpc, lut_params, mpcc_id); +@@ -636,8 +637,9 @@ bool dcn401_set_mcm_luts(struct pipe_ctx *pipe_ctx, + lut_params = &plane_state->in_shaper_func.pwl; + else if (plane_state->in_shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) { + // TODO: dpp_base replace +- rval = cm3_helper_translate_curve_to_hw_format(&plane_state->in_shaper_func, +- &dpp_base->shaper_params, true); ++ rval = cm3_helper_translate_curve_to_hw_format(plane_state->ctx, ++ &plane_state->in_shaper_func, ++ &dpp_base->shaper_params, true); + lut_params = rval ? &dpp_base->shaper_params : NULL; + } + result &= mpc->funcs->program_shaper(mpc, lut_params, mpcc_id); +@@ -671,7 +673,7 @@ bool dcn401_set_output_transfer_func(struct dc *dc, + params = &stream->out_transfer_func.pwl; + else if (pipe_ctx->stream->out_transfer_func.type == + TF_TYPE_DISTRIBUTED_POINTS && +- cm3_helper_translate_curve_to_hw_format( ++ cm3_helper_translate_curve_to_hw_format(stream->ctx, + &stream->out_transfer_func, + &mpc->blender_params, false)) + params = &mpc->blender_params; +diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c +index 8cd2969e7d4bfe..c4820f5e765813 100644 +--- a/drivers/gpu/drm/tegra/hdmi.c ++++ b/drivers/gpu/drm/tegra/hdmi.c +@@ -658,7 +658,7 @@ static void tegra_hdmi_write_infopack(struct tegra_hdmi *hdmi, const void *data, + { + const u8 *ptr = data; + unsigned long offset; +- size_t i, j; ++ size_t i; + u32 value; + + switch (ptr[0]) { +@@ -691,7 +691,7 @@ static void tegra_hdmi_write_infopack(struct tegra_hdmi *hdmi, const void *data, + * - subpack_low: bytes 0 - 3 + * - subpack_high: bytes 4 - 6 (with byte 7 padded to 0x00) + */ +- for (i = 3, j = 0; i < size; i += 7, j += 8) { ++ for (i = 3; i < size; i += 7) { + size_t rem = size - i, num = min_t(size_t, rem, 4); + + value = tegra_hdmi_subpack(&ptr[i], num); +diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c +index 21f3dfdcc5c957..bc7dd562cf6b6b 100644 +--- a/drivers/gpu/drm/tegra/sor.c ++++ b/drivers/gpu/drm/tegra/sor.c +@@ -1864,7 +1864,7 @@ static void tegra_sor_hdmi_write_infopack(struct tegra_sor *sor, + { + const u8 *ptr = data; + unsigned long offset; +- size_t i, j; ++ size_t i; + u32 value; + + switch (ptr[0]) { +@@ -1897,7 +1897,7 @@ static void tegra_sor_hdmi_write_infopack(struct tegra_sor *sor, + * - subpack_low: bytes 0 - 3 + * - subpack_high: bytes 4 - 6 (with byte 7 padded to 0x00) + */ +- for (i = 3, j = 0; i < size; i += 7, j += 8) { ++ for (i = 3; i < size; i += 7) { + size_t rem = size - i, num = min_t(size_t, rem, 4); + + value = tegra_sor_hdmi_subpack(&ptr[i], num); +diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu-impl.c b/drivers/iommu/arm/arm-smmu/arm-smmu-impl.c +index db9b9a8e139c87..4565a58bb213f4 100644 +--- a/drivers/iommu/arm/arm-smmu/arm-smmu-impl.c ++++ b/drivers/iommu/arm/arm-smmu/arm-smmu-impl.c +@@ -228,3 +228,17 @@ struct arm_smmu_device *arm_smmu_impl_init(struct arm_smmu_device *smmu) + + return smmu; + } ++ ++int __init arm_smmu_impl_module_init(void) ++{ ++ if (IS_ENABLED(CONFIG_ARM_SMMU_QCOM)) ++ return qcom_smmu_module_init(); ++ ++ return 0; ++} ++ ++void __exit arm_smmu_impl_module_exit(void) ++{ ++ if (IS_ENABLED(CONFIG_ARM_SMMU_QCOM)) ++ qcom_smmu_module_exit(); ++} +diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c +index c939d0856b719c..2c442aa2181571 100644 +--- a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c ++++ b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c +@@ -773,10 +773,6 @@ struct arm_smmu_device *qcom_smmu_impl_init(struct arm_smmu_device *smmu) + { + const struct device_node *np = smmu->dev->of_node; + const struct of_device_id *match; +- static u8 tbu_registered; +- +- if (!tbu_registered++) +- platform_driver_register(&qcom_smmu_tbu_driver); + + #ifdef CONFIG_ACPI + if (np == NULL) { +@@ -801,3 +797,13 @@ struct arm_smmu_device *qcom_smmu_impl_init(struct arm_smmu_device *smmu) + + return smmu; + } ++ ++int __init qcom_smmu_module_init(void) ++{ ++ return platform_driver_register(&qcom_smmu_tbu_driver); ++} ++ ++void __exit qcom_smmu_module_exit(void) ++{ ++ platform_driver_unregister(&qcom_smmu_tbu_driver); ++} +diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.c b/drivers/iommu/arm/arm-smmu/arm-smmu.c +index 4ced4b5bee4df3..488632b8eeab61 100644 +--- a/drivers/iommu/arm/arm-smmu/arm-smmu.c ++++ b/drivers/iommu/arm/arm-smmu/arm-smmu.c +@@ -2362,7 +2362,29 @@ static struct platform_driver arm_smmu_driver = { + .remove = arm_smmu_device_remove, + .shutdown = arm_smmu_device_shutdown, + }; +-module_platform_driver(arm_smmu_driver); ++ ++static int __init arm_smmu_init(void) ++{ ++ int ret; ++ ++ ret = platform_driver_register(&arm_smmu_driver); ++ if (ret) ++ return ret; ++ ++ ret = arm_smmu_impl_module_init(); ++ if (ret) ++ platform_driver_unregister(&arm_smmu_driver); ++ ++ return ret; ++} ++module_init(arm_smmu_init); ++ ++static void __exit arm_smmu_exit(void) ++{ ++ arm_smmu_impl_module_exit(); ++ platform_driver_unregister(&arm_smmu_driver); ++} ++module_exit(arm_smmu_exit); + + MODULE_DESCRIPTION("IOMMU API for ARM architected SMMU implementations"); + MODULE_AUTHOR("Will Deacon <[email protected]>"); +diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.h b/drivers/iommu/arm/arm-smmu/arm-smmu.h +index 2dbf3243b5ad2d..26d2e33cd328b8 100644 +--- a/drivers/iommu/arm/arm-smmu/arm-smmu.h ++++ b/drivers/iommu/arm/arm-smmu/arm-smmu.h +@@ -540,6 +540,11 @@ struct arm_smmu_device *arm_smmu_impl_init(struct arm_smmu_device *smmu); + struct arm_smmu_device *nvidia_smmu_impl_init(struct arm_smmu_device *smmu); + struct arm_smmu_device *qcom_smmu_impl_init(struct arm_smmu_device *smmu); + ++int __init arm_smmu_impl_module_init(void); ++void __exit arm_smmu_impl_module_exit(void); ++int __init qcom_smmu_module_init(void); ++void __exit qcom_smmu_module_exit(void); ++ + void arm_smmu_write_context_bank(struct arm_smmu_device *smmu, int idx); + int arm_mmu500_reset(struct arm_smmu_device *smmu); + +diff --git a/drivers/platform/x86/amd/pmc/pmc-quirks.c b/drivers/platform/x86/amd/pmc/pmc-quirks.c +index 404e62ad293a98..ed285afaf9b0d7 100644 +--- a/drivers/platform/x86/amd/pmc/pmc-quirks.c ++++ b/drivers/platform/x86/amd/pmc/pmc-quirks.c +@@ -302,6 +302,13 @@ static const struct dmi_system_id fwbug_list[] = { + DMI_MATCH(DMI_BOARD_NAME, "XxKK4NAx_XxSP4NAx"), + } + }, ++ { ++ .ident = "MECHREVO Wujie 15X Pro", ++ .driver_data = &quirk_spurious_8042, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_NAME, "WUJIE Series-X5SP4NAG"), ++ } ++ }, + {} + }; + +diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c +index 6b1b8e444e241c..74d3eb83f56a63 100644 +--- a/drivers/platform/x86/classmate-laptop.c ++++ b/drivers/platform/x86/classmate-laptop.c +@@ -207,7 +207,12 @@ static ssize_t cmpc_accel_sensitivity_show_v4(struct device *dev, + + acpi = to_acpi_device(dev); + inputdev = dev_get_drvdata(&acpi->dev); ++ if (!inputdev) ++ return -ENXIO; ++ + accel = dev_get_drvdata(&inputdev->dev); ++ if (!accel) ++ return -ENXIO; + + return sysfs_emit(buf, "%d\n", accel->sensitivity); + } +@@ -224,7 +229,12 @@ static ssize_t cmpc_accel_sensitivity_store_v4(struct device *dev, + + acpi = to_acpi_device(dev); + inputdev = dev_get_drvdata(&acpi->dev); ++ if (!inputdev) ++ return -ENXIO; ++ + accel = dev_get_drvdata(&inputdev->dev); ++ if (!accel) ++ return -ENXIO; + + r = kstrtoul(buf, 0, &sensitivity); + if (r) +@@ -256,7 +266,12 @@ static ssize_t cmpc_accel_g_select_show_v4(struct device *dev, + + acpi = to_acpi_device(dev); + inputdev = dev_get_drvdata(&acpi->dev); ++ if (!inputdev) ++ return -ENXIO; ++ + accel = dev_get_drvdata(&inputdev->dev); ++ if (!accel) ++ return -ENXIO; + + return sysfs_emit(buf, "%d\n", accel->g_select); + } +@@ -273,7 +288,12 @@ static ssize_t cmpc_accel_g_select_store_v4(struct device *dev, + + acpi = to_acpi_device(dev); + inputdev = dev_get_drvdata(&acpi->dev); ++ if (!inputdev) ++ return -ENXIO; ++ + accel = dev_get_drvdata(&inputdev->dev); ++ if (!accel) ++ return -ENXIO; + + r = kstrtoul(buf, 0, &g_select); + if (r) +@@ -302,6 +322,8 @@ static int cmpc_accel_open_v4(struct input_dev *input) + + acpi = to_acpi_device(input->dev.parent); + accel = dev_get_drvdata(&input->dev); ++ if (!accel) ++ return -ENXIO; + + cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity); + cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select); +@@ -549,7 +571,12 @@ static ssize_t cmpc_accel_sensitivity_show(struct device *dev, + + acpi = to_acpi_device(dev); + inputdev = dev_get_drvdata(&acpi->dev); ++ if (!inputdev) ++ return -ENXIO; ++ + accel = dev_get_drvdata(&inputdev->dev); ++ if (!accel) ++ return -ENXIO; + + return sysfs_emit(buf, "%d\n", accel->sensitivity); + } +@@ -566,7 +593,12 @@ static ssize_t cmpc_accel_sensitivity_store(struct device *dev, + + acpi = to_acpi_device(dev); + inputdev = dev_get_drvdata(&acpi->dev); ++ if (!inputdev) ++ return -ENXIO; ++ + accel = dev_get_drvdata(&inputdev->dev); ++ if (!accel) ++ return -ENXIO; + + r = kstrtoul(buf, 0, &sensitivity); + if (r) +diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c +index 255317e6fec881..937f1a5b78edfb 100644 +--- a/drivers/platform/x86/panasonic-laptop.c ++++ b/drivers/platform/x86/panasonic-laptop.c +@@ -1089,7 +1089,7 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) + PLATFORM_DEVID_NONE, NULL, 0); + if (IS_ERR(pcc->platform)) { + result = PTR_ERR(pcc->platform); +- goto out_backlight; ++ goto out_sysfs; + } + result = device_create_file(&pcc->platform->dev, + &dev_attr_cdpower); +@@ -1105,6 +1105,8 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) + + out_platform: + platform_device_unregister(pcc->platform); ++out_sysfs: ++ sysfs_remove_group(&device->dev.kobj, &pcc_attr_group); + out_backlight: + backlight_device_unregister(pcc->backlight); + out_input: +diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c +index ccfc2d26dd3725..0798bfd0372e47 100644 +--- a/drivers/scsi/qla2xxx/qla_bsg.c ++++ b/drivers/scsi/qla2xxx/qla_bsg.c +@@ -1546,8 +1546,9 @@ qla2x00_update_optrom(struct bsg_job *bsg_job) + ha->optrom_buffer = NULL; + ha->optrom_state = QLA_SWAITING; + mutex_unlock(&ha->optrom_mutex); +- bsg_job_done(bsg_job, bsg_reply->result, +- bsg_reply->reply_payload_rcv_len); ++ if (!rval) ++ bsg_job_done(bsg_job, bsg_reply->result, ++ bsg_reply->reply_payload_rcv_len); + return rval; + } + +@@ -2612,8 +2613,9 @@ qla2x00_manage_host_stats(struct bsg_job *bsg_job) + sizeof(struct ql_vnd_mng_host_stats_resp)); + + bsg_reply->result = DID_OK; +- bsg_job_done(bsg_job, bsg_reply->result, +- bsg_reply->reply_payload_rcv_len); ++ if (!ret) ++ bsg_job_done(bsg_job, bsg_reply->result, ++ bsg_reply->reply_payload_rcv_len); + + return ret; + } +@@ -2702,8 +2704,9 @@ qla2x00_get_host_stats(struct bsg_job *bsg_job) + bsg_job->reply_payload.sg_cnt, + data, response_len); + bsg_reply->result = DID_OK; +- bsg_job_done(bsg_job, bsg_reply->result, +- bsg_reply->reply_payload_rcv_len); ++ if (!ret) ++ bsg_job_done(bsg_job, bsg_reply->result, ++ bsg_reply->reply_payload_rcv_len); + + kfree(data); + host_stat_out: +@@ -2802,8 +2805,9 @@ reply: + bsg_job->reply_payload.sg_cnt, data, + response_len); + bsg_reply->result = DID_OK; +- bsg_job_done(bsg_job, bsg_reply->result, +- bsg_reply->reply_payload_rcv_len); ++ if (!ret) ++ bsg_job_done(bsg_job, bsg_reply->result, ++ bsg_reply->reply_payload_rcv_len); + + tgt_stat_out: + kfree(data); +@@ -2864,8 +2868,9 @@ qla2x00_manage_host_port(struct bsg_job *bsg_job) + bsg_job->reply_payload.sg_cnt, &rsp_data, + sizeof(struct ql_vnd_mng_host_port_resp)); + bsg_reply->result = DID_OK; +- bsg_job_done(bsg_job, bsg_reply->result, +- bsg_reply->reply_payload_rcv_len); ++ if (!ret) ++ bsg_job_done(bsg_job, bsg_reply->result, ++ bsg_reply->reply_payload_rcv_len); + + return ret; + } +@@ -3240,7 +3245,8 @@ int qla2x00_mailbox_passthru(struct bsg_job *bsg_job) + + bsg_job->reply_len = sizeof(*bsg_job->reply); + bsg_reply->result = DID_OK << 16; +- bsg_job_done(bsg_job, bsg_reply->result, bsg_reply->reply_payload_rcv_len); ++ if (!ret) ++ bsg_job_done(bsg_job, bsg_reply->result, bsg_reply->reply_payload_rcv_len); + + kfree(req_data); + +diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c +index 9f2cc5fb9f4562..d4505a4264460b 100644 +--- a/drivers/usb/serial/option.c ++++ b/drivers/usb/serial/option.c +@@ -1401,12 +1401,16 @@ static const struct usb_device_id option_ids[] = { + .driver_info = NCTRL(0) | RSVD(1) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a0, 0xff), /* Telit FN20C04 (rmnet) */ + .driver_info = RSVD(0) | NCTRL(3) }, ++ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a1, 0xff), /* Telit FN20C04 (RNDIS) */ ++ .driver_info = NCTRL(4) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a2, 0xff), /* Telit FN920C04 (MBIM) */ + .driver_info = NCTRL(4) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a3, 0xff), /* Telit FN920C04 (ECM) */ + .driver_info = NCTRL(4) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a4, 0xff), /* Telit FN20C04 (rmnet) */ + .driver_info = RSVD(0) | NCTRL(3) }, ++ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a6, 0xff), /* Telit FN920C04 (RNDIS) */ ++ .driver_info = NCTRL(4) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a7, 0xff), /* Telit FN920C04 (MBIM) */ + .driver_info = NCTRL(4) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a8, 0xff), /* Telit FN920C04 (ECM) */ +@@ -1415,6 +1419,8 @@ static const struct usb_device_id option_ids[] = { + .driver_info = RSVD(0) | NCTRL(2) | RSVD(3) | RSVD(4) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10aa, 0xff), /* Telit FN920C04 (MBIM) */ + .driver_info = NCTRL(3) | RSVD(4) | RSVD(5) }, ++ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10ab, 0xff), /* Telit FN920C04 (RNDIS) */ ++ .driver_info = NCTRL(3) | RSVD(4) | RSVD(5) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b0, 0xff, 0xff, 0x30), /* Telit FE990B (rmnet) */ + .driver_info = NCTRL(5) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b0, 0xff, 0xff, 0x40) }, +diff --git a/drivers/video/fbdev/riva/riva_hw.c b/drivers/video/fbdev/riva/riva_hw.c +index 8b829b7200642f..f292079566cfca 100644 +--- a/drivers/video/fbdev/riva/riva_hw.c ++++ b/drivers/video/fbdev/riva/riva_hw.c +@@ -436,6 +436,9 @@ static char nv3_arb(nv3_fifo_info * res_info, nv3_sim_state * state, nv3_arb_in + vmisses = 2; + eburst_size = state->memory_width * 1; + mburst_size = 32; ++ if (!state->mclk_khz) ++ return (0); ++ + gns = 1000000 * (gmisses*state->mem_page_miss + state->mem_latency)/state->mclk_khz; + ainfo->by_gfacc = gns*ainfo->gdrain_rate/1000000; + ainfo->wcmocc = 0; +diff --git a/drivers/video/fbdev/smscufx.c b/drivers/video/fbdev/smscufx.c +index 5f0dd01fd83495..891ce7b76d637e 100644 +--- a/drivers/video/fbdev/smscufx.c ++++ b/drivers/video/fbdev/smscufx.c +@@ -932,7 +932,6 @@ static int ufx_ops_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) + { + struct ufx_data *dev = info->par; +- struct dloarea *area = NULL; + + if (!atomic_read(&dev->usb_active)) + return 0; +@@ -947,6 +946,10 @@ static int ufx_ops_ioctl(struct fb_info *info, unsigned int cmd, + + /* TODO: Help propose a standard fb.h ioctl to report mmap damage */ + if (cmd == UFX_IOCTL_REPORT_DAMAGE) { ++ struct dloarea *area __free(kfree) = kmalloc(sizeof(*area), GFP_KERNEL); ++ if (!area) ++ return -ENOMEM; ++ + /* If we have a damage-aware client, turn fb_defio "off" + * To avoid perf imact of unnecessary page fault handling. + * Done by resetting the delay for this fb_info to a very +@@ -956,7 +959,8 @@ static int ufx_ops_ioctl(struct fb_info *info, unsigned int cmd, + if (info->fbdefio) + info->fbdefio->delay = UFX_DEFIO_WRITE_DISABLE; + +- area = (struct dloarea *)arg; ++ if (copy_from_user(area, (u8 __user *)arg, sizeof(*area))) ++ return -EFAULT; + + if (area->x < 0) + area->x = 0; +diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c +index 0f9446143c8e17..3ac0ecbf3ced38 100644 +--- a/fs/f2fs/data.c ++++ b/fs/f2fs/data.c +@@ -151,6 +151,12 @@ static void f2fs_finish_read_bio(struct bio *bio, bool in_task) + } + + dec_page_count(F2FS_F_SB(folio), __read_io_type(folio)); ++ ++ if (F2FS_F_SB(folio)->node_inode && is_node_folio(folio) && ++ f2fs_sanity_check_node_footer(F2FS_F_SB(folio), ++ folio, folio->index, NODE_TYPE_REGULAR, true)) ++ bio->bi_status = BLK_STS_IOERR; ++ + folio_end_read(folio, bio->bi_status == BLK_STS_OK); + } + +@@ -352,18 +358,27 @@ static void f2fs_write_end_io(struct bio *bio) + STOP_CP_REASON_WRITE_FAIL); + } + +- f2fs_bug_on(sbi, is_node_folio(folio) && +- folio->index != nid_of_node(folio)); ++ if (is_node_folio(folio)) { ++ f2fs_sanity_check_node_footer(sbi, folio, ++ folio->index, NODE_TYPE_REGULAR, true); ++ f2fs_bug_on(sbi, folio->index != nid_of_node(folio)); ++ } + + dec_page_count(sbi, type); ++ ++ /* ++ * we should access sbi before folio_end_writeback() to ++ * avoid racing w/ kill_f2fs_super() ++ */ ++ if (type == F2FS_WB_CP_DATA && !get_pages(sbi, type) && ++ wq_has_sleeper(&sbi->cp_wait)) ++ wake_up(&sbi->cp_wait); ++ + if (f2fs_in_warm_node_list(sbi, folio)) + f2fs_del_fsync_node_entry(sbi, folio); + folio_clear_f2fs_gcing(folio); + folio_end_writeback(folio); + } +- if (!get_pages(sbi, F2FS_WB_CP_DATA) && +- wq_has_sleeper(&sbi->cp_wait)) +- wake_up(&sbi->cp_wait); + + bio_put(bio); + } +@@ -1793,7 +1808,8 @@ out: + return err; + } + +-bool f2fs_overwrite_io(struct inode *inode, loff_t pos, size_t len) ++static bool __f2fs_overwrite_io(struct inode *inode, loff_t pos, size_t len, ++ bool check_first) + { + struct f2fs_map_blocks map; + block_t last_lblk; +@@ -1815,10 +1831,17 @@ bool f2fs_overwrite_io(struct inode *inode, loff_t pos, size_t len) + if (err || map.m_len == 0) + return false; + map.m_lblk += map.m_len; ++ if (check_first) ++ break; + } + return true; + } + ++bool f2fs_overwrite_io(struct inode *inode, loff_t pos, size_t len) ++{ ++ return __f2fs_overwrite_io(inode, pos, len, false); ++} ++ + static int f2fs_xattr_fiemap(struct inode *inode, + struct fiemap_extent_info *fieinfo) + { +@@ -3933,6 +3956,7 @@ static int check_swap_activate(struct swap_info_struct *sis, + + while (cur_lblock < last_lblock && cur_lblock < sis->max) { + struct f2fs_map_blocks map; ++ bool last_extent = false; + retry: + cond_resched(); + +@@ -3958,11 +3982,10 @@ retry: + pblock = map.m_pblk; + nr_pblocks = map.m_len; + +- if ((pblock - SM_I(sbi)->main_blkaddr) % blks_per_sec || +- nr_pblocks % blks_per_sec || +- f2fs_is_sequential_zone_area(sbi, pblock)) { +- bool last_extent = false; +- ++ if (!last_extent && ++ ((pblock - SM_I(sbi)->main_blkaddr) % blks_per_sec || ++ nr_pblocks % blks_per_sec || ++ f2fs_is_sequential_zone_area(sbi, pblock))) { + not_aligned++; + + nr_pblocks = roundup(nr_pblocks, blks_per_sec); +@@ -3983,8 +4006,8 @@ retry: + goto out; + } + +- if (!last_extent) +- goto retry; ++ /* lookup block mapping info after block migration */ ++ goto retry; + } + + if (cur_lblock + nr_pblocks >= sis->max) +@@ -4185,7 +4208,7 @@ static int f2fs_iomap_begin(struct inode *inode, loff_t offset, loff_t length, + * f2fs_map_lock and f2fs_balance_fs are not necessary. + */ + if ((flags & IOMAP_WRITE) && +- !f2fs_overwrite_io(inode, offset, length)) ++ !__f2fs_overwrite_io(inode, offset, length, true)) + map.m_may_create = true; + + err = f2fs_map_blocks(inode, &map, F2FS_GET_BLOCK_DIO); +diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h +index 37ad0c27c5b480..123c50f6619ae4 100644 +--- a/fs/f2fs/f2fs.h ++++ b/fs/f2fs/f2fs.h +@@ -508,13 +508,25 @@ struct fsync_inode_entry { + #define nats_in_cursum(jnl) (le16_to_cpu((jnl)->n_nats)) + #define sits_in_cursum(jnl) (le16_to_cpu((jnl)->n_sits)) + +-#define nat_in_journal(jnl, i) ((jnl)->nat_j.entries[i].ne) +-#define nid_in_journal(jnl, i) ((jnl)->nat_j.entries[i].nid) +-#define sit_in_journal(jnl, i) ((jnl)->sit_j.entries[i].se) +-#define segno_in_journal(jnl, i) ((jnl)->sit_j.entries[i].segno) +- +-#define MAX_NAT_JENTRIES(jnl) (NAT_JOURNAL_ENTRIES - nats_in_cursum(jnl)) +-#define MAX_SIT_JENTRIES(jnl) (SIT_JOURNAL_ENTRIES - sits_in_cursum(jnl)) ++#define nat_in_journal(jnl, i) \ ++ (((struct nat_journal_entry *)(jnl)->nat_j.entries)[i].ne) ++#define nid_in_journal(jnl, i) \ ++ (((struct nat_journal_entry *)(jnl)->nat_j.entries)[i].nid) ++#define sit_in_journal(jnl, i) \ ++ (((struct sit_journal_entry *)(jnl)->sit_j.entries)[i].se) ++#define segno_in_journal(jnl, i) \ ++ (((struct sit_journal_entry *)(jnl)->sit_j.entries)[i].segno) ++ ++#define sum_entries(sum) ((struct f2fs_summary *)(sum)) ++#define sum_journal(sbi, sum) \ ++ ((struct f2fs_journal *)((char *)(sum) + \ ++ ((sbi)->entries_in_sum * sizeof(struct f2fs_summary)))) ++#define sum_footer(sbi, sum) \ ++ ((struct summary_footer *)((char *)(sum) + (sbi)->sum_blocksize - \ ++ sizeof(struct summary_footer))) ++ ++#define MAX_NAT_JENTRIES(sbi, jnl) ((sbi)->nat_journal_entries - nats_in_cursum(jnl)) ++#define MAX_SIT_JENTRIES(sbi, jnl) ((sbi)->sit_journal_entries - sits_in_cursum(jnl)) + + static inline int update_nats_in_cursum(struct f2fs_journal *journal, int i) + { +@@ -532,14 +544,6 @@ static inline int update_sits_in_cursum(struct f2fs_journal *journal, int i) + return before; + } + +-static inline bool __has_cursum_space(struct f2fs_journal *journal, +- int size, int type) +-{ +- if (type == NAT_JOURNAL) +- return size <= MAX_NAT_JENTRIES(journal); +- return size <= MAX_SIT_JENTRIES(journal); +-} +- + /* for inline stuff */ + #define DEF_INLINE_RESERVED_SIZE 1 + static inline int get_extra_isize(struct inode *inode); +@@ -1512,6 +1516,15 @@ enum f2fs_lookup_mode { + LOOKUP_AUTO, + }; + ++/* For node type in __get_node_folio() */ ++enum node_type { ++ NODE_TYPE_REGULAR, ++ NODE_TYPE_INODE, ++ NODE_TYPE_XATTR, ++ NODE_TYPE_NON_INODE, ++}; ++ ++ + static inline int f2fs_test_bit(unsigned int nr, char *addr); + static inline void f2fs_set_bit(unsigned int nr, char *addr); + static inline void f2fs_clear_bit(unsigned int nr, char *addr); +@@ -1750,6 +1763,15 @@ struct f2fs_sb_info { + bool readdir_ra; /* readahead inode in readdir */ + u64 max_io_bytes; /* max io bytes to merge IOs */ + ++ /* variable summary block units */ ++ unsigned int sum_blocksize; /* sum block size */ ++ unsigned int sums_per_block; /* sum block count per block */ ++ unsigned int entries_in_sum; /* entry count in sum block */ ++ unsigned int sum_entry_size; /* total entry size in sum block */ ++ unsigned int sum_journal_size; /* journal size in sum block */ ++ unsigned int nat_journal_entries; /* nat journal entry count in the journal */ ++ unsigned int sit_journal_entries; /* sit journal entry count in the journal */ ++ + block_t user_block_count; /* # of user blocks */ + block_t total_valid_block_count; /* # of valid blocks */ + block_t discard_blks; /* discard command candidats */ +@@ -2799,6 +2821,14 @@ static inline block_t __start_sum_addr(struct f2fs_sb_info *sbi) + return le32_to_cpu(F2FS_CKPT(sbi)->cp_pack_start_sum); + } + ++static inline bool __has_cursum_space(struct f2fs_sb_info *sbi, ++ struct f2fs_journal *journal, int size, int type) ++{ ++ if (type == NAT_JOURNAL) ++ return size <= MAX_NAT_JENTRIES(sbi, journal); ++ return size <= MAX_SIT_JENTRIES(sbi, journal); ++} ++ + extern void f2fs_mark_inode_dirty_sync(struct inode *inode, bool sync); + static inline int inc_valid_node_count(struct f2fs_sb_info *sbi, + struct inode *inode, bool is_inode) +@@ -3853,6 +3883,9 @@ struct folio *f2fs_new_node_folio(struct dnode_of_data *dn, unsigned int ofs); + void f2fs_ra_node_page(struct f2fs_sb_info *sbi, nid_t nid); + struct folio *f2fs_get_node_folio(struct f2fs_sb_info *sbi, pgoff_t nid, + enum node_type node_type); ++int f2fs_sanity_check_node_footer(struct f2fs_sb_info *sbi, ++ struct folio *folio, pgoff_t nid, ++ enum node_type ntype, bool in_irq); + struct folio *f2fs_get_inode_folio(struct f2fs_sb_info *sbi, pgoff_t ino); + struct folio *f2fs_get_xnode_folio(struct f2fs_sb_info *sbi, pgoff_t xnid); + int f2fs_move_node_folio(struct folio *node_folio, int gc_type); +@@ -3952,7 +3985,8 @@ void f2fs_wait_on_block_writeback_range(struct inode *inode, block_t blkaddr, + block_t len); + void f2fs_write_data_summaries(struct f2fs_sb_info *sbi, block_t start_blk); + void f2fs_write_node_summaries(struct f2fs_sb_info *sbi, block_t start_blk); +-int f2fs_lookup_journal_in_cursum(struct f2fs_journal *journal, int type, ++int f2fs_lookup_journal_in_cursum(struct f2fs_sb_info *sbi, ++ struct f2fs_journal *journal, int type, + unsigned int val, int alloc); + void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc); + int f2fs_check_and_fix_write_pointer(struct f2fs_sb_info *sbi); +diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c +index 5f90cca64c7aa2..9d2f4f22fd3963 100644 +--- a/fs/f2fs/gc.c ++++ b/fs/f2fs/gc.c +@@ -1766,8 +1766,8 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, + + sanity_check_seg_type(sbi, get_seg_entry(sbi, segno)->type); + +- segno = rounddown(segno, SUMS_PER_BLOCK); +- sum_blk_cnt = DIV_ROUND_UP(end_segno - segno, SUMS_PER_BLOCK); ++ segno = rounddown(segno, sbi->sums_per_block); ++ sum_blk_cnt = DIV_ROUND_UP(end_segno - segno, sbi->sums_per_block); + /* readahead multi ssa blocks those have contiguous address */ + if (__is_large_section(sbi)) + f2fs_ra_meta_pages(sbi, GET_SUM_BLOCK(sbi, segno), +@@ -1777,17 +1777,17 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, + while (segno < end_segno) { + struct folio *sum_folio = f2fs_get_sum_folio(sbi, segno); + +- segno += SUMS_PER_BLOCK; ++ segno += sbi->sums_per_block; + if (IS_ERR(sum_folio)) { + int err = PTR_ERR(sum_folio); + +- end_segno = segno - SUMS_PER_BLOCK; +- segno = rounddown(start_segno, SUMS_PER_BLOCK); ++ end_segno = segno - sbi->sums_per_block; ++ segno = rounddown(start_segno, sbi->sums_per_block); + while (segno < end_segno) { + sum_folio = filemap_get_folio(META_MAPPING(sbi), + GET_SUM_BLOCK(sbi, segno)); + folio_put_refs(sum_folio, 2); +- segno += SUMS_PER_BLOCK; ++ segno += sbi->sums_per_block; + } + return err; + } +@@ -1803,8 +1803,8 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, + /* find segment summary of victim */ + struct folio *sum_folio = filemap_get_folio(META_MAPPING(sbi), + GET_SUM_BLOCK(sbi, segno)); +- unsigned int block_end_segno = rounddown(segno, SUMS_PER_BLOCK) +- + SUMS_PER_BLOCK; ++ unsigned int block_end_segno = rounddown(segno, sbi->sums_per_block) ++ + sbi->sums_per_block; + + if (block_end_segno > end_segno) + block_end_segno = end_segno; +@@ -1830,12 +1830,13 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, + migrated >= sbi->migration_granularity) + continue; + +- sum = SUM_BLK_PAGE_ADDR(sum_folio, cur_segno); +- if (type != GET_SUM_TYPE((&sum->footer))) { ++ sum = SUM_BLK_PAGE_ADDR(sbi, sum_folio, cur_segno); ++ if (type != GET_SUM_TYPE(sum_footer(sbi, sum))) { + f2fs_err(sbi, "Inconsistent segment (%u) type " + "[%d, %d] in SSA and SIT", + cur_segno, type, +- GET_SUM_TYPE((&sum->footer))); ++ GET_SUM_TYPE( ++ sum_footer(sbi, sum))); + f2fs_stop_checkpoint(sbi, false, + STOP_CP_REASON_CORRUPTED_SUMMARY); + continue; +@@ -2093,6 +2094,7 @@ int f2fs_gc_range(struct f2fs_sb_info *sbi, + if (unlikely(f2fs_cp_error(sbi))) + return -EIO; + ++ stat_inc_gc_call_count(sbi, FOREGROUND); + for (segno = start_seg; segno <= end_seg; segno += SEGS_PER_SEC(sbi)) { + struct gc_inode_list gc_list = { + .ilist = LIST_HEAD_INIT(gc_list.ilist), +diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c +index 482a362f262543..591fcdf3ba77b9 100644 +--- a/fs/f2fs/node.c ++++ b/fs/f2fs/node.c +@@ -606,7 +606,7 @@ retry: + goto retry; + } + +- i = f2fs_lookup_journal_in_cursum(journal, NAT_JOURNAL, nid, 0); ++ i = f2fs_lookup_journal_in_cursum(sbi, journal, NAT_JOURNAL, nid, 0); + if (i >= 0) { + ne = nat_in_journal(journal, i); + node_info_from_raw_nat(ni, &ne); +@@ -1500,9 +1500,9 @@ void f2fs_ra_node_page(struct f2fs_sb_info *sbi, nid_t nid) + f2fs_folio_put(afolio, err ? true : false); + } + +-static int sanity_check_node_footer(struct f2fs_sb_info *sbi, ++int f2fs_sanity_check_node_footer(struct f2fs_sb_info *sbi, + struct folio *folio, pgoff_t nid, +- enum node_type ntype) ++ enum node_type ntype, bool in_irq) + { + if (unlikely(nid != nid_of_node(folio))) + goto out_err; +@@ -1527,12 +1527,13 @@ static int sanity_check_node_footer(struct f2fs_sb_info *sbi, + goto out_err; + return 0; + out_err: +- f2fs_warn(sbi, "inconsistent node block, node_type:%d, nid:%lu, " +- "node_footer[nid:%u,ino:%u,ofs:%u,cpver:%llu,blkaddr:%u]", +- ntype, nid, nid_of_node(folio), ino_of_node(folio), +- ofs_of_node(folio), cpver_of_node(folio), +- next_blkaddr_of_node(folio)); + set_sbi_flag(sbi, SBI_NEED_FSCK); ++ f2fs_warn_ratelimited(sbi, "inconsistent node block, node_type:%d, nid:%lu, " ++ "node_footer[nid:%u,ino:%u,ofs:%u,cpver:%llu,blkaddr:%u]", ++ ntype, nid, nid_of_node(folio), ino_of_node(folio), ++ ofs_of_node(folio), cpver_of_node(folio), ++ next_blkaddr_of_node(folio)); ++ + f2fs_handle_error(sbi, ERROR_INCONSISTENT_FOOTER); + return -EFSCORRUPTED; + } +@@ -1578,7 +1579,7 @@ repeat: + goto out_err; + } + page_hit: +- err = sanity_check_node_footer(sbi, folio, nid, ntype); ++ err = f2fs_sanity_check_node_footer(sbi, folio, nid, ntype, false); + if (!err) + return folio; + out_err: +@@ -1751,7 +1752,12 @@ static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted + + /* get old block addr of this node page */ + nid = nid_of_node(folio); +- f2fs_bug_on(sbi, folio->index != nid); ++ ++ if (f2fs_sanity_check_node_footer(sbi, folio, nid, ++ NODE_TYPE_REGULAR, false)) { ++ f2fs_handle_critical_error(sbi, STOP_CP_REASON_CORRUPTED_NID); ++ goto redirty_out; ++ } + + if (f2fs_get_node_info(sbi, nid, &ni, !do_balance)) + goto redirty_out; +@@ -1774,8 +1780,13 @@ static bool __write_node_folio(struct folio *folio, bool atomic, bool *submitted + goto redirty_out; + } + +- if (atomic && !test_opt(sbi, NOBARRIER)) +- fio.op_flags |= REQ_PREFLUSH | REQ_FUA; ++ if (atomic) { ++ if (!test_opt(sbi, NOBARRIER)) ++ fio.op_flags |= REQ_PREFLUSH | REQ_FUA; ++ if (IS_INODE(folio)) ++ set_dentry_mark(folio, ++ f2fs_need_dentry_mark(sbi, ino_of_node(folio))); ++ } + + /* should add to global list before clearing PAGECACHE status */ + if (f2fs_in_warm_node_list(sbi, folio)) { +@@ -1916,8 +1927,9 @@ continue_unlock: + if (is_inode_flag_set(inode, + FI_DIRTY_INODE)) + f2fs_update_inode(inode, folio); +- set_dentry_mark(folio, +- f2fs_need_dentry_mark(sbi, ino)); ++ if (!atomic) ++ set_dentry_mark(folio, ++ f2fs_need_dentry_mark(sbi, ino)); + } + /* may be written by other thread */ + if (!folio_test_dirty(folio)) +@@ -2937,7 +2949,7 @@ int f2fs_restore_node_summary(struct f2fs_sb_info *sbi, + /* scan the node segment */ + last_offset = BLKS_PER_SEG(sbi); + addr = START_BLOCK(sbi, segno); +- sum_entry = &sum->entries[0]; ++ sum_entry = sum_entries(sum); + + for (i = 0; i < last_offset; i += nrpages, addr += nrpages) { + nrpages = bio_max_segs(last_offset - i); +@@ -3078,7 +3090,7 @@ static int __flush_nat_entry_set(struct f2fs_sb_info *sbi, + * #2, flush nat entries to nat page. + */ + if (enabled_nat_bits(sbi, cpc) || +- !__has_cursum_space(journal, set->entry_cnt, NAT_JOURNAL)) ++ !__has_cursum_space(sbi, journal, set->entry_cnt, NAT_JOURNAL)) + to_journal = false; + + if (to_journal) { +@@ -3101,7 +3113,7 @@ static int __flush_nat_entry_set(struct f2fs_sb_info *sbi, + f2fs_bug_on(sbi, nat_get_blkaddr(ne) == NEW_ADDR); + + if (to_journal) { +- offset = f2fs_lookup_journal_in_cursum(journal, ++ offset = f2fs_lookup_journal_in_cursum(sbi, journal, + NAT_JOURNAL, nid, 1); + f2fs_bug_on(sbi, offset < 0); + raw_ne = &nat_in_journal(journal, offset); +@@ -3172,7 +3184,7 @@ int f2fs_flush_nat_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) + * into nat entry set. + */ + if (enabled_nat_bits(sbi, cpc) || +- !__has_cursum_space(journal, ++ !__has_cursum_space(sbi, journal, + nm_i->nat_cnt[DIRTY_NAT], NAT_JOURNAL)) + remove_nats_in_journal(sbi); + +@@ -3183,7 +3195,7 @@ int f2fs_flush_nat_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) + set_idx = setvec[found - 1]->set + 1; + for (idx = 0; idx < found; idx++) + __adjust_nat_entry_set(setvec[idx], &sets, +- MAX_NAT_JENTRIES(journal)); ++ MAX_NAT_JENTRIES(sbi, journal)); + } + + /* flush dirty nats in nat entry set */ +diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h +index 9cb8dcf8d41760..824ac9f0e6e42a 100644 +--- a/fs/f2fs/node.h ++++ b/fs/f2fs/node.h +@@ -52,14 +52,6 @@ enum { + IS_PREALLOC, /* nat entry is preallocated */ + }; + +-/* For node type in __get_node_folio() */ +-enum node_type { +- NODE_TYPE_REGULAR, +- NODE_TYPE_INODE, +- NODE_TYPE_XATTR, +- NODE_TYPE_NON_INODE, +-}; +- + /* + * For node information + */ +diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c +index 62a0c71b5b75d6..06674e694b271e 100644 +--- a/fs/f2fs/recovery.c ++++ b/fs/f2fs/recovery.c +@@ -514,7 +514,7 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi, + struct curseg_info *curseg = CURSEG_I(sbi, i); + + if (curseg->segno == segno) { +- sum = curseg->sum_blk->entries[blkoff]; ++ sum = sum_entries(curseg->sum_blk)[blkoff]; + goto got_it; + } + } +@@ -522,8 +522,8 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi, + sum_folio = f2fs_get_sum_folio(sbi, segno); + if (IS_ERR(sum_folio)) + return PTR_ERR(sum_folio); +- sum_node = SUM_BLK_PAGE_ADDR(sum_folio, segno); +- sum = sum_node->entries[blkoff]; ++ sum_node = SUM_BLK_PAGE_ADDR(sbi, sum_folio, segno); ++ sum = sum_entries(sum_node)[blkoff]; + f2fs_folio_put(sum_folio, true); + got_it: + /* Use the locked dnode page and inode */ +diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c +index 10d873d1b328cd..23b94a8fd843de 100644 +--- a/fs/f2fs/segment.c ++++ b/fs/f2fs/segment.c +@@ -2689,12 +2689,12 @@ int f2fs_npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra) + valid_sum_count += f2fs_curseg_valid_blocks(sbi, i); + } + +- sum_in_page = (PAGE_SIZE - 2 * SUM_JOURNAL_SIZE - ++ sum_in_page = (sbi->blocksize - 2 * sbi->sum_journal_size - + SUM_FOOTER_SIZE) / SUMMARY_SIZE; + if (valid_sum_count <= sum_in_page) + return 1; + else if ((valid_sum_count - sum_in_page) <= +- (PAGE_SIZE - SUM_FOOTER_SIZE) / SUMMARY_SIZE) ++ (sbi->blocksize - SUM_FOOTER_SIZE) / SUMMARY_SIZE) + return 2; + return 3; + } +@@ -2714,7 +2714,7 @@ void f2fs_update_meta_page(struct f2fs_sb_info *sbi, + { + struct folio *folio; + +- if (SUMS_PER_BLOCK == 1) ++ if (!f2fs_sb_has_packed_ssa(sbi)) + folio = f2fs_grab_meta_folio(sbi, blk_addr); + else + folio = f2fs_get_meta_folio_retry(sbi, blk_addr); +@@ -2732,7 +2732,7 @@ static void write_sum_page(struct f2fs_sb_info *sbi, + { + struct folio *folio; + +- if (SUMS_PER_BLOCK == 1) ++ if (!f2fs_sb_has_packed_ssa(sbi)) + return f2fs_update_meta_page(sbi, (void *)sum_blk, + GET_SUM_BLOCK(sbi, segno)); + +@@ -2740,7 +2740,8 @@ static void write_sum_page(struct f2fs_sb_info *sbi, + if (IS_ERR(folio)) + return; + +- memcpy(SUM_BLK_PAGE_ADDR(folio, segno), sum_blk, sizeof(*sum_blk)); ++ memcpy(SUM_BLK_PAGE_ADDR(sbi, folio, segno), sum_blk, ++ sbi->sum_blocksize); + folio_mark_dirty(folio); + f2fs_folio_put(folio, true); + } +@@ -2759,11 +2760,11 @@ static void write_current_sum_page(struct f2fs_sb_info *sbi, + mutex_lock(&curseg->curseg_mutex); + + down_read(&curseg->journal_rwsem); +- memcpy(&dst->journal, curseg->journal, SUM_JOURNAL_SIZE); ++ memcpy(sum_journal(sbi, dst), curseg->journal, sbi->sum_journal_size); + up_read(&curseg->journal_rwsem); + +- memcpy(dst->entries, src->entries, SUM_ENTRY_SIZE); +- memcpy(&dst->footer, &src->footer, SUM_FOOTER_SIZE); ++ memcpy(sum_entries(dst), sum_entries(src), sbi->sum_entry_size); ++ memcpy(sum_footer(sbi, dst), sum_footer(sbi, src), SUM_FOOTER_SIZE); + + mutex_unlock(&curseg->curseg_mutex); + +@@ -2936,7 +2937,7 @@ static void reset_curseg(struct f2fs_sb_info *sbi, int type, int modified) + curseg->next_blkoff = 0; + curseg->next_segno = NULL_SEGNO; + +- sum_footer = &(curseg->sum_blk->footer); ++ sum_footer = sum_footer(sbi, curseg->sum_blk); + memset(sum_footer, 0, sizeof(struct summary_footer)); + + sanity_check_seg_type(sbi, seg_type); +@@ -3082,11 +3083,11 @@ static int change_curseg(struct f2fs_sb_info *sbi, int type) + sum_folio = f2fs_get_sum_folio(sbi, new_segno); + if (IS_ERR(sum_folio)) { + /* GC won't be able to use stale summary pages by cp_error */ +- memset(curseg->sum_blk, 0, SUM_ENTRY_SIZE); ++ memset(curseg->sum_blk, 0, sbi->sum_entry_size); + return PTR_ERR(sum_folio); + } +- sum_node = SUM_BLK_PAGE_ADDR(sum_folio, new_segno); +- memcpy(curseg->sum_blk, sum_node, SUM_ENTRY_SIZE); ++ sum_node = SUM_BLK_PAGE_ADDR(sbi, sum_folio, new_segno); ++ memcpy(curseg->sum_blk, sum_node, sbi->sum_entry_size); + f2fs_folio_put(sum_folio, true); + return 0; + } +@@ -3818,7 +3819,7 @@ int f2fs_allocate_data_block(struct f2fs_sb_info *sbi, struct folio *folio, + + f2fs_wait_discard_bio(sbi, *new_blkaddr); + +- curseg->sum_blk->entries[curseg->next_blkoff] = *sum; ++ sum_entries(curseg->sum_blk)[curseg->next_blkoff] = *sum; + if (curseg->alloc_type == SSR) { + curseg->next_blkoff = f2fs_find_next_ssr_block(sbi, curseg); + } else { +@@ -4187,7 +4188,7 @@ void f2fs_do_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, + } + + curseg->next_blkoff = GET_BLKOFF_FROM_SEG0(sbi, new_blkaddr); +- curseg->sum_blk->entries[curseg->next_blkoff] = *sum; ++ sum_entries(curseg->sum_blk)[curseg->next_blkoff] = *sum; + + if (!recover_curseg || recover_newaddr) { + if (!from_gc) +@@ -4307,12 +4308,12 @@ static int read_compacted_summaries(struct f2fs_sb_info *sbi) + + /* Step 1: restore nat cache */ + seg_i = CURSEG_I(sbi, CURSEG_HOT_DATA); +- memcpy(seg_i->journal, kaddr, SUM_JOURNAL_SIZE); ++ memcpy(seg_i->journal, kaddr, sbi->sum_journal_size); + + /* Step 2: restore sit cache */ + seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA); +- memcpy(seg_i->journal, kaddr + SUM_JOURNAL_SIZE, SUM_JOURNAL_SIZE); +- offset = 2 * SUM_JOURNAL_SIZE; ++ memcpy(seg_i->journal, kaddr + sbi->sum_journal_size, sbi->sum_journal_size); ++ offset = 2 * sbi->sum_journal_size; + + /* Step 3: restore summary entries */ + for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { +@@ -4334,9 +4335,9 @@ static int read_compacted_summaries(struct f2fs_sb_info *sbi) + struct f2fs_summary *s; + + s = (struct f2fs_summary *)(kaddr + offset); +- seg_i->sum_blk->entries[j] = *s; ++ sum_entries(seg_i->sum_blk)[j] = *s; + offset += SUMMARY_SIZE; +- if (offset + SUMMARY_SIZE <= PAGE_SIZE - ++ if (offset + SUMMARY_SIZE <= sbi->blocksize - + SUM_FOOTER_SIZE) + continue; + +@@ -4392,7 +4393,7 @@ static int read_normal_summaries(struct f2fs_sb_info *sbi, int type) + + if (IS_NODESEG(type)) { + if (__exist_node_summaries(sbi)) { +- struct f2fs_summary *ns = &sum->entries[0]; ++ struct f2fs_summary *ns = sum_entries(sum); + int i; + + for (i = 0; i < BLKS_PER_SEG(sbi); i++, ns++) { +@@ -4412,11 +4413,13 @@ static int read_normal_summaries(struct f2fs_sb_info *sbi, int type) + + /* update journal info */ + down_write(&curseg->journal_rwsem); +- memcpy(curseg->journal, &sum->journal, SUM_JOURNAL_SIZE); ++ memcpy(curseg->journal, sum_journal(sbi, sum), sbi->sum_journal_size); + up_write(&curseg->journal_rwsem); + +- memcpy(curseg->sum_blk->entries, sum->entries, SUM_ENTRY_SIZE); +- memcpy(&curseg->sum_blk->footer, &sum->footer, SUM_FOOTER_SIZE); ++ memcpy(sum_entries(curseg->sum_blk), sum_entries(sum), ++ sbi->sum_entry_size); ++ memcpy(sum_footer(sbi, curseg->sum_blk), sum_footer(sbi, sum), ++ SUM_FOOTER_SIZE); + curseg->next_segno = segno; + reset_curseg(sbi, type, 0); + curseg->alloc_type = ckpt->alloc_type[type]; +@@ -4460,8 +4463,8 @@ static int restore_curseg_summaries(struct f2fs_sb_info *sbi) + } + + /* sanity check for summary blocks */ +- if (nats_in_cursum(nat_j) > NAT_JOURNAL_ENTRIES || +- sits_in_cursum(sit_j) > SIT_JOURNAL_ENTRIES) { ++ if (nats_in_cursum(nat_j) > sbi->nat_journal_entries || ++ sits_in_cursum(sit_j) > sbi->sit_journal_entries) { + f2fs_err(sbi, "invalid journal entries nats %u sits %u", + nats_in_cursum(nat_j), sits_in_cursum(sit_j)); + return -EINVAL; +@@ -4485,13 +4488,13 @@ static void write_compacted_summaries(struct f2fs_sb_info *sbi, block_t blkaddr) + + /* Step 1: write nat cache */ + seg_i = CURSEG_I(sbi, CURSEG_HOT_DATA); +- memcpy(kaddr, seg_i->journal, SUM_JOURNAL_SIZE); +- written_size += SUM_JOURNAL_SIZE; ++ memcpy(kaddr, seg_i->journal, sbi->sum_journal_size); ++ written_size += sbi->sum_journal_size; + + /* Step 2: write sit cache */ + seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA); +- memcpy(kaddr + written_size, seg_i->journal, SUM_JOURNAL_SIZE); +- written_size += SUM_JOURNAL_SIZE; ++ memcpy(kaddr + written_size, seg_i->journal, sbi->sum_journal_size); ++ written_size += sbi->sum_journal_size; + + /* Step 3: write summary entries */ + for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { +@@ -4504,10 +4507,10 @@ static void write_compacted_summaries(struct f2fs_sb_info *sbi, block_t blkaddr) + written_size = 0; + } + summary = (struct f2fs_summary *)(kaddr + written_size); +- *summary = seg_i->sum_blk->entries[j]; ++ *summary = sum_entries(seg_i->sum_blk)[j]; + written_size += SUMMARY_SIZE; + +- if (written_size + SUMMARY_SIZE <= PAGE_SIZE - ++ if (written_size + SUMMARY_SIZE <= sbi->blocksize - + SUM_FOOTER_SIZE) + continue; + +@@ -4549,8 +4552,9 @@ void f2fs_write_node_summaries(struct f2fs_sb_info *sbi, block_t start_blk) + write_normal_summaries(sbi, start_blk, CURSEG_HOT_NODE); + } + +-int f2fs_lookup_journal_in_cursum(struct f2fs_journal *journal, int type, +- unsigned int val, int alloc) ++int f2fs_lookup_journal_in_cursum(struct f2fs_sb_info *sbi, ++ struct f2fs_journal *journal, int type, ++ unsigned int val, int alloc) + { + int i; + +@@ -4559,13 +4563,13 @@ int f2fs_lookup_journal_in_cursum(struct f2fs_journal *journal, int type, + if (le32_to_cpu(nid_in_journal(journal, i)) == val) + return i; + } +- if (alloc && __has_cursum_space(journal, 1, NAT_JOURNAL)) ++ if (alloc && __has_cursum_space(sbi, journal, 1, NAT_JOURNAL)) + return update_nats_in_cursum(journal, 1); + } else if (type == SIT_JOURNAL) { + for (i = 0; i < sits_in_cursum(journal); i++) + if (le32_to_cpu(segno_in_journal(journal, i)) == val) + return i; +- if (alloc && __has_cursum_space(journal, 1, SIT_JOURNAL)) ++ if (alloc && __has_cursum_space(sbi, journal, 1, SIT_JOURNAL)) + return update_sits_in_cursum(journal, 1); + } + return -1; +@@ -4713,8 +4717,8 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) + * entries, remove all entries from journal and add and account + * them in sit entry set. + */ +- if (!__has_cursum_space(journal, sit_i->dirty_sentries, SIT_JOURNAL) || +- !to_journal) ++ if (!__has_cursum_space(sbi, journal, ++ sit_i->dirty_sentries, SIT_JOURNAL) || !to_journal) + remove_sits_in_journal(sbi); + + /* +@@ -4731,7 +4735,8 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) + unsigned int segno = start_segno; + + if (to_journal && +- !__has_cursum_space(journal, ses->entry_cnt, SIT_JOURNAL)) ++ !__has_cursum_space(sbi, journal, ses->entry_cnt, ++ SIT_JOURNAL)) + to_journal = false; + + if (to_journal) { +@@ -4759,7 +4764,7 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) + } + + if (to_journal) { +- offset = f2fs_lookup_journal_in_cursum(journal, ++ offset = f2fs_lookup_journal_in_cursum(sbi, journal, + SIT_JOURNAL, segno, 1); + f2fs_bug_on(sbi, offset < 0); + segno_in_journal(journal, offset) = +@@ -4966,12 +4971,13 @@ static int build_curseg(struct f2fs_sb_info *sbi) + + for (i = 0; i < NO_CHECK_TYPE; i++) { + mutex_init(&array[i].curseg_mutex); +- array[i].sum_blk = f2fs_kzalloc(sbi, PAGE_SIZE, GFP_KERNEL); ++ array[i].sum_blk = f2fs_kzalloc(sbi, sbi->sum_blocksize, ++ GFP_KERNEL); + if (!array[i].sum_blk) + return -ENOMEM; + init_rwsem(&array[i].journal_rwsem); + array[i].journal = f2fs_kzalloc(sbi, +- sizeof(struct f2fs_journal), GFP_KERNEL); ++ sbi->sum_journal_size, GFP_KERNEL); + if (!array[i].journal) + return -ENOMEM; + array[i].seg_type = log_type_to_seg_type(i); +diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h +index f3e2fff45cf532..d05d133c89aff8 100644 +--- a/fs/f2fs/segment.h ++++ b/fs/f2fs/segment.h +@@ -85,12 +85,11 @@ static inline void sanity_check_seg_type(struct f2fs_sb_info *sbi, + #define GET_ZONE_FROM_SEG(sbi, segno) \ + GET_ZONE_FROM_SEC(sbi, GET_SEC_FROM_SEG(sbi, segno)) + +-#define SUMS_PER_BLOCK (F2FS_BLKSIZE / F2FS_SUM_BLKSIZE) + #define GET_SUM_BLOCK(sbi, segno) \ +- (SM_I(sbi)->ssa_blkaddr + (segno / SUMS_PER_BLOCK)) +-#define GET_SUM_BLKOFF(segno) (segno % SUMS_PER_BLOCK) +-#define SUM_BLK_PAGE_ADDR(folio, segno) \ +- (folio_address(folio) + GET_SUM_BLKOFF(segno) * F2FS_SUM_BLKSIZE) ++ (SM_I(sbi)->ssa_blkaddr + (segno / (sbi)->sums_per_block)) ++#define GET_SUM_BLKOFF(sbi, segno) (segno % (sbi)->sums_per_block) ++#define SUM_BLK_PAGE_ADDR(sbi, folio, segno) \ ++ (folio_address(folio) + GET_SUM_BLKOFF(sbi, segno) * (sbi)->sum_blocksize) + + #define GET_SUM_TYPE(footer) ((footer)->entry_type) + #define SET_SUM_TYPE(footer, type) ((footer)->entry_type = (type)) +diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c +index eb466a11d9d7f6..907f632193abf0 100644 +--- a/fs/f2fs/super.c ++++ b/fs/f2fs/super.c +@@ -4059,20 +4059,6 @@ static int sanity_check_raw_super(struct f2fs_sb_info *sbi, + if (sanity_check_area_boundary(sbi, folio, index)) + return -EFSCORRUPTED; + +- /* +- * Check for legacy summary layout on 16KB+ block devices. +- * Modern f2fs-tools packs multiple 4KB summary areas into one block, +- * whereas legacy versions used one block per summary, leading +- * to a much larger SSA. +- */ +- if (SUMS_PER_BLOCK > 1 && +- !(__F2FS_HAS_FEATURE(raw_super, F2FS_FEATURE_PACKED_SSA))) { +- f2fs_info(sbi, "Error: Device formatted with a legacy version. " +- "Please reformat with a tool supporting the packed ssa " +- "feature for block sizes larger than 4kb."); +- return -EOPNOTSUPP; +- } +- + return 0; + } + +@@ -4283,6 +4269,18 @@ static void init_sb_info(struct f2fs_sb_info *sbi) + spin_lock_init(&sbi->gc_remaining_trials_lock); + atomic64_set(&sbi->current_atomic_write, 0); + ++ sbi->sum_blocksize = f2fs_sb_has_packed_ssa(sbi) ? ++ 4096 : sbi->blocksize; ++ sbi->sums_per_block = sbi->blocksize / sbi->sum_blocksize; ++ sbi->entries_in_sum = sbi->sum_blocksize / 8; ++ sbi->sum_entry_size = SUMMARY_SIZE * sbi->entries_in_sum; ++ sbi->sum_journal_size = sbi->sum_blocksize - SUM_FOOTER_SIZE - ++ sbi->sum_entry_size; ++ sbi->nat_journal_entries = (sbi->sum_journal_size - 2) / ++ sizeof(struct nat_journal_entry); ++ sbi->sit_journal_entries = (sbi->sum_journal_size - 2) / ++ sizeof(struct sit_journal_entry); ++ + sbi->dir_level = DEF_DIR_LEVEL; + sbi->interval_time[CP_TIME] = DEF_CP_INTERVAL; + sbi->interval_time[REQ_TIME] = DEF_IDLE_INTERVAL; +diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c +index 5685b454bfd12d..c6fa8f74b91c2c 100644 +--- a/fs/f2fs/sysfs.c ++++ b/fs/f2fs/sysfs.c +@@ -58,6 +58,7 @@ struct f2fs_attr { + const char *buf, size_t len); + int struct_type; + int offset; ++ int size; + int id; + }; + +@@ -344,11 +345,30 @@ static ssize_t main_blkaddr_show(struct f2fs_attr *a, + (unsigned long long)MAIN_BLKADDR(sbi)); + } + ++static ssize_t __sbi_show_value(struct f2fs_attr *a, ++ struct f2fs_sb_info *sbi, char *buf, ++ unsigned char *value) ++{ ++ switch (a->size) { ++ case 1: ++ return sysfs_emit(buf, "%u\n", *(u8 *)value); ++ case 2: ++ return sysfs_emit(buf, "%u\n", *(u16 *)value); ++ case 4: ++ return sysfs_emit(buf, "%u\n", *(u32 *)value); ++ case 8: ++ return sysfs_emit(buf, "%llu\n", *(u64 *)value); ++ default: ++ f2fs_bug_on(sbi, 1); ++ return sysfs_emit(buf, ++ "show sysfs node value with wrong type\n"); ++ } ++} ++ + static ssize_t f2fs_sbi_show(struct f2fs_attr *a, + struct f2fs_sb_info *sbi, char *buf) + { + unsigned char *ptr = NULL; +- unsigned int *ui; + + ptr = __struct_ptr(sbi, a->struct_type); + if (!ptr) +@@ -428,9 +448,30 @@ static ssize_t f2fs_sbi_show(struct f2fs_attr *a, + atomic_read(&sbi->cp_call_count[BACKGROUND])); + #endif + +- ui = (unsigned int *)(ptr + a->offset); ++ return __sbi_show_value(a, sbi, buf, ptr + a->offset); ++} + +- return sysfs_emit(buf, "%u\n", *ui); ++static void __sbi_store_value(struct f2fs_attr *a, ++ struct f2fs_sb_info *sbi, ++ unsigned char *ui, unsigned long value) ++{ ++ switch (a->size) { ++ case 1: ++ *(u8 *)ui = value; ++ break; ++ case 2: ++ *(u16 *)ui = value; ++ break; ++ case 4: ++ *(u32 *)ui = value; ++ break; ++ case 8: ++ *(u64 *)ui = value; ++ break; ++ default: ++ f2fs_bug_on(sbi, 1); ++ f2fs_err(sbi, "store sysfs node value with wrong type"); ++ } + } + + static ssize_t __sbi_store(struct f2fs_attr *a, +@@ -749,7 +790,7 @@ out: + return count; + } + +- if (!strcmp(a->attr.name, "gc_pin_file_threshold")) { ++ if (!strcmp(a->attr.name, "gc_pin_file_thresh")) { + if (t > MAX_GC_FAILED_PINNED_FILES) + return -EINVAL; + sbi->gc_pin_file_threshold = t; +@@ -906,7 +947,7 @@ out: + return count; + } + +- *ui = (unsigned int)t; ++ __sbi_store_value(a, sbi, ptr + a->offset, t); + + return count; + } +@@ -1053,24 +1094,27 @@ static struct f2fs_attr f2fs_attr_sb_##_name = { \ + .id = F2FS_FEATURE_##_feat, \ + } + +-#define F2FS_ATTR_OFFSET(_struct_type, _name, _mode, _show, _store, _offset) \ ++#define F2FS_ATTR_OFFSET(_struct_type, _name, _mode, _show, _store, _offset, _size) \ + static struct f2fs_attr f2fs_attr_##_name = { \ + .attr = {.name = __stringify(_name), .mode = _mode }, \ + .show = _show, \ + .store = _store, \ + .struct_type = _struct_type, \ +- .offset = _offset \ ++ .offset = _offset, \ ++ .size = _size \ + } + + #define F2FS_RO_ATTR(struct_type, struct_name, name, elname) \ + F2FS_ATTR_OFFSET(struct_type, name, 0444, \ + f2fs_sbi_show, NULL, \ +- offsetof(struct struct_name, elname)) ++ offsetof(struct struct_name, elname), \ ++ sizeof_field(struct struct_name, elname)) + + #define F2FS_RW_ATTR(struct_type, struct_name, name, elname) \ + F2FS_ATTR_OFFSET(struct_type, name, 0644, \ + f2fs_sbi_show, f2fs_sbi_store, \ +- offsetof(struct struct_name, elname)) ++ offsetof(struct struct_name, elname), \ ++ sizeof_field(struct struct_name, elname)) + + #define F2FS_GENERAL_RO_ATTR(name) \ + static struct f2fs_attr f2fs_attr_##name = __ATTR(name, 0444, name##_show, NULL) +diff --git a/fs/romfs/super.c b/fs/romfs/super.c +index 0addcc849ff2ca..e83f9b78d7a16e 100644 +--- a/fs/romfs/super.c ++++ b/fs/romfs/super.c +@@ -458,7 +458,10 @@ static int romfs_fill_super(struct super_block *sb, struct fs_context *fc) + + #ifdef CONFIG_BLOCK + if (!sb->s_mtd) { +- sb_set_blocksize(sb, ROMBSIZE); ++ if (!sb_set_blocksize(sb, ROMBSIZE)) { ++ errorf(fc, "romfs: unable to set blocksize\n"); ++ return -EINVAL; ++ } + } else { + sb->s_blocksize = ROMBSIZE; + sb->s_blocksize_bits = blksize_bits(ROMBSIZE); +diff --git a/include/asm-generic/tlb.h b/include/asm-generic/tlb.h +index 1fff717cae510d..4d679d2a206b46 100644 +--- a/include/asm-generic/tlb.h ++++ b/include/asm-generic/tlb.h +@@ -46,7 +46,8 @@ + * + * The mmu_gather API consists of: + * +- * - tlb_gather_mmu() / tlb_gather_mmu_fullmm() / tlb_finish_mmu() ++ * - tlb_gather_mmu() / tlb_gather_mmu_fullmm() / tlb_gather_mmu_vma() / ++ * tlb_finish_mmu() + * + * start and finish a mmu_gather + * +@@ -364,6 +365,20 @@ struct mmu_gather { + unsigned int vma_huge : 1; + unsigned int vma_pfn : 1; + ++ /* ++ * Did we unshare (unmap) any shared page tables? For now only ++ * used for hugetlb PMD table sharing. ++ */ ++ unsigned int unshared_tables : 1; ++ ++ /* ++ * Did we unshare any page tables such that they are now exclusive ++ * and could get reused+modified by the new owner? When setting this ++ * flag, "unshared_tables" will be set as well. For now only used ++ * for hugetlb PMD table sharing. ++ */ ++ unsigned int fully_unshared_tables : 1; ++ + unsigned int batch_count; + + #ifndef CONFIG_MMU_GATHER_NO_GATHER +@@ -400,6 +415,7 @@ static inline void __tlb_reset_range(struct mmu_gather *tlb) + tlb->cleared_pmds = 0; + tlb->cleared_puds = 0; + tlb->cleared_p4ds = 0; ++ tlb->unshared_tables = 0; + /* + * Do not reset mmu_gather::vma_* fields here, we do not + * call into tlb_start_vma() again to set them if there is an +@@ -484,7 +500,7 @@ static inline void tlb_flush_mmu_tlbonly(struct mmu_gather *tlb) + * these bits. + */ + if (!(tlb->freed_tables || tlb->cleared_ptes || tlb->cleared_pmds || +- tlb->cleared_puds || tlb->cleared_p4ds)) ++ tlb->cleared_puds || tlb->cleared_p4ds || tlb->unshared_tables)) + return; + + tlb_flush(tlb); +@@ -773,6 +789,63 @@ static inline bool huge_pmd_needs_flush(pmd_t oldpmd, pmd_t newpmd) + } + #endif + ++#ifdef CONFIG_HUGETLB_PMD_PAGE_TABLE_SHARING ++static inline void tlb_unshare_pmd_ptdesc(struct mmu_gather *tlb, struct ptdesc *pt, ++ unsigned long addr) ++{ ++ /* ++ * The caller must make sure that concurrent unsharing + exclusive ++ * reuse is impossible until tlb_flush_unshared_tables() was called. ++ */ ++ VM_WARN_ON_ONCE(!ptdesc_pmd_is_shared(pt)); ++ ptdesc_pmd_pts_dec(pt); ++ ++ /* Clearing a PUD pointing at a PMD table with PMD leaves. */ ++ tlb_flush_pmd_range(tlb, addr & PUD_MASK, PUD_SIZE); ++ ++ /* ++ * If the page table is now exclusively owned, we fully unshared ++ * a page table. ++ */ ++ if (!ptdesc_pmd_is_shared(pt)) ++ tlb->fully_unshared_tables = true; ++ tlb->unshared_tables = true; ++} ++ ++static inline void tlb_flush_unshared_tables(struct mmu_gather *tlb) ++{ ++ /* ++ * As soon as the caller drops locks to allow for reuse of ++ * previously-shared tables, these tables could get modified and ++ * even reused outside of hugetlb context, so we have to make sure that ++ * any page table walkers (incl. TLB, GUP-fast) are aware of that ++ * change. ++ * ++ * Even if we are not fully unsharing a PMD table, we must ++ * flush the TLB for the unsharer now. ++ */ ++ if (tlb->unshared_tables) ++ tlb_flush_mmu_tlbonly(tlb); ++ ++ /* ++ * Similarly, we must make sure that concurrent GUP-fast will not ++ * walk previously-shared page tables that are getting modified+reused ++ * elsewhere. So broadcast an IPI to wait for any concurrent GUP-fast. ++ * ++ * We only perform this when we are the last sharer of a page table, ++ * as the IPI will reach all CPUs: any GUP-fast. ++ * ++ * Note that on configs where tlb_remove_table_sync_one() is a NOP, ++ * the expectation is that the tlb_flush_mmu_tlbonly() would have issued ++ * required IPIs already for us. ++ */ ++ if (tlb->fully_unshared_tables) { ++ tlb_remove_table_sync_one(); ++ tlb->fully_unshared_tables = false; ++ } ++} ++#endif /* CONFIG_HUGETLB_PMD_PAGE_TABLE_SHARING */ ++ + #endif /* CONFIG_MMU */ + + #endif /* _ASM_GENERIC__TLB_H */ +diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h +index a7880787cad366..dc41722fcc9dde 100644 +--- a/include/linux/f2fs_fs.h ++++ b/include/linux/f2fs_fs.h +@@ -17,7 +17,6 @@ + #define F2FS_LOG_SECTORS_PER_BLOCK (PAGE_SHIFT - 9) /* log number for sector/blk */ + #define F2FS_BLKSIZE PAGE_SIZE /* support only block == page */ + #define F2FS_BLKSIZE_BITS PAGE_SHIFT /* bits for F2FS_BLKSIZE */ +-#define F2FS_SUM_BLKSIZE 4096 /* only support 4096 byte sum block */ + #define F2FS_MAX_EXTENSION 64 /* # of extension entries */ + #define F2FS_EXTENSION_LEN 8 /* max size of extension */ + +@@ -442,10 +441,8 @@ struct f2fs_sit_block { + * from node's page's beginning to get a data block address. + * ex) data_blkaddr = (block_t)(nodepage_start_address + ofs_in_node) + */ +-#define ENTRIES_IN_SUM (F2FS_SUM_BLKSIZE / 8) + #define SUMMARY_SIZE (7) /* sizeof(struct f2fs_summary) */ + #define SUM_FOOTER_SIZE (5) /* sizeof(struct summary_footer) */ +-#define SUM_ENTRY_SIZE (SUMMARY_SIZE * ENTRIES_IN_SUM) + + /* a summary entry for a block in a segment */ + struct f2fs_summary { +@@ -468,22 +465,6 @@ struct summary_footer { + __le32 check_sum; /* summary checksum */ + } __packed; + +-#define SUM_JOURNAL_SIZE (F2FS_SUM_BLKSIZE - SUM_FOOTER_SIZE -\ +- SUM_ENTRY_SIZE) +-#define NAT_JOURNAL_ENTRIES ((SUM_JOURNAL_SIZE - 2) /\ +- sizeof(struct nat_journal_entry)) +-#define NAT_JOURNAL_RESERVED ((SUM_JOURNAL_SIZE - 2) %\ +- sizeof(struct nat_journal_entry)) +-#define SIT_JOURNAL_ENTRIES ((SUM_JOURNAL_SIZE - 2) /\ +- sizeof(struct sit_journal_entry)) +-#define SIT_JOURNAL_RESERVED ((SUM_JOURNAL_SIZE - 2) %\ +- sizeof(struct sit_journal_entry)) +- +-/* Reserved area should make size of f2fs_extra_info equals to +- * that of nat_journal and sit_journal. +- */ +-#define EXTRA_INFO_RESERVED (SUM_JOURNAL_SIZE - 2 - 8) +- + /* + * frequently updated NAT/SIT entries can be stored in the spare area in + * summary blocks +@@ -498,9 +479,16 @@ struct nat_journal_entry { + struct f2fs_nat_entry ne; + } __packed; + ++/* ++ * The nat_journal structure is a placeholder whose actual size varies depending ++ * on the use of packed_ssa. Therefore, it must always be accessed only through ++ * specific sets of macros and fields, and size calculations should use ++ * size-related macros instead of sizeof(). ++ * Relevant macros: sbi->nat_journal_entries, nat_in_journal(), ++ * nid_in_journal(), MAX_NAT_JENTRIES(). ++ */ + struct nat_journal { +- struct nat_journal_entry entries[NAT_JOURNAL_ENTRIES]; +- __u8 reserved[NAT_JOURNAL_RESERVED]; ++ struct nat_journal_entry entries[0]; + } __packed; + + struct sit_journal_entry { +@@ -508,14 +496,21 @@ struct sit_journal_entry { + struct f2fs_sit_entry se; + } __packed; + ++/* ++ * The sit_journal structure is a placeholder whose actual size varies depending ++ * on the use of packed_ssa. Therefore, it must always be accessed only through ++ * specific sets of macros and fields, and size calculations should use ++ * size-related macros instead of sizeof(). ++ * Relevant macros: sbi->sit_journal_entries, sit_in_journal(), ++ * segno_in_journal(), MAX_SIT_JENTRIES(). ++ */ + struct sit_journal { +- struct sit_journal_entry entries[SIT_JOURNAL_ENTRIES]; +- __u8 reserved[SIT_JOURNAL_RESERVED]; ++ struct sit_journal_entry entries[0]; + } __packed; + + struct f2fs_extra_info { + __le64 kbytes_written; +- __u8 reserved[EXTRA_INFO_RESERVED]; ++ __u8 reserved[]; + } __packed; + + struct f2fs_journal { +@@ -531,11 +526,33 @@ struct f2fs_journal { + }; + } __packed; + +-/* Block-sized summary block structure */ ++/* ++ * Block-sized summary block structure ++ * ++ * The f2fs_summary_block structure is a placeholder whose actual size varies ++ * depending on the use of packed_ssa. Therefore, it must always be accessed ++ * only through specific sets of macros and fields, and size calculations should ++ * use size-related macros instead of sizeof(). ++ * Relevant macros: sbi->sum_blocksize, sbi->entries_in_sum, ++ * sbi->sum_entry_size, sum_entries(), sum_journal(), sum_footer(). ++ * ++ * Summary Block Layout ++ * ++ * +-----------------------+ <--- Block Start ++ * | struct f2fs_summary | ++ * | entries[0] | ++ * | ... | ++ * | entries[N-1] | ++ * +-----------------------+ ++ * | struct f2fs_journal | ++ * +-----------------------+ ++ * | struct summary_footer | ++ * +-----------------------+ <--- Block End ++ */ + struct f2fs_summary_block { +- struct f2fs_summary entries[ENTRIES_IN_SUM]; +- struct f2fs_journal journal; +- struct summary_footer footer; ++ struct f2fs_summary entries[0]; ++ // struct f2fs_journal journal; ++ // struct summary_footer footer; + } __packed; + + /* +diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h +index 89054f714992fb..6fc7934eafa116 100644 +--- a/include/linux/hugetlb.h ++++ b/include/linux/hugetlb.h +@@ -241,8 +241,9 @@ pte_t *huge_pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma, + pte_t *huge_pte_offset(struct mm_struct *mm, + unsigned long addr, unsigned long sz); + unsigned long hugetlb_mask_last_page(struct hstate *h); +-int huge_pmd_unshare(struct mm_struct *mm, struct vm_area_struct *vma, +- unsigned long addr, pte_t *ptep); ++int huge_pmd_unshare(struct mmu_gather *tlb, struct vm_area_struct *vma, ++ unsigned long addr, pte_t *ptep); ++void huge_pmd_unshare_flush(struct mmu_gather *tlb, struct vm_area_struct *vma); + void adjust_range_if_pmd_sharing_possible(struct vm_area_struct *vma, + unsigned long *start, unsigned long *end); + +@@ -302,13 +303,17 @@ static inline struct address_space *hugetlb_folio_mapping_lock_write( + return NULL; + } + +-static inline int huge_pmd_unshare(struct mm_struct *mm, +- struct vm_area_struct *vma, +- unsigned long addr, pte_t *ptep) ++static inline int huge_pmd_unshare(struct mmu_gather *tlb, ++ struct vm_area_struct *vma, unsigned long addr, pte_t *ptep) + { + return 0; + } + ++static inline void huge_pmd_unshare_flush(struct mmu_gather *tlb, ++ struct vm_area_struct *vma) ++{ ++} ++ + static inline void adjust_range_if_pmd_sharing_possible( + struct vm_area_struct *vma, + unsigned long *start, unsigned long *end) +diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h +index 90e5790c318f02..8b1045c51e0a68 100644 +--- a/include/linux/mm_types.h ++++ b/include/linux/mm_types.h +@@ -1490,6 +1490,7 @@ static inline void mm_set_cpus_allowed(struct mm_struct *mm, const struct cpumas + struct mmu_gather; + extern void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm); + extern void tlb_gather_mmu_fullmm(struct mmu_gather *tlb, struct mm_struct *mm); ++void tlb_gather_mmu_vma(struct mmu_gather *tlb, struct vm_area_struct *vma); + extern void tlb_finish_mmu(struct mmu_gather *tlb); + + struct vm_fault; +diff --git a/include/trace/events/dma.h b/include/trace/events/dma.h +index b3fef140ae155b..33e99e792f1aa2 100644 +--- a/include/trace/events/dma.h ++++ b/include/trace/events/dma.h +@@ -275,6 +275,8 @@ TRACE_EVENT(dma_free_sgt, + sizeof(u64), sizeof(u64))) + ); + ++#define DMA_TRACE_MAX_ENTRIES 128 ++ + TRACE_EVENT(dma_map_sg, + TP_PROTO(struct device *dev, struct scatterlist *sgl, int nents, + int ents, enum dma_data_direction dir, unsigned long attrs), +@@ -282,9 +284,12 @@ TRACE_EVENT(dma_map_sg, + + TP_STRUCT__entry( + __string(device, dev_name(dev)) +- __dynamic_array(u64, phys_addrs, nents) +- __dynamic_array(u64, dma_addrs, ents) +- __dynamic_array(unsigned int, lengths, ents) ++ __field(int, full_nents) ++ __field(int, full_ents) ++ __field(bool, truncated) ++ __dynamic_array(u64, phys_addrs, min(nents, DMA_TRACE_MAX_ENTRIES)) ++ __dynamic_array(u64, dma_addrs, min(ents, DMA_TRACE_MAX_ENTRIES)) ++ __dynamic_array(unsigned int, lengths, min(ents, DMA_TRACE_MAX_ENTRIES)) + __field(enum dma_data_direction, dir) + __field(unsigned long, attrs) + ), +@@ -292,11 +297,16 @@ TRACE_EVENT(dma_map_sg, + TP_fast_assign( + struct scatterlist *sg; + int i; ++ int traced_nents = min_t(int, nents, DMA_TRACE_MAX_ENTRIES); ++ int traced_ents = min_t(int, ents, DMA_TRACE_MAX_ENTRIES); + + __assign_str(device); +- for_each_sg(sgl, sg, nents, i) ++ __entry->full_nents = nents; ++ __entry->full_ents = ents; ++ __entry->truncated = (nents > DMA_TRACE_MAX_ENTRIES) || (ents > DMA_TRACE_MAX_ENTRIES); ++ for_each_sg(sgl, sg, traced_nents, i) + ((u64 *)__get_dynamic_array(phys_addrs))[i] = sg_phys(sg); +- for_each_sg(sgl, sg, ents, i) { ++ for_each_sg(sgl, sg, traced_ents, i) { + ((u64 *)__get_dynamic_array(dma_addrs))[i] = + sg_dma_address(sg); + ((unsigned int *)__get_dynamic_array(lengths))[i] = +@@ -306,9 +316,12 @@ TRACE_EVENT(dma_map_sg, + __entry->attrs = attrs; + ), + +- TP_printk("%s dir=%s dma_addrs=%s sizes=%s phys_addrs=%s attrs=%s", ++ TP_printk("%s dir=%s nents=%d/%d ents=%d/%d%s dma_addrs=%s sizes=%s phys_addrs=%s attrs=%s", + __get_str(device), + decode_dma_data_direction(__entry->dir), ++ min_t(int, __entry->full_nents, DMA_TRACE_MAX_ENTRIES), __entry->full_nents, ++ min_t(int, __entry->full_ents, DMA_TRACE_MAX_ENTRIES), __entry->full_ents, ++ __entry->truncated ? " [TRUNCATED]" : "", + __print_array(__get_dynamic_array(dma_addrs), + __get_dynamic_array_len(dma_addrs) / + sizeof(u64), sizeof(u64)), +diff --git a/io_uring/fdinfo.c b/io_uring/fdinfo.c +index 294c75a8a3bdbc..3585ad8308504a 100644 +--- a/io_uring/fdinfo.c ++++ b/io_uring/fdinfo.c +@@ -65,7 +65,7 @@ static void __io_uring_show_fdinfo(struct io_ring_ctx *ctx, struct seq_file *m) + unsigned int cq_head = READ_ONCE(r->cq.head); + unsigned int cq_tail = READ_ONCE(r->cq.tail); + unsigned int sq_shift = 0; +- unsigned int sq_entries; ++ unsigned int cq_entries, sq_entries; + int sq_pid = -1, sq_cpu = -1; + u64 sq_total_time = 0, sq_work_time = 0; + unsigned int i; +@@ -119,9 +119,11 @@ static void __io_uring_show_fdinfo(struct io_ring_ctx *ctx, struct seq_file *m) + } + } + seq_printf(m, "\n"); ++ cond_resched(); + } + seq_printf(m, "CQEs:\t%u\n", cq_tail - cq_head); +- while (cq_head < cq_tail) { ++ cq_entries = min(cq_tail - cq_head, ctx->cq_entries); ++ for (i = 0; i < cq_entries; i++) { + struct io_uring_cqe *cqe; + bool cqe32 = false; + +@@ -136,8 +138,11 @@ static void __io_uring_show_fdinfo(struct io_ring_ctx *ctx, struct seq_file *m) + cqe->big_cqe[0], cqe->big_cqe[1]); + seq_printf(m, "\n"); + cq_head++; +- if (cqe32) ++ if (cqe32) { + cq_head++; ++ i++; ++ } ++ cond_resched(); + } + + if (ctx->flags & IORING_SETUP_SQPOLL) { +diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c +index 61b56b6ca66a76..1245418cc8b3bd 100644 +--- a/kernel/cgroup/cpuset.c ++++ b/kernel/cgroup/cpuset.c +@@ -385,7 +385,7 @@ static inline bool partition_is_populated(struct cpuset *cs, + cs->attach_in_progress) + return true; + if (!excluded_child && !cs->nr_subparts) +- return cgroup_is_populated(cs->css.cgroup); ++ return cpuset_is_populated(cs); + + rcu_read_lock(); + cpuset_for_each_descendant_pre(cp, pos_css, cs) { +diff --git a/mm/hugetlb.c b/mm/hugetlb.c +index 6a60af4798bee0..be0f935a8b1212 100644 +--- a/mm/hugetlb.c ++++ b/mm/hugetlb.c +@@ -5797,7 +5797,7 @@ int move_hugetlb_page_tables(struct vm_area_struct *vma, + unsigned long last_addr_mask; + pte_t *src_pte, *dst_pte; + struct mmu_notifier_range range; +- bool shared_pmd = false; ++ struct mmu_gather tlb; + + mmu_notifier_range_init(&range, MMU_NOTIFY_CLEAR, 0, mm, old_addr, + old_end); +@@ -5807,6 +5807,7 @@ int move_hugetlb_page_tables(struct vm_area_struct *vma, + * range. + */ + flush_cache_range(vma, range.start, range.end); ++ tlb_gather_mmu_vma(&tlb, vma); + + mmu_notifier_invalidate_range_start(&range); + last_addr_mask = hugetlb_mask_last_page(h); +@@ -5823,8 +5824,7 @@ int move_hugetlb_page_tables(struct vm_area_struct *vma, + if (huge_pte_none(huge_ptep_get(mm, old_addr, src_pte))) + continue; + +- if (huge_pmd_unshare(mm, vma, old_addr, src_pte)) { +- shared_pmd = true; ++ if (huge_pmd_unshare(&tlb, vma, old_addr, src_pte)) { + old_addr |= last_addr_mask; + new_addr |= last_addr_mask; + continue; +@@ -5835,15 +5835,16 @@ int move_hugetlb_page_tables(struct vm_area_struct *vma, + break; + + move_huge_pte(vma, old_addr, new_addr, src_pte, dst_pte, sz); ++ tlb_remove_huge_tlb_entry(h, &tlb, src_pte, old_addr); + } + +- if (shared_pmd) +- flush_hugetlb_tlb_range(vma, range.start, range.end); +- else +- flush_hugetlb_tlb_range(vma, old_end - len, old_end); ++ tlb_flush_mmu_tlbonly(&tlb); ++ huge_pmd_unshare_flush(&tlb, vma); ++ + mmu_notifier_invalidate_range_end(&range); + i_mmap_unlock_write(mapping); + hugetlb_vma_unlock_write(vma); ++ tlb_finish_mmu(&tlb); + + return len + old_addr - old_end; + } +@@ -5862,7 +5863,6 @@ void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma, + unsigned long sz = huge_page_size(h); + bool adjust_reservation; + unsigned long last_addr_mask; +- bool force_flush = false; + + WARN_ON(!is_vm_hugetlb_page(vma)); + BUG_ON(start & ~huge_page_mask(h)); +@@ -5885,10 +5885,8 @@ void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma, + } + + ptl = huge_pte_lock(h, mm, ptep); +- if (huge_pmd_unshare(mm, vma, address, ptep)) { ++ if (huge_pmd_unshare(tlb, vma, address, ptep)) { + spin_unlock(ptl); +- tlb_flush_pmd_range(tlb, address & PUD_MASK, PUD_SIZE); +- force_flush = true; + address |= last_addr_mask; + continue; + } +@@ -6004,14 +6002,7 @@ void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma, + } + tlb_end_vma(tlb, vma); + +- /* +- * There is nothing protecting a previously-shared page table that we +- * unshared through huge_pmd_unshare() from getting freed after we +- * release i_mmap_rwsem, so flush the TLB now. If huge_pmd_unshare() +- * succeeded, flush the range corresponding to the pud. +- */ +- if (force_flush) +- tlb_flush_mmu_tlbonly(tlb); ++ huge_pmd_unshare_flush(tlb, vma); + } + + void __hugetlb_zap_begin(struct vm_area_struct *vma, +@@ -7104,11 +7095,11 @@ long hugetlb_change_protection(struct vm_area_struct *vma, + pte_t pte; + struct hstate *h = hstate_vma(vma); + long pages = 0, psize = huge_page_size(h); +- bool shared_pmd = false; + struct mmu_notifier_range range; + unsigned long last_addr_mask; + bool uffd_wp = cp_flags & MM_CP_UFFD_WP; + bool uffd_wp_resolve = cp_flags & MM_CP_UFFD_WP_RESOLVE; ++ struct mmu_gather tlb; + + /* + * In the case of shared PMDs, the area to flush could be beyond +@@ -7121,6 +7112,7 @@ long hugetlb_change_protection(struct vm_area_struct *vma, + + BUG_ON(address >= end); + flush_cache_range(vma, range.start, range.end); ++ tlb_gather_mmu_vma(&tlb, vma); + + mmu_notifier_invalidate_range_start(&range); + hugetlb_vma_lock_write(vma); +@@ -7145,7 +7137,7 @@ long hugetlb_change_protection(struct vm_area_struct *vma, + } + } + ptl = huge_pte_lock(h, mm, ptep); +- if (huge_pmd_unshare(mm, vma, address, ptep)) { ++ if (huge_pmd_unshare(&tlb, vma, address, ptep)) { + /* + * When uffd-wp is enabled on the vma, unshare + * shouldn't happen at all. Warn about it if it +@@ -7154,7 +7146,6 @@ long hugetlb_change_protection(struct vm_area_struct *vma, + WARN_ON_ONCE(uffd_wp || uffd_wp_resolve); + pages++; + spin_unlock(ptl); +- shared_pmd = true; + address |= last_addr_mask; + continue; + } +@@ -7206,6 +7197,7 @@ long hugetlb_change_protection(struct vm_area_struct *vma, + pte = huge_pte_clear_uffd_wp(pte); + huge_ptep_modify_prot_commit(vma, address, ptep, old_pte, pte); + pages++; ++ tlb_remove_huge_tlb_entry(h, &tlb, ptep, address); + } else { + /* None pte */ + if (unlikely(uffd_wp)) +@@ -7218,16 +7210,9 @@ long hugetlb_change_protection(struct vm_area_struct *vma, + + cond_resched(); + } +- /* +- * There is nothing protecting a previously-shared page table that we +- * unshared through huge_pmd_unshare() from getting freed after we +- * release i_mmap_rwsem, so flush the TLB now. If huge_pmd_unshare() +- * succeeded, flush the range corresponding to the pud. +- */ +- if (shared_pmd) +- flush_hugetlb_tlb_range(vma, range.start, range.end); +- else +- flush_hugetlb_tlb_range(vma, start, end); ++ ++ tlb_flush_mmu_tlbonly(&tlb); ++ huge_pmd_unshare_flush(&tlb, vma); + /* + * No need to call mmu_notifier_arch_invalidate_secondary_tlbs() we are + * downgrading page table protection not changing it to point to a new +@@ -7238,6 +7223,7 @@ long hugetlb_change_protection(struct vm_area_struct *vma, + i_mmap_unlock_write(vma->vm_file->f_mapping); + hugetlb_vma_unlock_write(vma); + mmu_notifier_invalidate_range_end(&range); ++ tlb_finish_mmu(&tlb); + + return pages > 0 ? (pages << h->order) : pages; + } +@@ -7590,18 +7576,27 @@ out: + return pte; + } + +-/* +- * unmap huge page backed by shared pte. ++/** ++ * huge_pmd_unshare - Unmap a pmd table if it is shared by multiple users ++ * @tlb: the current mmu_gather. ++ * @vma: the vma covering the pmd table. ++ * @addr: the address we are trying to unshare. ++ * @ptep: pointer into the (pmd) page table. ++ * ++ * Called with the page table lock held, the i_mmap_rwsem held in write mode ++ * and the hugetlb vma lock held in write mode. + * +- * Called with page table lock held. ++ * Note: The caller must call huge_pmd_unshare_flush() before dropping the ++ * i_mmap_rwsem. + * +- * returns: 1 successfully unmapped a shared pte page +- * 0 the underlying pte page is not shared, or it is the last user ++ * Returns: 1 if it was a shared PMD table and it got unmapped, or 0 if it ++ * was not a shared PMD table. + */ +-int huge_pmd_unshare(struct mm_struct *mm, struct vm_area_struct *vma, +- unsigned long addr, pte_t *ptep) ++int huge_pmd_unshare(struct mmu_gather *tlb, struct vm_area_struct *vma, ++ unsigned long addr, pte_t *ptep) + { + unsigned long sz = huge_page_size(hstate_vma(vma)); ++ struct mm_struct *mm = vma->vm_mm; + pgd_t *pgd = pgd_offset(mm, addr); + p4d_t *p4d = p4d_offset(pgd, addr); + pud_t *pud = pud_offset(p4d, addr); +@@ -7613,18 +7608,36 @@ int huge_pmd_unshare(struct mm_struct *mm, struct vm_area_struct *vma, + i_mmap_assert_write_locked(vma->vm_file->f_mapping); + hugetlb_vma_assert_locked(vma); + pud_clear(pud); +- /* +- * Once our caller drops the rmap lock, some other process might be +- * using this page table as a normal, non-hugetlb page table. +- * Wait for pending gup_fast() in other threads to finish before letting +- * that happen. +- */ +- tlb_remove_table_sync_one(); +- ptdesc_pmd_pts_dec(virt_to_ptdesc(ptep)); ++ ++ tlb_unshare_pmd_ptdesc(tlb, virt_to_ptdesc(ptep), addr); ++ + mm_dec_nr_pmds(mm); + return 1; + } + ++/* ++ * huge_pmd_unshare_flush - Complete a sequence of huge_pmd_unshare() calls ++ * @tlb: the current mmu_gather. ++ * @vma: the vma covering the pmd table. ++ * ++ * Perform necessary TLB flushes or IPI broadcasts to synchronize PMD table ++ * unsharing with concurrent page table walkers. ++ * ++ * This function must be called after a sequence of huge_pmd_unshare() ++ * calls while still holding the i_mmap_rwsem. ++ */ ++void huge_pmd_unshare_flush(struct mmu_gather *tlb, struct vm_area_struct *vma) ++{ ++ /* ++ * We must synchronize page table unsharing such that nobody will ++ * try reusing a previously-shared page table while it might still ++ * be in use by previous sharers (TLB, GUP_fast). ++ */ ++ i_mmap_assert_write_locked(vma->vm_file->f_mapping); ++ ++ tlb_flush_unshared_tables(tlb); ++} ++ + #else /* !CONFIG_HUGETLB_PMD_PAGE_TABLE_SHARING */ + + pte_t *huge_pmd_share(struct mm_struct *mm, struct vm_area_struct *vma, +@@ -7633,12 +7646,16 @@ pte_t *huge_pmd_share(struct mm_struct *mm, struct vm_area_struct *vma, + return NULL; + } + +-int huge_pmd_unshare(struct mm_struct *mm, struct vm_area_struct *vma, +- unsigned long addr, pte_t *ptep) ++int huge_pmd_unshare(struct mmu_gather *tlb, struct vm_area_struct *vma, ++ unsigned long addr, pte_t *ptep) + { + return 0; + } + ++void huge_pmd_unshare_flush(struct mmu_gather *tlb, struct vm_area_struct *vma) ++{ ++} ++ + void adjust_range_if_pmd_sharing_possible(struct vm_area_struct *vma, + unsigned long *start, unsigned long *end) + { +@@ -7905,6 +7922,7 @@ static void hugetlb_unshare_pmds(struct vm_area_struct *vma, + unsigned long sz = huge_page_size(h); + struct mm_struct *mm = vma->vm_mm; + struct mmu_notifier_range range; ++ struct mmu_gather tlb; + unsigned long address; + spinlock_t *ptl; + pte_t *ptep; +@@ -7916,6 +7934,8 @@ static void hugetlb_unshare_pmds(struct vm_area_struct *vma, + return; + + flush_cache_range(vma, start, end); ++ tlb_gather_mmu_vma(&tlb, vma); ++ + /* + * No need to call adjust_range_if_pmd_sharing_possible(), because + * we have already done the PUD_SIZE alignment. +@@ -7934,10 +7954,10 @@ static void hugetlb_unshare_pmds(struct vm_area_struct *vma, + if (!ptep) + continue; + ptl = huge_pte_lock(h, mm, ptep); +- huge_pmd_unshare(mm, vma, address, ptep); ++ huge_pmd_unshare(&tlb, vma, address, ptep); + spin_unlock(ptl); + } +- flush_hugetlb_tlb_range(vma, start, end); ++ huge_pmd_unshare_flush(&tlb, vma); + if (take_locks) { + i_mmap_unlock_write(vma->vm_file->f_mapping); + hugetlb_vma_unlock_write(vma); +@@ -7947,6 +7967,7 @@ static void hugetlb_unshare_pmds(struct vm_area_struct *vma, + * Documentation/mm/mmu_notifier.rst. + */ + mmu_notifier_invalidate_range_end(&range); ++ tlb_finish_mmu(&tlb); + } + + /* +diff --git a/mm/mmu_gather.c b/mm/mmu_gather.c +index 374aa6f021c6b0..5619ee967f3c7d 100644 +--- a/mm/mmu_gather.c ++++ b/mm/mmu_gather.c +@@ -9,6 +9,7 @@ + #include <linux/smp.h> + #include <linux/swap.h> + #include <linux/rmap.h> ++#include <linux/hugetlb.h> + + #include <asm/pgalloc.h> + #include <asm/tlb.h> +@@ -426,6 +427,7 @@ static void __tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, + #endif + tlb->vma_pfn = 0; + ++ tlb->fully_unshared_tables = 0; + __tlb_reset_range(tlb); + inc_tlb_flush_pending(tlb->mm); + } +@@ -459,6 +461,31 @@ void tlb_gather_mmu_fullmm(struct mmu_gather *tlb, struct mm_struct *mm) + __tlb_gather_mmu(tlb, mm, true); + } + ++/** ++ * tlb_gather_mmu_vma - initialize an mmu_gather structure for operating on a ++ * single VMA ++ * @tlb: the mmu_gather structure to initialize ++ * @vma: the vm_area_struct ++ * ++ * Called to initialize an (on-stack) mmu_gather structure for operating on ++ * a single VMA. In contrast to tlb_gather_mmu(), calling this function will ++ * not require another call to tlb_start_vma(). In contrast to tlb_start_vma(), ++ * this function will *not* call flush_cache_range(). ++ * ++ * For hugetlb VMAs, this function will also initialize the mmu_gather ++ * page_size accordingly, not requiring a separate call to ++ * tlb_change_page_size(). ++ * ++ */ ++void tlb_gather_mmu_vma(struct mmu_gather *tlb, struct vm_area_struct *vma) ++{ ++ tlb_gather_mmu(tlb, vma->vm_mm); ++ tlb_update_vma_flags(tlb, vma); ++ if (is_vm_hugetlb_page(vma)) ++ /* All entries have the same size. */ ++ tlb_change_page_size(tlb, huge_page_size(hstate_vma(vma))); ++} ++ + /** + * tlb_finish_mmu - finish an mmu_gather structure + * @tlb: the mmu_gather structure to finish +@@ -468,6 +495,12 @@ void tlb_gather_mmu_fullmm(struct mmu_gather *tlb, struct mm_struct *mm) + */ + void tlb_finish_mmu(struct mmu_gather *tlb) + { ++ /* ++ * We expect an earlier huge_pmd_unshare_flush() call to sort this out, ++ * due to complicated locking requirements with page table unsharing. ++ */ ++ VM_WARN_ON_ONCE(tlb->fully_unshared_tables); ++ + /* + * If there are parallel threads are doing PTE changes on same range + * under non-exclusive lock (e.g., mmap_lock read-side) but defer TLB +diff --git a/mm/rmap.c b/mm/rmap.c +index d52055a026a058..f1e6a97cf46043 100644 +--- a/mm/rmap.c ++++ b/mm/rmap.c +@@ -76,7 +76,7 @@ + #include <linux/mm_inline.h> + #include <linux/oom.h> + +-#include <asm/tlbflush.h> ++#include <asm/tlb.h> + + #define CREATE_TRACE_POINTS + #include <trace/events/migrate.h> +@@ -2019,13 +2019,17 @@ static bool try_to_unmap_one(struct folio *folio, struct vm_area_struct *vma, + * if unsuccessful. + */ + if (!anon) { ++ struct mmu_gather tlb; ++ + VM_BUG_ON(!(flags & TTU_RMAP_LOCKED)); + if (!hugetlb_vma_trylock_write(vma)) + goto walk_abort; +- if (huge_pmd_unshare(mm, vma, address, pvmw.pte)) { ++ ++ tlb_gather_mmu_vma(&tlb, vma); ++ if (huge_pmd_unshare(&tlb, vma, address, pvmw.pte)) { + hugetlb_vma_unlock_write(vma); +- flush_tlb_range(vma, +- range.start, range.end); ++ huge_pmd_unshare_flush(&tlb, vma); ++ tlb_finish_mmu(&tlb); + /* + * The PMD table was unmapped, + * consequently unmapping the folio. +@@ -2033,6 +2037,7 @@ static bool try_to_unmap_one(struct folio *folio, struct vm_area_struct *vma, + goto walk_done; + } + hugetlb_vma_unlock_write(vma); ++ tlb_finish_mmu(&tlb); + } + pteval = huge_ptep_clear_flush(vma, address, pvmw.pte); + if (pte_dirty(pteval)) +@@ -2398,17 +2403,20 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma, + * fail if unsuccessful. + */ + if (!anon) { ++ struct mmu_gather tlb; ++ + VM_BUG_ON(!(flags & TTU_RMAP_LOCKED)); + if (!hugetlb_vma_trylock_write(vma)) { + page_vma_mapped_walk_done(&pvmw); + ret = false; + break; + } +- if (huge_pmd_unshare(mm, vma, address, pvmw.pte)) { +- hugetlb_vma_unlock_write(vma); +- flush_tlb_range(vma, +- range.start, range.end); + ++ tlb_gather_mmu_vma(&tlb, vma); ++ if (huge_pmd_unshare(&tlb, vma, address, pvmw.pte)) { ++ hugetlb_vma_unlock_write(vma); ++ huge_pmd_unshare_flush(&tlb, vma); ++ tlb_finish_mmu(&tlb); + /* + * The PMD table was unmapped, + * consequently unmapping the folio. +@@ -2417,6 +2425,7 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma, + break; + } + hugetlb_vma_unlock_write(vma); ++ tlb_finish_mmu(&tlb); + } + /* Nuke the hugetlb page table entry */ + pteval = huge_ptep_clear_flush(vma, address, pvmw.pte); +diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs +index a849b7dde2fd91..176531f54ed30f 100644 +--- a/rust/kernel/device.rs ++++ b/rust/kernel/device.rs +@@ -62,8 +62,9 @@ pub mod property; + /// + /// # Implementing Bus Devices + /// +-/// This section provides a guideline to implement bus specific devices, such as [`pci::Device`] or +-/// [`platform::Device`]. ++/// This section provides a guideline to implement bus specific devices, such as: ++#[cfg_attr(CONFIG_PCI, doc = "* [`pci::Device`](kernel::pci::Device)")] ++/// * [`platform::Device`] + /// + /// A bus specific device should be defined as follows. + /// +@@ -155,7 +156,6 @@ pub mod property; + /// + /// [`AlwaysRefCounted`]: kernel::types::AlwaysRefCounted + /// [`impl_device_context_deref`]: kernel::impl_device_context_deref +-/// [`pci::Device`]: kernel::pci::Device + /// [`platform::Device`]: kernel::platform::Device + #[repr(transparent)] + pub struct Device<Ctx: DeviceContext = Normal>(Opaque<bindings::device>, PhantomData<Ctx>); +diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs +index 4e0af3e1a3b9ad..777ea5c2431c7e 100644 +--- a/rust/kernel/dma.rs ++++ b/rust/kernel/dma.rs +@@ -26,8 +26,9 @@ pub type DmaAddress = bindings::dma_addr_t; + /// Trait to be implemented by DMA capable bus devices. + /// + /// The [`dma::Device`](Device) trait should be implemented by bus specific device representations, +-/// where the underlying bus is DMA capable, such as [`pci::Device`](::kernel::pci::Device) or +-/// [`platform::Device`](::kernel::platform::Device). ++/// where the underlying bus is DMA capable, such as: ++#[cfg_attr(CONFIG_PCI, doc = "* [`pci::Device`](kernel::pci::Device)")] ++/// * [`platform::Device`](::kernel::platform::Device) + pub trait Device: AsRef<device::Device<Core>> { + /// Set up the device's DMA streaming addressing capabilities. + /// +diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs +index 279e3af206822c..16931b94d0d48c 100644 +--- a/rust/kernel/driver.rs ++++ b/rust/kernel/driver.rs +@@ -33,7 +33,14 @@ + //! } + //! ``` + //! +-//! For specific examples see [`auxiliary::Driver`], [`pci::Driver`] and [`platform::Driver`]. ++//! For specific examples see: ++//! ++//! * [`platform::Driver`](kernel::platform::Driver) ++#" ++)] ++#")] + //! + //! The `probe()` callback should return a `Result<Pin<KBox<Self>>>`, i.e. the driver's private + //! data. The bus abstraction should store the pointer in the corresponding bus device. The generic +@@ -79,7 +86,6 @@ + //! + //! For this purpose the generic infrastructure in [`device_id`] should be used. + //! +-//! [`auxiliary::Driver`]: kernel::auxiliary::Driver + //! [`Core`]: device::Core + //! [`Device`]: device::Device + //! [`Device<Core>`]: device::Device<device::Core> +@@ -87,8 +93,6 @@ + //! [`DeviceContext`]: device::DeviceContext + //! [`device_id`]: kernel::device_id + //! [`module_driver`]: kernel::module_driver +-//! [`pci::Driver`]: kernel::pci::Driver +-//! [`platform::Driver`]: kernel::platform::Driver + + use crate::error::{Error, Result}; + use crate::{acpi, device, of, str::CStr, try_pin_init, types::Opaque, ThisModule}; +diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c +index 2e9efafa732fe3..a16cb45ac59e34 100644 +--- a/sound/hda/codecs/realtek/alc269.c ++++ b/sound/hda/codecs/realtek/alc269.c +@@ -3371,11 +3371,22 @@ static void alc287_alc1318_playback_pcm_hook(struct hda_pcm_stream *hinfo, + struct snd_pcm_substream *substream, + int action) + { ++ static const struct coef_fw dis_coefs[] = { ++ WRITE_COEF(0x24, 0x0013), WRITE_COEF(0x25, 0x0000), WRITE_COEF(0x26, 0xC203), ++ WRITE_COEF(0x28, 0x0004), WRITE_COEF(0x29, 0xb023), ++ }; /* Disable AMP silence detection */ ++ static const struct coef_fw en_coefs[] = { ++ WRITE_COEF(0x24, 0x0013), WRITE_COEF(0x25, 0x0000), WRITE_COEF(0x26, 0xC203), ++ WRITE_COEF(0x28, 0x0084), WRITE_COEF(0x29, 0xb023), ++ }; /* Enable AMP silence detection */ ++ + switch (action) { + case HDA_GEN_PCM_ACT_OPEN: ++ alc_process_coef_fw(codec, dis_coefs); + alc_write_coefex_idx(codec, 0x5a, 0x00, 0x954f); /* write gpio3 to high */ + break; + case HDA_GEN_PCM_ACT_CLOSE: ++ alc_process_coef_fw(codec, en_coefs); + alc_write_coefex_idx(codec, 0x5a, 0x00, 0x554f); /* write gpio3 as default value */ + break; + } +@@ -6260,6 +6271,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x1025, 0x1430, "Acer TravelMate B311R-31", ALC256_FIXUP_ACER_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1025, 0x1466, "Acer Aspire A515-56", ALC255_FIXUP_ACER_HEADPHONE_AND_MIC), + SND_PCI_QUIRK(0x1025, 0x1534, "Acer Predator PH315-54", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), ++ SND_PCI_QUIRK(0x1025, 0x1539, "Acer Nitro 5 AN515-57", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x159c, "Acer Nitro 5 AN515-58", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x1597, "Acer Nitro 5 AN517-55", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x169a, "Acer Swift SFG16", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), +@@ -7243,6 +7255,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x1ee7, 0x2078, "HONOR BRB-X M1010", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1f66, 0x0105, "Ayaneo Portable Game Player", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x2014, 0x800a, "Positivo ARN50", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), ++ SND_PCI_QUIRK(0x2039, 0x0001, "Inspur S14-G1", ALC295_FIXUP_CHROME_BOOK), + SND_PCI_QUIRK(0x2782, 0x0214, "VAIO VJFE-CL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x2782, 0x0228, "Infinix ZERO BOOK 13", ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13), + SND_PCI_QUIRK(0x2782, 0x0232, "CHUWI CoreBook XPro", ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO), +diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c +index c4a4a06528b45d..67f2fee1939804 100644 +--- a/sound/soc/amd/yc/acp6x-mach.c ++++ b/sound/soc/amd/yc/acp6x-mach.c +@@ -542,6 +542,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { + DMI_MATCH(DMI_PRODUCT_NAME, "15NBC1011"), + } + }, ++ { ++ .driver_data = &acp6x_card, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "ASUS EXPERTBOOK PM1503CDA"), ++ } ++ }, + { + .driver_data = &acp6x_card, + .matches = { +@@ -633,6 +640,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { + DMI_MATCH(DMI_BOARD_NAME, "8BD6"), + } + }, ++ { ++ .driver_data = &acp6x_card, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "HP"), ++ DMI_MATCH(DMI_BOARD_NAME, "8EE4"), ++ } ++ }, + { + .driver_data = &acp6x_card, + .matches = { +diff --git a/sound/soc/codecs/cs35l45.c b/sound/soc/codecs/cs35l45.c +index d4dcdf37bb709a..9b1eff4e9bb717 100644 +--- a/sound/soc/codecs/cs35l45.c ++++ b/sound/soc/codecs/cs35l45.c +@@ -455,7 +455,7 @@ static const struct snd_soc_dapm_widget cs35l45_dapm_widgets[] = { + SND_SOC_DAPM_AIF_OUT("ASP_TX2", NULL, 1, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX2_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASP_TX3", NULL, 2, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX3_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASP_TX4", NULL, 3, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX4_EN_SHIFT, 0), +- SND_SOC_DAPM_AIF_OUT("ASP_TX5", NULL, 3, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX5_EN_SHIFT, 0), ++ SND_SOC_DAPM_AIF_OUT("ASP_TX5", NULL, 4, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX5_EN_SHIFT, 0), + + SND_SOC_DAPM_MUX("ASP_TX1 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[0]), + SND_SOC_DAPM_MUX("ASP_TX2 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[1]), +diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c +index 867e23d4fb8d8b..744488f371ea43 100644 +--- a/sound/soc/codecs/cs42l43-jack.c ++++ b/sound/soc/codecs/cs42l43-jack.c +@@ -496,7 +496,23 @@ void cs42l43_bias_sense_timeout(struct work_struct *work) + pm_runtime_put_autosuspend(priv->dev); + } + +-static void cs42l43_start_load_detect(struct cs42l43_codec *priv) ++static const struct reg_sequence cs42l43_3pole_patch[] = { ++ { 0x4000, 0x00000055 }, ++ { 0x4000, 0x000000AA }, ++ { 0x17420, 0x8500F300 }, ++ { 0x17424, 0x36003E00 }, ++ { 0x4000, 0x00000000 }, ++}; ++ ++static const struct reg_sequence cs42l43_4pole_patch[] = { ++ { 0x4000, 0x00000055 }, ++ { 0x4000, 0x000000AA }, ++ { 0x17420, 0x7800E600 }, ++ { 0x17424, 0x36003800 }, ++ { 0x4000, 0x00000000 }, ++}; ++ ++static void cs42l43_start_load_detect(struct cs42l43_codec *priv, bool mic) + { + struct cs42l43 *cs42l43 = priv->core; + +@@ -520,6 +536,15 @@ static void cs42l43_start_load_detect(struct cs42l43_codec *priv) + dev_err(priv->dev, "Load detect HP power down timed out\n"); + } + ++ if (mic) ++ regmap_multi_reg_write_bypassed(cs42l43->regmap, ++ cs42l43_4pole_patch, ++ ARRAY_SIZE(cs42l43_4pole_patch)); ++ else ++ regmap_multi_reg_write_bypassed(cs42l43->regmap, ++ cs42l43_3pole_patch, ++ ARRAY_SIZE(cs42l43_3pole_patch)); ++ + regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN3, + CS42L43_ADC1_EN_MASK | CS42L43_ADC2_EN_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_DACCNFG2, CS42L43_HP_HPF_EN_MASK, 0); +@@ -598,7 +623,7 @@ static int cs42l43_run_load_detect(struct cs42l43_codec *priv, bool mic) + + reinit_completion(&priv->load_detect); + +- cs42l43_start_load_detect(priv); ++ cs42l43_start_load_detect(priv, mic); + time_left = wait_for_completion_timeout(&priv->load_detect, + msecs_to_jiffies(CS42L43_LOAD_TIMEOUT_MS)); + cs42l43_stop_load_detect(priv); +@@ -622,11 +647,11 @@ static int cs42l43_run_load_detect(struct cs42l43_codec *priv, bool mic) + } + + switch (val & CS42L43_AMP3_RES_DET_MASK) { +- case 0x0: // low impedance +- case 0x1: // high impedance ++ case 0x0: // < 22 Ohm impedance ++ case 0x1: // < 150 Ohm impedance ++ case 0x2: // < 1000 Ohm impedance + return CS42L43_JACK_HEADPHONE; +- case 0x2: // lineout +- case 0x3: // Open circuit ++ case 0x3: // > 1000 Ohm impedance + return CS42L43_JACK_LINEOUT; + default: + return -EINVAL; +diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c +index 58db4906a01d50..51669e5fe8888e 100644 +--- a/sound/soc/fsl/fsl_xcvr.c ++++ b/sound/soc/fsl/fsl_xcvr.c +@@ -223,10 +223,13 @@ static int fsl_xcvr_mode_put(struct snd_kcontrol *kcontrol, + + xcvr->mode = snd_soc_enum_item_to_val(e, item[0]); + ++ down_read(&card->snd_card->controls_rwsem); + fsl_xcvr_activate_ctl(dai, fsl_xcvr_arc_mode_kctl.name, + (xcvr->mode == FSL_XCVR_MODE_ARC)); + fsl_xcvr_activate_ctl(dai, fsl_xcvr_earc_capds_kctl.name, + (xcvr->mode == FSL_XCVR_MODE_EARC)); ++ up_read(&card->snd_card->controls_rwsem); ++ + /* Allow playback for SPDIF only */ + rtd = snd_soc_get_pcm_runtime(card, card->dai_link); + rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count = +diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c +index 09acd80d23e0fc..cf50de5c2edd8d 100644 +--- a/sound/soc/intel/boards/sof_es8336.c ++++ b/sound/soc/intel/boards/sof_es8336.c +@@ -332,6 +332,15 @@ static int sof_es8336_quirk_cb(const struct dmi_system_id *id) + * if the topology file is modified as well. + */ + static const struct dmi_system_id sof_es8336_quirk_table[] = { ++ { ++ .callback = sof_es8336_quirk_cb, ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "BOD-WXX9"), ++ }, ++ .driver_data = (void *)(SOF_ES8336_HEADPHONE_GPIO | ++ SOF_ES8336_ENABLE_DMIC) ++ }, + { + .callback = sof_es8336_quirk_cb, + .matches = { +diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c +index 92fac7ed782f73..6c95b1f8fc1a59 100644 +--- a/sound/soc/intel/boards/sof_sdw.c ++++ b/sound/soc/intel/boards/sof_sdw.c +@@ -802,6 +802,7 @@ static const struct snd_pci_quirk sof_sdw_ssid_quirk_table[] = { + SND_PCI_QUIRK(0x17aa, 0x2347, "Lenovo P16", SOC_SDW_CODEC_MIC), + SND_PCI_QUIRK(0x17aa, 0x2348, "Lenovo P16", SOC_SDW_CODEC_MIC), + SND_PCI_QUIRK(0x17aa, 0x2349, "Lenovo P1", SOC_SDW_CODEC_MIC), ++ SND_PCI_QUIRK(0x17aa, 0x3821, "Lenovo 0x3821", SOC_SDW_SIDECAR_AMPS), + {} + }; +
