* Re: [PATCH v3 2/2] mailbox: Make mbox_send_message() return error code when tx fails
From: Joonwon Kang @ 2026-04-03 15:19 UTC (permalink / raw)
To: jassisinghbrar
Cc: angelogioacchino.delregno, jonathanh, joonwonkang,
linux-arm-kernel, linux-kernel, linux-mediatek, linux-tegra,
matthias.bgg, stable, thierry.reding, akpm
In-Reply-To: <CABb+yY3hYcJ82QGor3w5KKHUGz9Pc1k64Jdf-94E4Yvv0DTeyQ@mail.gmail.com>
> On Thu, Apr 2, 2026 at 12:07 PM Joonwon Kang <joonwonkang@google.com> wrote:
> >
> > When the mailbox controller failed transmitting message, the error code
> > was only passed to the client's tx done handler and not to
> > mbox_send_message(). For this reason, the function could return a false
> > success. This commit resolves the issue by introducing the tx status and
> > checking it before mbox_send_message() returns.
> >
> Can you please share the scenario when this becomes necessary? This
> can potentially change the ground underneath some clients, so we have
> to be sure this is really useful.
I would say the problem here is generic enough to apply to all the cases where
the send result needs to be checked. Since the return value of the send API is
not the real send result, any users who believe that this blocking send API
will return the real send result could fall for that. For example, users may
think the send was successful even though it was not actually. I believe it is
uncommon that users have to register a callback solely to get the send result
even though they are using the blocking send API already. Also, I guess there
is no special reason why only the mailbox send API should work this way among
other typical blocking send APIs. For these reasons, this patch makes the send
API return the real send result. This way, users will not need to register the
redundant callback and I think the return value will align with their common
expectation.
Regarding the change in the ground for some clients, could you help to clarify
a bit more on what change, you expect, would surprise the clients?
Thanks,
Joonwon Kang
>
> Thanks
> Jassi
>
>
> > Cc: stable@vger.kernel.org
> > Signed-off-by: Joonwon Kang <joonwonkang@google.com>
> > ---
> > drivers/mailbox/mailbox.c | 20 +++++++++++++++-----
> > include/linux/mailbox_controller.h | 2 ++
> > 2 files changed, 17 insertions(+), 5 deletions(-)
> >
> > diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
> > index d63386468982..ea9aec9dc947 100644
> > --- a/drivers/mailbox/mailbox.c
> > +++ b/drivers/mailbox/mailbox.c
> > @@ -21,7 +21,10 @@
> > static LIST_HEAD(mbox_cons);
> > static DEFINE_MUTEX(con_mutex);
> >
> > -static int add_to_rbuf(struct mbox_chan *chan, void *mssg, struct completion *tx_complete)
> > +static int add_to_rbuf(struct mbox_chan *chan,
> > + void *mssg,
> > + struct completion *tx_complete,
> > + int *tx_status)
> > {
> > int idx;
> >
> > @@ -34,6 +37,7 @@ static int add_to_rbuf(struct mbox_chan *chan, void *mssg, struct completion *tx
> > idx = chan->msg_free;
> > chan->msg_data[idx].data = mssg;
> > chan->msg_data[idx].tx_complete = tx_complete;
> > + chan->msg_data[idx].tx_status = tx_status;
> > chan->msg_count++;
> >
> > if (idx == MBOX_TX_QUEUE_LEN - 1)
> > @@ -91,12 +95,13 @@ static void msg_submit(struct mbox_chan *chan)
> >
> > static void tx_tick(struct mbox_chan *chan, int r, int idx)
> > {
> > - struct mbox_message mssg = {MBOX_NO_MSG, NULL};
> > + struct mbox_message mssg = {MBOX_NO_MSG, NULL, NULL};
> >
> > scoped_guard(spinlock_irqsave, &chan->lock) {
> > if (idx >= 0 && idx != chan->active_req) {
> > chan->msg_data[idx].data = MBOX_NO_MSG;
> > chan->msg_data[idx].tx_complete = NULL;
> > + chan->msg_data[idx].tx_status = NULL;
> > return;
> > }
> >
> > @@ -116,8 +121,10 @@ static void tx_tick(struct mbox_chan *chan, int r, int idx)
> > if (chan->cl->tx_done)
> > chan->cl->tx_done(chan->cl, mssg.data, r);
> >
> > - if (r != -ETIME && chan->cl->tx_block)
> > + if (r != -ETIME && chan->cl->tx_block) {
> > + *mssg.tx_status = r;
> > complete(mssg.tx_complete);
> > + }
> > }
> >
> > static enum hrtimer_restart txdone_hrtimer(struct hrtimer *hrtimer)
> > @@ -286,15 +293,16 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg)
> > int t;
> > int idx;
> > struct completion tx_complete;
> > + int tx_status = 0;
> >
> > if (!chan || !chan->cl || mssg == MBOX_NO_MSG)
> > return -EINVAL;
> >
> > if (chan->cl->tx_block) {
> > init_completion(&tx_complete);
> > - t = add_to_rbuf(chan, mssg, &tx_complete);
> > + t = add_to_rbuf(chan, mssg, &tx_complete, &tx_status);
> > } else {
> > - t = add_to_rbuf(chan, mssg, NULL);
> > + t = add_to_rbuf(chan, mssg, NULL, NULL);
> > }
> >
> > if (t < 0) {
> > @@ -318,6 +326,8 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg)
> > idx = t;
> > t = -ETIME;
> > tx_tick(chan, t, idx);
> > + } else if (tx_status < 0) {
> > + t = tx_status;
> > }
> > }
> >
> > diff --git a/include/linux/mailbox_controller.h b/include/linux/mailbox_controller.h
> > index 912499ad08ed..890da97bcb50 100644
> > --- a/include/linux/mailbox_controller.h
> > +++ b/include/linux/mailbox_controller.h
> > @@ -117,10 +117,12 @@ struct mbox_controller {
> > * struct mbox_message - Internal representation of a mailbox message
> > * @data: Data packet
> > * @tx_complete: Pointer to the transmission completion
> > + * @tx_status: Pointer to the transmission status
> > */
> > struct mbox_message {
> > void *data;
> > struct completion *tx_complete;
> > + int *tx_status;
> > };
> >
> > /**
> > --
> > 2.53.0.1185.g05d4b7b318-goog
> >
^ permalink raw reply
* Re: [PATCH v6 07/10] clk: realtek: Add support for MMC-tuned PLL clocks
From: Brian Masney @ 2026-04-03 15:10 UTC (permalink / raw)
To: Yu-Chun Lin
Cc: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou, devicetree, linux-clk, linux-kernel,
linux-arm-kernel, linux-realtek-soc, james.tai, cy.huang,
stanley_chang
In-Reply-To: <20260402073957.2742459-8-eleanor.lin@realtek.com>
Hi Yu-Chun,
I should have finished going through Sashiko while manually reviewing
your patches.
On Thu, Apr 02, 2026 at 03:39:54PM +0800, Yu-Chun Lin wrote:
> From: Cheng-Yu Lee <cylee12@realtek.com>
>
> Add clk_pll_mmc_ops for enable/disable, prepare, rate control, and status
> operations on MMC PLL clocks.
>
> Also add clk_pll_mmc_phase_ops to support phase get/set operations.
>
> Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
> Co-developed-by: Jyan Chou <jyanchou@realtek.com>
> Signed-off-by: Jyan Chou <jyanchou@realtek.com>
> Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
> Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
> ---
> +static int clk_pll_mmc_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate)
> +{
> + struct clk_pll_mmc *clkm = to_clk_pll_mmc(hw);
> + u32 val = PLL_MMC_SSC_DIV_N_VAL;
> + int ret;
> +
> + ret = regmap_update_bits(clkm->clkr.regmap,
> + clkm->ssc_dig_ofs + PLL_SSC_DIG_EMMC1_OFFSET,
> + PLL_FLAG_INITAL_EMMC_MASK, 0x0 << PLL_FLAG_INITAL_EMMC_SHIFT);
> + if (ret)
> + return ret;
> +
> + ret = set_ssc_div_n(clkm, val);
> + if (ret)
> + return ret;
> +
> + ret = set_ssc_div_ext_f(clkm, 1517);
> + if (ret)
> + return ret;
> +
> + switch (val) {
> + case 31 ... 46:
> + ret |= set_pi_ibselh(clkm, 3);
> + ret |= set_sscpll_rs(clkm, 3);
> + ret |= set_sscpll_icp(clkm, 2);
Sashiko reports:
https://sashiko.dev/#/patchset/20260402073957.2742459-1-eleanor.lin%40realtek.com
Is it intended to use bitwise OR to accumulate these return values? Because
these hardware operations return standard negative error codes on failure,
performing a bitwise OR on multiple negative integers will merge their bit
patterns and create a corrupted error code.
> + break;
> +
> + case 20 ... 30:
> + ret |= set_pi_ibselh(clkm, 2);
> + ret |= set_sscpll_rs(clkm, 3);
> + ret |= set_sscpll_icp(clkm, 1);
> + break;
> +
> + case 10 ... 19:
> + ret |= set_pi_ibselh(clkm, 1);
> + ret |= set_sscpll_rs(clkm, 2);
> + ret |= set_sscpll_icp(clkm, 1);
> + break;
> +
> + case 5 ... 9:
> + ret |= set_pi_ibselh(clkm, 0);
> + ret |= set_sscpll_rs(clkm, 2);
> + ret |= set_sscpll_icp(clkm, 0);
> + break;
> + }
> + if (ret)
> + return ret;
> +
> + ret = regmap_update_bits(clkm->clkr.regmap,
> + clkm->ssc_dig_ofs + PLL_SSC_DIG_EMMC3_OFFSET,
> + PLL_NCODE_SSC_EMMC_MASK,
> + 27 << PLL_NCODE_SSC_EMMC_SHIFT);
Sashiko reports:
https://sashiko.dev/#/patchset/20260402073957.2742459-1-eleanor.lin%40realtek.com
Are the mask and shift values mismatched here? PLL_FLAG_INITAL_EMMC_MASK is
defined as BIT(1) (0x02), but PLL_FLAG_INITAL_EMMC_SHIFT is 8.
When regmap_update_bits() applies the 0x02 mask to a value shifted by 8,
won't it evaluate to 0 and fail to set the intended initialization flag?
Brian
^ permalink raw reply
* Re: [PATCH v6 07/10] clk: realtek: Add support for MMC-tuned PLL clocks
From: Brian Masney @ 2026-04-03 15:07 UTC (permalink / raw)
To: Yu-Chun Lin
Cc: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou, devicetree, linux-clk, linux-kernel,
linux-arm-kernel, linux-realtek-soc, james.tai, cy.huang,
stanley_chang
In-Reply-To: <20260402073957.2742459-8-eleanor.lin@realtek.com>
Hi Yu-Chun and Cheng-Yu,
On Thu, Apr 02, 2026 at 03:39:54PM +0800, Yu-Chun Lin wrote:
> From: Cheng-Yu Lee <cylee12@realtek.com>
>
> Add clk_pll_mmc_ops for enable/disable, prepare, rate control, and status
> operations on MMC PLL clocks.
>
> Also add clk_pll_mmc_phase_ops to support phase get/set operations.
>
> Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
> Co-developed-by: Jyan Chou <jyanchou@realtek.com>
> Signed-off-by: Jyan Chou <jyanchou@realtek.com>
> Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
> Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
> ---
> Changes in v6:
> - Add the headers used in c file to follow the "Include What You Use" principle.
> - Move to_clk_pll_mmc() from clk-pll.h to clk-pll-mmc.c to limit its scope.
> - Change offset type from int to unsigned int.
> ---
> MAINTAINERS | 8 +
> drivers/clk/realtek/Kconfig | 3 +
> drivers/clk/realtek/Makefile | 2 +
> drivers/clk/realtek/clk-pll-mmc.c | 410 ++++++++++++++++++++++++++++++
> drivers/clk/realtek/clk-pll.h | 13 +
> 5 files changed, 436 insertions(+)
> create mode 100644 drivers/clk/realtek/clk-pll-mmc.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 8318156a02b5..4b28af4b26b5 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -22245,6 +22245,14 @@ F: drivers/reset/realtek/*
> F: include/dt-bindings/clock/realtek*
> F: include/dt-bindings/reset/realtek*
>
> +REALTEK SOC PLL CLOCK FOR MMC SUPPORT
> +M: Cheng-Yu Lee <cylee12@realtek.com>
> +M: Jyan Chou <jyanchou@realtek.com>
> +M: Yu-Chun Lin <eleanor.lin@realtek.com>
> +L: linux-clk@vger.kernel.org
> +S: Supported
> +F: drivers/clk/realtek/clk-pll-mmc.c
> +
> REALTEK SPI-NAND
> M: Chris Packham <chris.packham@alliedtelesis.co.nz>
> S: Maintained
> diff --git a/drivers/clk/realtek/Kconfig b/drivers/clk/realtek/Kconfig
> index bc47d3f1c452..b31a31e57b3a 100644
> --- a/drivers/clk/realtek/Kconfig
> +++ b/drivers/clk/realtek/Kconfig
> @@ -25,4 +25,7 @@ config RTK_CLK_COMMON
> multiple Realtek clock implementations, and include integration
> with reset controllers where required.
>
> +config RTK_CLK_PLL_MMC
> + bool
> +
> endif
> diff --git a/drivers/clk/realtek/Makefile b/drivers/clk/realtek/Makefile
> index f90dc57fcfdb..fd7d777902c8 100644
> --- a/drivers/clk/realtek/Makefile
> +++ b/drivers/clk/realtek/Makefile
> @@ -7,3 +7,5 @@ clk-rtk-y += clk-pll.o
> clk-rtk-y += clk-regmap-gate.o
> clk-rtk-y += clk-regmap-mux.o
> clk-rtk-y += freq_table.o
> +
> +clk-rtk-$(CONFIG_RTK_CLK_PLL_MMC) += clk-pll-mmc.o
> diff --git a/drivers/clk/realtek/clk-pll-mmc.c b/drivers/clk/realtek/clk-pll-mmc.c
> new file mode 100644
> index 000000000000..d28c7027d3f0
> --- /dev/null
> +++ b/drivers/clk/realtek/clk-pll-mmc.c
> @@ -0,0 +1,410 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2021 Realtek Semiconductor Corporation
> + * Author: Cheng-Yu Lee <cylee12@realtek.com>
> + */
> +
> +#include <linux/bits.h>
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +#include "clk-pll.h"
> +
> +#define PLL_EMMC1_OFFSET 0x0
> +#define PLL_EMMC2_OFFSET 0x4
> +#define PLL_EMMC3_OFFSET 0x8
> +#define PLL_EMMC4_OFFSET 0xc
> +#define PLL_SSC_DIG_EMMC1_OFFSET 0x0
> +#define PLL_SSC_DIG_EMMC3_OFFSET 0xc
> +#define PLL_SSC_DIG_EMMC4_OFFSET 0x10
> +
> +#define PLL_MMC_SSC_DIV_N_VAL 0x1b
> +
> +#define PLL_PHRT0_MASK BIT(1)
> +#define PLL_PHSEL_MASK GENMASK(4, 0)
> +#define PLL_SSCPLL_RS_MASK GENMASK(12, 10)
> +#define PLL_SSCPLL_ICP_MASK GENMASK(9, 5)
> +#define PLL_SSC_DIV_EXT_F_MASK GENMASK(25, 13)
> +#define PLL_PI_IBSELH_MASK GENMASK(28, 27)
> +#define PLL_SSC_DIV_N_MASK GENMASK(23, 16)
> +#define PLL_NCODE_SSC_EMMC_MASK GENMASK(20, 13)
> +#define PLL_FCODE_SSC_EMMC_MASK GENMASK(12, 0)
> +#define PLL_GRAN_EST_EM_MC_MASK GENMASK(20, 0)
> +#define PLL_EN_SSC_EMMC_MASK BIT(0)
> +#define PLL_FLAG_INITAL_EMMC_MASK BIT(1)
> +
> +#define PLL_PHRT0_SHIFT 1
> +#define PLL_SSCPLL_RS_SHIFT 10
> +#define PLL_SSCPLL_ICP_SHIFT 5
> +#define PLL_SSC_DIV_EXT_F_SHIFT 13
> +#define PLL_PI_IBSELH_SHIFT 27
> +#define PLL_SSC_DIV_N_SHIFT 16
> +#define PLL_NCODE_SSC_EMMC_SHIFT 13
> +#define PLL_FLAG_INITAL_EMMC_SHIFT 8
> +
> +#define CYCLE_DEGREES 360
> +#define PHASE_STEPS 32
> +#define PHASE_SCALE_FACTOR 1125
> +
> +static inline struct clk_pll_mmc *to_clk_pll_mmc(struct clk_hw *hw)
> +{
> + struct clk_regmap *clkr = to_clk_regmap(hw);
> +
> + return container_of(clkr, struct clk_pll_mmc, clkr);
> +}
> +
> +static inline int get_phrt0(struct clk_pll_mmc *clkm, u32 *val)
> +{
> + u32 reg;
> + int ret;
> +
> + ret = regmap_read(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC1_OFFSET, ®);
> + if (ret)
> + return ret;
> +
> + *val = (reg >> PLL_PHRT0_SHIFT) & PLL_PHRT0_MASK;
Sashiko reports the following:
https://sashiko.dev/#/patchset/20260402073957.2742459-1-eleanor.lin%40realtek.com
With PLL_PHRT0_SHIFT defined as 1 and PLL_PHRT0_MASK as BIT(1) (0x02), shifting
right by 1 moves the target bit 1 to position 0, but masking with 0x02 checks
position 1 of the shifted value.
Will this cause clk_pll_mmc_is_enabled() to always evaluate to false since it
expects val == 0x1?
> + return 0;
> +}
> +
> +static inline int set_phrt0(struct clk_pll_mmc *clkm, u32 val)
> +{
> + return regmap_update_bits(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC1_OFFSET,
> + PLL_PHRT0_MASK, val << PLL_PHRT0_SHIFT);
> +}
> +
> +static inline int get_phsel(struct clk_pll_mmc *clkm, int id, u32 *val)
> +{
> + int ret;
> + u32 raw_val;
> + u32 sft = id ? 8 : 3;
Put variables in reverse Christmas tree order.
> +
> + ret = regmap_read(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC1_OFFSET, &raw_val);
> + if (ret)
> + return ret;
> +
> + *val = (raw_val >> sft) & PLL_PHSEL_MASK;
> + return 0;
> +}
> +
> +static inline int set_phsel(struct clk_pll_mmc *clkm, int id, u32 val)
> +{
> + u32 sft = id ? 8 : 3;
> +
> + return regmap_update_bits(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC1_OFFSET,
> + PLL_PHSEL_MASK << sft, val << sft);
> +}
> +
> +static inline int set_sscpll_rs(struct clk_pll_mmc *clkm, u32 val)
> +{
> + return regmap_update_bits(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC2_OFFSET,
> + PLL_SSCPLL_RS_MASK, val << PLL_SSCPLL_RS_SHIFT);
> +}
> +
> +static inline int set_sscpll_icp(struct clk_pll_mmc *clkm, u32 val)
> +{
> + return regmap_update_bits(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC2_OFFSET,
> + PLL_SSCPLL_ICP_MASK, val << PLL_SSCPLL_ICP_SHIFT);
> +}
> +
> +static inline int get_ssc_div_ext_f(struct clk_pll_mmc *clkm, u32 *val)
> +{
> + u32 raw_val;
> + int ret;
> +
> + ret = regmap_read(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC2_OFFSET, &raw_val);
> + if (ret)
> + return ret;
> +
> + *val = (raw_val & PLL_SSC_DIV_EXT_F_MASK) >> PLL_SSC_DIV_EXT_F_SHIFT;
> + return 0;
> +}
> +
> +static inline int set_ssc_div_ext_f(struct clk_pll_mmc *clkm, u32 val)
> +{
> + return regmap_update_bits(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC2_OFFSET,
> + PLL_SSC_DIV_EXT_F_MASK,
> + val << PLL_SSC_DIV_EXT_F_SHIFT);
> +}
> +
> +static inline int set_pi_ibselh(struct clk_pll_mmc *clkm, u32 val)
> +{
> + return regmap_update_bits(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC2_OFFSET,
> + PLL_PI_IBSELH_MASK, val << PLL_PI_IBSELH_SHIFT);
> +}
> +
> +static inline int set_ssc_div_n(struct clk_pll_mmc *clkm, u32 val)
> +{
> + return regmap_update_bits(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC3_OFFSET,
> + PLL_SSC_DIV_N_MASK, val << PLL_SSC_DIV_N_SHIFT);
> +}
> +
> +static inline int get_ssc_div_n(struct clk_pll_mmc *clkm, u32 *val)
> +{
> + int ret;
> + u32 raw_val;
> +
> + ret = regmap_read(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC3_OFFSET, &raw_val);
> + if (ret)
> + return ret;
> +
> + *val = (raw_val & PLL_SSC_DIV_N_MASK) >> PLL_SSC_DIV_N_SHIFT;
> + return 0;
> +}
> +
> +static inline int set_pow_ctl(struct clk_pll_mmc *clkm, u32 val)
> +{
> + return regmap_write(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC4_OFFSET, val);
> +}
> +
> +static inline int get_pow_ctl(struct clk_pll_mmc *clkm, u32 *val)
> +{
> + int ret;
> + u32 raw_val;
> +
> + ret = regmap_read(clkm->clkr.regmap, clkm->pll_ofs + PLL_EMMC4_OFFSET, &raw_val);
> + if (ret)
> + return ret;
> +
> + *val = raw_val;
> +
> + return 0;
> +}
> +
> +static int clk_pll_mmc_phase_set_phase(struct clk_hw *hw, int degrees)
> +{
> + struct clk_hw *hwp = clk_hw_get_parent(hw);
> + struct clk_pll_mmc *clkm;
> + int phase_id;
> + int ret;
> + u32 val;
> +
> + if (!hwp)
> + return -ENOENT;
> +
> + clkm = to_clk_pll_mmc(hwp);
> + phase_id = (hw - &clkm->phase0_hw) ? 1 : 0;
Are you checking to see if these two pointers are the same? If so, what
do you think about this instead?
hw == &clkm->phase0_hw
> + val = DIV_ROUND_CLOSEST(degrees * 100, PHASE_SCALE_FACTOR);
> + ret = set_phsel(clkm, phase_id, val);
> + if (ret)
> + return ret;
> +
> + usleep_range(10, 20);
> + return 0;
> +}
> +
> +static int clk_pll_mmc_phase_get_phase(struct clk_hw *hw)
> +{
> + struct clk_hw *hwp;
> + struct clk_pll_mmc *clkm;
> + int phase_id;
> + int ret;
> + u32 val;
> +
> + hwp = clk_hw_get_parent(hw);
> + if (!hwp)
> + return -ENOENT;
> +
> + clkm = to_clk_pll_mmc(hwp);
> + phase_id = (hw - &clkm->phase0_hw) ? 1 : 0;
> + ret = get_phsel(clkm, phase_id, &val);
> + if (ret)
> + return ret;
> +
> + val = DIV_ROUND_CLOSEST(val * CYCLE_DEGREES, PHASE_STEPS);
> +
> + return val;
> +}
> +
> +const struct clk_ops rtk_clk_pll_mmc_phase_ops = {
> + .set_phase = clk_pll_mmc_phase_set_phase,
> + .get_phase = clk_pll_mmc_phase_get_phase,
> +};
> +EXPORT_SYMBOL_NS_GPL(rtk_clk_pll_mmc_phase_ops, "REALTEK_CLK");
> +
> +static int clk_pll_mmc_prepare(struct clk_hw *hw)
> +{
> + struct clk_pll_mmc *clkm = to_clk_pll_mmc(hw);
> +
> + return set_pow_ctl(clkm, 7);
> +}
> +
> +static void clk_pll_mmc_unprepare(struct clk_hw *hw)
> +{
> + struct clk_pll_mmc *clkm = to_clk_pll_mmc(hw);
> +
> + set_pow_ctl(clkm, 0);
> +}
> +
> +static int clk_pll_mmc_is_prepared(struct clk_hw *hw)
> +{
> + struct clk_pll_mmc *clkm = to_clk_pll_mmc(hw);
> + u32 val;
> + int ret;
> +
> + ret = get_pow_ctl(clkm, &val);
> + if (ret)
> + return 1;
> +
> + return val != 0x0;
> +}
> +
> +static int clk_pll_mmc_enable(struct clk_hw *hw)
> +{
> + struct clk_pll_mmc *clkm = to_clk_pll_mmc(hw);
> + int ret;
> +
> + ret = set_phrt0(clkm, 1);
> + if (ret)
> + return ret;
> +
> + udelay(10);
> + return 0;
> +}
> +
> +static void clk_pll_mmc_disable(struct clk_hw *hw)
> +{
> + struct clk_pll_mmc *clkm = to_clk_pll_mmc(hw);
> +
> + set_phrt0(clkm, 0);
> + udelay(10);
> +}
> +
> +static int clk_pll_mmc_is_enabled(struct clk_hw *hw)
> +{
> + struct clk_pll_mmc *clkm = to_clk_pll_mmc(hw);
> + u32 val;
> + int ret;
> +
> + ret = get_phrt0(clkm, &val);
> + if (ret)
> + return 1;
> +
> + return val == 0x1;
> +}
> +
> +static unsigned long clk_pll_mmc_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
> +{
> + struct clk_pll_mmc *clkm = to_clk_pll_mmc(hw);
> + u32 val, ext_f;
> + int ret;
> +
> + ret = get_ssc_div_n(clkm, &val);
> + if (ret)
> + return ret;
> +
> + ret = get_ssc_div_ext_f(clkm, &ext_f);
> + if (ret)
> + return ret;
> +
> + return parent_rate / 4 * (val + 2) + (parent_rate / 4 * ext_f) / 8192;
> +}
> +
> +static int clk_pll_mmc_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
> +{
Should there be a check for a parent rate of zero before the division is
done?
> + u32 val = DIV_ROUND_CLOSEST(req->rate * 4, req->best_parent_rate);
> +
> + req->rate = req->best_parent_rate * val / 4;
> + return 0;
> +}
> +
> +static int clk_pll_mmc_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate)
> +{
> + struct clk_pll_mmc *clkm = to_clk_pll_mmc(hw);
> + u32 val = PLL_MMC_SSC_DIV_N_VAL;
> + int ret;
> +
> + ret = regmap_update_bits(clkm->clkr.regmap,
> + clkm->ssc_dig_ofs + PLL_SSC_DIG_EMMC1_OFFSET,
> + PLL_FLAG_INITAL_EMMC_MASK, 0x0 << PLL_FLAG_INITAL_EMMC_SHIFT);
> + if (ret)
> + return ret;
> +
> + ret = set_ssc_div_n(clkm, val);
> + if (ret)
> + return ret;
> +
> + ret = set_ssc_div_ext_f(clkm, 1517);
> + if (ret)
> + return ret;
> +
> + switch (val) {
> + case 31 ... 46:
> + ret |= set_pi_ibselh(clkm, 3);
> + ret |= set_sscpll_rs(clkm, 3);
> + ret |= set_sscpll_icp(clkm, 2);
> + break;
> +
> + case 20 ... 30:
> + ret |= set_pi_ibselh(clkm, 2);
> + ret |= set_sscpll_rs(clkm, 3);
> + ret |= set_sscpll_icp(clkm, 1);
> + break;
> +
> + case 10 ... 19:
> + ret |= set_pi_ibselh(clkm, 1);
> + ret |= set_sscpll_rs(clkm, 2);
> + ret |= set_sscpll_icp(clkm, 1);
> + break;
> +
> + case 5 ... 9:
> + ret |= set_pi_ibselh(clkm, 0);
> + ret |= set_sscpll_rs(clkm, 2);
> + ret |= set_sscpll_icp(clkm, 0);
> + break;
> + }
> + if (ret)
> + return ret;
> +
> + ret = regmap_update_bits(clkm->clkr.regmap,
> + clkm->ssc_dig_ofs + PLL_SSC_DIG_EMMC3_OFFSET,
> + PLL_NCODE_SSC_EMMC_MASK,
> + 27 << PLL_NCODE_SSC_EMMC_SHIFT);
> + if (ret)
> + return ret;
> +
> + ret = regmap_update_bits(clkm->clkr.regmap,
> + clkm->ssc_dig_ofs + PLL_SSC_DIG_EMMC3_OFFSET,
> + PLL_FCODE_SSC_EMMC_MASK, 321);
> + if (ret)
> + return ret;
> +
> + ret = regmap_update_bits(clkm->clkr.regmap,
> + clkm->ssc_dig_ofs + PLL_SSC_DIG_EMMC4_OFFSET,
> + PLL_GRAN_EST_EM_MC_MASK, 5985);
> + if (ret)
> + return ret;
> +
> + ret = regmap_update_bits(clkm->clkr.regmap,
> + clkm->ssc_dig_ofs + PLL_SSC_DIG_EMMC1_OFFSET,
> + PLL_EN_SSC_EMMC_MASK, 0x1);
> + if (ret)
> + return ret;
> +
> + ret = regmap_update_bits(clkm->clkr.regmap,
> + clkm->ssc_dig_ofs + PLL_SSC_DIG_EMMC1_OFFSET,
> + PLL_EN_SSC_EMMC_MASK, 0x0);
> + if (ret)
> + return ret;
> +
> + ret = regmap_update_bits(clkm->clkr.regmap,
> + clkm->ssc_dig_ofs + PLL_SSC_DIG_EMMC1_OFFSET,
> + PLL_FLAG_INITAL_EMMC_MASK,
> + 0x1 << PLL_FLAG_INITAL_EMMC_SHIFT);
It looks like the rate and parent rate are not used in this function.
Will this always end up with the same rate when everything is
successful?
Brian
^ permalink raw reply
* Re: [PATCH v6 06/10] clk: realtek: Add support for mux clock
From: Brian Masney @ 2026-04-03 14:54 UTC (permalink / raw)
To: Yu-Chun Lin
Cc: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou, devicetree, linux-clk, linux-kernel,
linux-arm-kernel, linux-realtek-soc, james.tai, cy.huang,
stanley_chang
In-Reply-To: <20260402073957.2742459-7-eleanor.lin@realtek.com>
Hi Yu-Chun and Cheng-Yu,
On Thu, Apr 02, 2026 at 03:39:53PM +0800, Yu-Chun Lin wrote:
> From: Cheng-Yu Lee <cylee12@realtek.com>
>
> Add a simple regmap-based clk_ops implementation for Realtek mux clocks.
>
> The implementation supports parent selection and rate determination through
> regmap-backed register access.
>
> Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
> Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
> Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
> ---
> Changes in v6:
> - Add the headers used in c file to follow the "Include What You Use" principle.
> ---
> drivers/clk/realtek/Makefile | 1 +
> drivers/clk/realtek/clk-regmap-mux.c | 48 ++++++++++++++++++++++++++++
> drivers/clk/realtek/clk-regmap-mux.h | 43 +++++++++++++++++++++++++
> 3 files changed, 92 insertions(+)
> create mode 100644 drivers/clk/realtek/clk-regmap-mux.c
> create mode 100644 drivers/clk/realtek/clk-regmap-mux.h
>
> diff --git a/drivers/clk/realtek/Makefile b/drivers/clk/realtek/Makefile
> index 74375f8127ac..f90dc57fcfdb 100644
> --- a/drivers/clk/realtek/Makefile
> +++ b/drivers/clk/realtek/Makefile
> @@ -5,4 +5,5 @@ clk-rtk-y += common.o
>
> clk-rtk-y += clk-pll.o
> clk-rtk-y += clk-regmap-gate.o
> +clk-rtk-y += clk-regmap-mux.o
> clk-rtk-y += freq_table.o
> diff --git a/drivers/clk/realtek/clk-regmap-mux.c b/drivers/clk/realtek/clk-regmap-mux.c
> new file mode 100644
> index 000000000000..068b056d61f0
> --- /dev/null
> +++ b/drivers/clk/realtek/clk-regmap-mux.c
> @@ -0,0 +1,48 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2017 Realtek Semiconductor Corporation
> + * Author: Cheng-Yu Lee <cylee12@realtek.com>
> + */
> +
> +#include <linux/regmap.h>
> +#include <linux/clk-provider.h>
Sort the includes.
> +#include "clk-regmap-mux.h"
> +
> +static u8 clk_regmap_mux_get_parent(struct clk_hw *hw)
> +{
> + struct clk_regmap_mux *clkm = to_clk_regmap_mux(hw);
> + int num_parents = clk_hw_get_num_parents(hw);
> + u32 val;
> + int ret;
> +
> + ret = regmap_read(clkm->clkr.regmap, clkm->mux_ofs, &val);
> + if (ret)
> + return 0;
This is another case where it'd be nice to get the get_parent
declaration fixed. Stephen recently linked to some work of his from 2022
here.
https://lore.kernel.org/linux-clk/177431305509.5403.15386021337517970667@lazor/
There's nothing for you to do right now.
> +
> + val = val >> clkm->shift & clkm->mask;
I know there's the order of operations, however for clarity I would just
include some () here to make it clear the expected order.
> +
> + if (val >= num_parents)
Remove newline before if.
> + return 0;
> +
> + return val;
Or you could just use a ternary operator:
return val >= num_parents ? 0 : val;
> +}
> +
> +static int clk_regmap_mux_set_parent(struct clk_hw *hw, u8 index)
> +{
> + struct clk_regmap_mux *clkm = to_clk_regmap_mux(hw);
> +
> + return regmap_update_bits(clkm->clkr.regmap, clkm->mux_ofs,
> + clkm->mask << clkm->shift, index << clkm->shift);
> +}
> +
> +const struct clk_ops rtk_clk_regmap_mux_ops = {
> + .set_parent = clk_regmap_mux_set_parent,
> + .get_parent = clk_regmap_mux_get_parent,
> + .determine_rate = __clk_mux_determine_rate,
> +};
> +EXPORT_SYMBOL_NS_GPL(rtk_clk_regmap_mux_ops, "REALTEK_CLK");
> +
> +const struct clk_ops rtk_clk_regmap_mux_ro_ops = {
> + .get_parent = clk_regmap_mux_get_parent,
> +};
> +EXPORT_SYMBOL_NS_GPL(rtk_clk_regmap_mux_ro_ops, "REALTEK_CLK");
rtk_clk_regmap_mux_ro_ops is exported, however the declaration is not actually
declared in any header files.
Brian
^ permalink raw reply
* Re: [PATCH v3 1/2] mailbox: Use per-thread completion to fix wrong completion order
From: Joonwon Kang @ 2026-04-03 14:51 UTC (permalink / raw)
To: jassisinghbrar
Cc: angelogioacchino.delregno, jonathanh, joonwonkang,
linux-arm-kernel, linux-kernel, linux-mediatek, linux-tegra,
matthias.bgg, stable, thierry.reding
In-Reply-To: <CABb+yY0ub51k-eFpPfgARXtwYjWzRSjbPDLtoMD77YQR8JH+=Q@mail.gmail.com>
> On Thu, Apr 2, 2026 at 12:07 PM Joonwon Kang <joonwonkang@google.com> wrote:
> >
> > Previously, a sender thread in mbox_send_message() could be woken up at
> > a wrong time in blocking mode. It is because there was only a single
> > completion for a channel whereas messages from multiple threads could be
> > sent in any order; since the shared completion could be signalled in any
> > order, it could wake up a wrong sender thread.
> >
> > This commit resolves the false wake-up issue with the following changes:
> > - Completions are created just as many as the number of concurrent sender
> > threads
> > - A completion is created on a sender thread's stack
> > - Each slot of the message queue, i.e. `msg_data`, contains a pointer to
> > its target completion
> > - tx_tick() signals the completion of the currently active slot of the
> > message queue
> >
> I think I reviewed it already or is this happening on
> one-channel-one-client usage? Because mailbox api does not support
> channels shared among multiple clients.
Yes, this patch is handling the one-channel-one-client usage but when that
single channel is shared between multiple threads. From my understanding, the
discussion back then ended with how to circumvent the issue rather than whether
we will eventually solve this in the mailbox framework or not, and if yes, how
we will, and if not, why. I think it should still be resolved in the framework
for the reasons in the cover letter. Could you help to give a second review
with regards to those aspects?
Thanks,
Joonwon Kang
^ permalink raw reply
* Re: [PATCH v6 04/10] clk: realtek: Add support for phase locked loops (PLLs)
From: Brian Masney @ 2026-04-03 14:44 UTC (permalink / raw)
To: Yu-Chun Lin
Cc: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou, devicetree, linux-clk, linux-kernel,
linux-arm-kernel, linux-realtek-soc, james.tai, cy.huang,
stanley_chang
In-Reply-To: <20260402073957.2742459-5-eleanor.lin@realtek.com>
Hi Cheng-Yu and Yu-Chun,
On Thu, Apr 02, 2026 at 03:39:51PM +0800, Yu-Chun Lin wrote:
> From: Cheng-Yu Lee <cylee12@realtek.com>
>
> Provide a full set of PLL operations for programmable PLLs and a read-only
> variant for fixed or hardware-managed PLLs.
>
> Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
> Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
> Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
> ---
> +static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long parent_rate)
> +{
> + struct clk_pll *clkp = to_clk_pll(hw);
> + const struct freq_table *fv;
> + int ret;
> +
> + fv = ftbl_find_by_rate(clkp->freq_tbl, rate);
> + if (!fv || fv->rate != rate)
> + return -EINVAL;
> +
> + if (clkp->seq_pre_set_freq) {
> + ret = regmap_multi_reg_write(clkp->clkr.regmap, clkp->seq_pre_set_freq,
> + clkp->num_seq_pre_set_freq);
> + if (ret)
> + return ret;
> + }
> +
> + ret = regmap_update_bits(clkp->clkr.regmap, clkp->freq_reg,
> + clkp->freq_mask, fv->val);
> + if (ret)
> + return ret;
> +
> + if (clkp->seq_post_set_freq) {
> + ret = regmap_multi_reg_write(clkp->clkr.regmap, clkp->seq_post_set_freq,
> + clkp->num_seq_post_set_freq);
> + if (ret)
> + return ret;
> + }
> +
> + if (is_power_on(clkp)) {
> + ret = wait_freq_ready(clkp);
I should have checked Sashiko before I hit send on my last review.
https://sashiko.dev/#/patchset/20260402073957.2742459-1-eleanor.lin%40realtek.com
It suggested the following:
In the Common Clock Framework, .set_rate executes under the prepare_lock
mutex, while .enable and .disable execute under the enable_lock spinlock.
Could an interleaved clk_pll_enable() corrupt the hardware state by running
its seq_power_on sequence concurrently with these multi-step register
updates?
There also appears to be a potential race condition later in this function:
if (is_power_on(clkp)) {
ret = wait_freq_ready(clkp);
...
}
If .disable() powers off the PLL right before wait_freq_ready() is called,
will wait_freq_ready() poll a disabled PLL and erroneously return
-ETIMEDOUT? Is a private spinlock needed to serialize these operations?
Brian
^ permalink raw reply
* Re: [PATCH] arm64: dts: ti: k3-j721e-main: Update delay select values for MMC1/2 subsystems
From: Romain Naour @ 2026-04-03 14:42 UTC (permalink / raw)
To: Moteen Shah, devicetree, linux-arm-kernel, linux-omap
Cc: conor+dt, krzk+dt, robh, kristo, vigneshr, nm, stable
In-Reply-To: <8d4a2839-5b1e-479e-a462-dbbc3d016020@ti.com>
Hello Moteen, All,
Le 31/03/2026 à 14:19, Moteen Shah a écrit :
> Hey Romain,
>
> Thanks for the patch
>
> On 19/02/26 02:08, Romain Naour wrote:
>> The previous SPRSP36J datasheet recommends to set ti,otap-del-sel-sd-hs
>> value to 0 for MMC1 and MMC2 interfaces. These values were updated in
>> kernel 6.5. As a result we have some occasional regression with ultra
>> high speed DDR50 SDXC cards while mounting the rootfs:
>
> This error shouldn't be limited to just DDR50, were you seeing similar behavior
> with other speed modes?
I have a followup patch to enable back the SDR104 support with j721e SoC (SR 1.1
and 2.0) and I noticed the same behavior with some "specific" SDcards.
The J721e SR 1.0 doesn't support SDR104 due to an errata.
Nowadays, even the TI J721e EVM board revA (reference board) uses a SR1.1 SoC.
See the post on TI forum with further analysis:
https://e2e.ti.com/support/processors-group/processors/f/processors-forum/1626659/dra829j-q1-mmcsd-ultra-high-speed-uhs-modes-issues
It turnout that the kernel is not able to detect UHS speed with some SDcards
vendors after uboot initialized them with UHS speed (SDR104). I'm not sure why.
Still, the datasheet was updated with a new set of timing values for HS and
legacy speed.
Maybe I should remove the part about SD card initialization issues, it may be
related to another issue.
>>
>> mmc1: error -110 whilst initialising SD card
>>
>> A similar issue may occur with u-boot after a reboot while
>> initialising the SD card:
>>
>> mmc_init: -110, time 67
>>
>> Update the delay values for legacy and high speed modes, based on
>> the latest revised datasheet SPRSP36K released in April 2024 [1].
>>
>> (MMC1/2 - SD/SDIO Interface): Updated/Changed the
>> "OTAPDLYENA, DELAY ENABLE" and "OTAPDLYSEL, DELAY VALUE" for the
>> Default Speed and High Speed modes from "0x0" to "0x1"
>>
>> [1] Table 6-86. MMC1/2 DLL Delay Mapping for All Timing Modes, in
>> https://www.ti.com/lit/ds/symlink/tda4vm.pdf,
>> (SPRSP36K – SEPTEMBER 2021 – REVISED APRIL 2024)
>>
>> Cc: stable@vger.kernel.org # 6.5+
>> Fixes: af398252d68e ("arm64: dts: ti: k3-j721e-main: Update delay select
>> values for MMC subsystems")
>> Signed-off-by: Romain Naour <romain.naour@smile.fr>
>> ---
>> arch/arm64/boot/dts/ti/k3-j721e-main.dtsi | 8 ++++----
>> 1 file changed, 4 insertions(+), 4 deletions(-)
>>
>> diff --git a/arch/arm64/boot/dts/ti/k3-j721e-main.dtsi b/arch/arm64/boot/dts/
>> ti/k3-j721e-main.dtsi
>> index d5fd30a01032..418e6010ef1f 100644
>> --- a/arch/arm64/boot/dts/ti/k3-j721e-main.dtsi
>> +++ b/arch/arm64/boot/dts/ti/k3-j721e-main.dtsi
>> @@ -1643,8 +1643,8 @@ main_sdhci1: mmc@4fb0000 {
>> clocks = <&k3_clks 92 5>, <&k3_clks 92 0>;
>> assigned-clocks = <&k3_clks 92 0>;
>> assigned-clock-parents = <&k3_clks 92 1>;
>> - ti,otap-del-sel-legacy = <0x0>;
>> - ti,otap-del-sel-sd-hs = <0x0>;
>> + ti,otap-del-sel-legacy = <0x1>;
>> + ti,otap-del-sel-sd-hs = <0x1>;
>> ti,otap-del-sel-sdr12 = <0xf>;
>> ti,otap-del-sel-sdr25 = <0xf>;
>> ti,otap-del-sel-sdr50 = <0xc>;
>> @@ -1671,8 +1671,8 @@ main_sdhci2: mmc@4f98000 {
>> clocks = <&k3_clks 93 5>, <&k3_clks 93 0>;
>> assigned-clocks = <&k3_clks 93 0>;
>> assigned-clock-parents = <&k3_clks 93 1>;
>> - ti,otap-del-sel-legacy = <0x0>;
>> - ti,otap-del-sel-sd-hs = <0x0>;
>> + ti,otap-del-sel-legacy = <0x1>;
>> + ti,otap-del-sel-sd-hs = <0x1>;
>> ti,otap-del-sel-sdr12 = <0xf>;
>> ti,otap-del-sel-sdr25 = <0xf>;
>> ti,otap-del-sel-sdr50 = <0xc>;
>
>
> Reviewed-by: Moteen Shah <m-shah@ti.com>
Thanks!
Best regards,
Romain
>
> Regards,
> Moteen
>
^ permalink raw reply
* Re: [PATCH] Subject: ASoC stm32_sai: fix incorrect BCLK polarity for DSP_A/B, LEFT_J
From: Mark Brown @ 2026-04-03 14:41 UTC (permalink / raw)
To: Tomasz Merta
Cc: alsa-devel@alsa-project.org, olivier.moysan@foss.st.com,
arnaud.pouliquen@foss.st.com, lgirdwood@gmail.com, perex@perex.cz,
tiwai@suse.com, mcoquelin.stm32@gmail.com,
alexandre.torgue@foss.st.com,
linux-stm32@st-md-mailman.stormreply.com,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org
In-Reply-To: <SA1PR04MB8467CE79DCA58DF65FD9A798935EA@SA1PR04MB8467.namprd04.prod.outlook.com>
[-- Attachment #1: Type: text/plain, Size: 989 bytes --]
On Fri, Apr 03, 2026 at 02:07:52PM +0000, Tomasz Merta wrote:
> From 553c09cfa84fa801fbd8dcd5c9ae96e94a54ee31 Mon Sep 17 00:00:00 2001
>
> Tomasz Merta
> Software Engineer
> E: Tomasz.Merta@arrow.com
> Arrow Electronics | arrow.com
> From: Tomasz Merta <tomasz.merta@arrow.com>
> Date: Fri, 3 Apr 2026 10:33:11 +0200
> Subject: [PATCH] Subject: ASoC stm32_sai: fix incorrect BCLK polarity for
> DSP_A/B, LEFT_J
>
> The STM32 SAI driver do not set the clock strobing bit (CKSTR) for DSP_A,
> DSP_B and LEFT_J formats, causing data to be sampled on the wrong BCLK
> edge when SND_SOC_DAIFMT_NB_NF is used.
Your mail is pretty mangled here, there's a HTML part and the patch
appears to be appended after a signature, it looks like all the tabs got
converted into spaces too. If you're having trouble sending b4's web
submission API:
https://b4.docs.kernel.org/en/latest/contributor/send.html
bypasses your mail infrastructure which tends to make life easier.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [PATCH 1/1] dt-bindings: timer: fsl,imxgpt: add compatible string fsl,imx25-epit
From: Daniel Lezcano @ 2026-04-03 14:40 UTC (permalink / raw)
To: Mark Brown
Cc: Frank Li, Daniel Lezcano, Thomas Gleixner, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam,
open list:CLOCKSOURCE, CLOCKEVENT DRIVERS,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
open list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
Stephen Rothwell
In-Reply-To: <3ba9e33e-8d58-469c-9c21-5861c0f6c5eb@sirena.org.uk>
On 4/3/26 13:43, Mark Brown wrote:
> On Fri, Apr 03, 2026 at 10:15:49AM +0200, Daniel Lezcano wrote:
>> On 4/3/26 10:00, Frank Li wrote:
>
>>> Can't find it at linux-next master branch, anything wrong!
>
>> The patch is in timer/next but may be linux-next disabled my branch
>
> I have a timers/drivers/next branch in your git tree in -next but
> no record of anything else. That branch was last updated on January
> 20th. If you want something else adding let me know.
No it is ok, I had a confusion in my branches. I updated the wrong one.
Now it is fixed.
Thanks
^ permalink raw reply
* Re: [PATCH v6 05/10] clk: realtek: Add support for gate clock
From: Brian Masney @ 2026-04-03 14:40 UTC (permalink / raw)
To: Yu-Chun Lin
Cc: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou, devicetree, linux-clk, linux-kernel,
linux-arm-kernel, linux-realtek-soc, james.tai, cy.huang,
stanley_chang
In-Reply-To: <20260402073957.2742459-6-eleanor.lin@realtek.com>
Hi Cheng-Yu,
On Thu, Apr 02, 2026 at 03:39:52PM +0800, Yu-Chun Lin wrote:
> From: Cheng-Yu Lee <cylee12@realtek.com>
>
> Introduce clk_regmap_gate_ops supporting enable, disable, is_enabled, and
> disable_unused for standard regmap gate clocks.
disable_unused is not implemented below.
>
> Add clk_regmap_gate_ro_ops as a read-only variant exposing only is_enabled.
>
> Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
> Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
> Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
> ---
> Changes in v6:
> - Add the headers used in c file to follow the "Include What You Use" principle.
> ---
> drivers/clk/realtek/Makefile | 2 +
> drivers/clk/realtek/clk-regmap-gate.c | 69 +++++++++++++++++++++++++++
> drivers/clk/realtek/clk-regmap-gate.h | 65 +++++++++++++++++++++++++
> 3 files changed, 136 insertions(+)
> create mode 100644 drivers/clk/realtek/clk-regmap-gate.c
> create mode 100644 drivers/clk/realtek/clk-regmap-gate.h
>
> diff --git a/drivers/clk/realtek/Makefile b/drivers/clk/realtek/Makefile
> index a89ad77993e9..74375f8127ac 100644
> --- a/drivers/clk/realtek/Makefile
> +++ b/drivers/clk/realtek/Makefile
> @@ -2,5 +2,7 @@
> obj-$(CONFIG_RTK_CLK_COMMON) += clk-rtk.o
>
> clk-rtk-y += common.o
> +
> clk-rtk-y += clk-pll.o
> +clk-rtk-y += clk-regmap-gate.o
> clk-rtk-y += freq_table.o
> diff --git a/drivers/clk/realtek/clk-regmap-gate.c b/drivers/clk/realtek/clk-regmap-gate.c
> new file mode 100644
> index 000000000000..8738d6c6f8dd
> --- /dev/null
> +++ b/drivers/clk/realtek/clk-regmap-gate.c
> @@ -0,0 +1,69 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2017 Realtek Semiconductor Corporation
> + * Author: Cheng-Yu Lee <cylee12@realtek.com>
> + */
> +
> +#include <linux/regmap.h>
> +#include <linux/bits.h>
> +#include "clk-regmap-gate.h"
> +#include <linux/clk-provider.h>
linux/clk-provider.h needs to be included before clk-regmap-gate.h.
Also Sashiko reports that linux/export.h should also be included.
https://sashiko.dev/#/patchset/20260402073957.2742459-1-eleanor.lin%40realtek.com
Brian
^ permalink raw reply
* Re: [PATCH v6 04/10] clk: realtek: Add support for phase locked loops (PLLs)
From: Brian Masney @ 2026-04-03 14:34 UTC (permalink / raw)
To: Yu-Chun Lin
Cc: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou, devicetree, linux-clk, linux-kernel,
linux-arm-kernel, linux-realtek-soc, james.tai, cy.huang,
stanley_chang
In-Reply-To: <20260402073957.2742459-5-eleanor.lin@realtek.com>
Hi Cheng-Yu,
On Thu, Apr 02, 2026 at 03:39:51PM +0800, Yu-Chun Lin wrote:
> From: Cheng-Yu Lee <cylee12@realtek.com>
>
> Provide a full set of PLL operations for programmable PLLs and a read-only
> variant for fixed or hardware-managed PLLs.
>
> Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
> Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
> Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
> ---
> Changes in v6:
> - Add the headers used in c file to follow the "Include What You Use" principle.
> - Move to_clk_pll() from clk-pll.h to clk-pll.c to limit its scope.
> ---
> drivers/clk/realtek/Makefile | 2 +
> drivers/clk/realtek/clk-pll.c | 164 +++++++++++++++++++++++++++++++
> drivers/clk/realtek/clk-pll.h | 42 ++++++++
> drivers/clk/realtek/freq_table.c | 36 +++++++
> drivers/clk/realtek/freq_table.h | 21 ++++
> 5 files changed, 265 insertions(+)
> create mode 100644 drivers/clk/realtek/clk-pll.c
> create mode 100644 drivers/clk/realtek/clk-pll.h
> create mode 100644 drivers/clk/realtek/freq_table.c
> create mode 100644 drivers/clk/realtek/freq_table.h
>
> diff --git a/drivers/clk/realtek/Makefile b/drivers/clk/realtek/Makefile
> index 377ec776ee47..a89ad77993e9 100644
> --- a/drivers/clk/realtek/Makefile
> +++ b/drivers/clk/realtek/Makefile
> @@ -2,3 +2,5 @@
> obj-$(CONFIG_RTK_CLK_COMMON) += clk-rtk.o
>
> clk-rtk-y += common.o
> +clk-rtk-y += clk-pll.o
> +clk-rtk-y += freq_table.o
> diff --git a/drivers/clk/realtek/clk-pll.c b/drivers/clk/realtek/clk-pll.c
> new file mode 100644
> index 000000000000..44730b22a94c
> --- /dev/null
> +++ b/drivers/clk/realtek/clk-pll.c
> @@ -0,0 +1,164 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2024 Realtek Semiconductor Corporation
> + * Author: Cheng-Yu Lee <cylee12@realtek.com>
> + */
> +
> +#include <linux/regmap.h>
> +#include "clk-pll.h"
> +
> +#define TIMEOUT 2000
> +
> +static inline struct clk_pll *to_clk_pll(struct clk_hw *hw)
> +{
> + struct clk_regmap *clkr = to_clk_regmap(hw);
> +
> + return container_of(clkr, struct clk_pll, clkr);
> +}
> +
> +static int wait_freq_ready(struct clk_pll *clkp)
> +{
> + u32 pollval;
> +
> + if (!clkp->freq_ready_valid)
> + return 0;
> +
> + return regmap_read_poll_timeout_atomic(clkp->clkr.regmap, clkp->freq_ready_reg, pollval,
> + (pollval & clkp->freq_ready_mask)
> + == clkp->freq_ready_val, 0, TIMEOUT);
I would put the "(pollval & clkp->freq_ready_mask) == clkp->freq_ready_val" on
the same line to improve readability. You can go out to 100 characters.
Also should the delay be greater than 0 to avoid tons of constant
retries?
> +}
> +
> +static bool is_power_on(struct clk_pll *clkp)
> +{
> + u32 val;
> +
> + if (!clkp->power_reg)
> + return true;
> +
> + if (regmap_read(clkp->clkr.regmap, clkp->power_reg, &val))
> + return true;
Is the intention if there is an error, then it marks it as success?
> +
> + return (val & clkp->power_mask) == clkp->power_val_on;
> +}
> +
> +static void clk_pll_disable(struct clk_hw *hw)
> +{
> + struct clk_pll *clkp = to_clk_pll(hw);
> +
> + if (!clkp->seq_power_off)
> + return;
> +
> + regmap_multi_reg_write(clkp->clkr.regmap, clkp->seq_power_off,
> + clkp->num_seq_power_off);
> +}
> +
> +static int clk_pll_is_enabled(struct clk_hw *hw)
> +{
> + struct clk_pll *clkp = to_clk_pll(hw);
> +
> + return is_power_on(clkp);
> +}
> +
> +static int clk_pll_determine_rate(struct clk_hw *hw,
> + struct clk_rate_request *req)
> +{
> + struct clk_pll *clkp = to_clk_pll(hw);
> + const struct freq_table *ftblv = NULL;
> +
> + ftblv = ftbl_find_by_rate(clkp->freq_tbl, req->rate);
> + if (!ftblv)
> + return -EINVAL;
> +
> + req->rate = ftblv->rate;
> + return 0;
Add newline before return.
> +}
> +
> +static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + struct clk_pll *clkp = to_clk_pll(hw);
> + const struct freq_table *fv;
> + u32 freq_val;
> +
> + if (regmap_read(clkp->clkr.regmap, clkp->freq_reg, &freq_val))
> + return 0;
> +
> + freq_val &= clkp->freq_mask;
> +
> + fv = ftbl_find_by_val_with_mask(clkp->freq_tbl, clkp->freq_mask,
> + freq_val);
> + return fv ? fv->rate : 0;
Add newline before return.
> +}
> +
> +static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long parent_rate)
> +{
> + struct clk_pll *clkp = to_clk_pll(hw);
> + const struct freq_table *fv;
> + int ret;
> +
> + fv = ftbl_find_by_rate(clkp->freq_tbl, rate);
> + if (!fv || fv->rate != rate)
> + return -EINVAL;
> +
> + if (clkp->seq_pre_set_freq) {
> + ret = regmap_multi_reg_write(clkp->clkr.regmap, clkp->seq_pre_set_freq,
> + clkp->num_seq_pre_set_freq);
> + if (ret)
> + return ret;
> + }
> +
> + ret = regmap_update_bits(clkp->clkr.regmap, clkp->freq_reg,
> + clkp->freq_mask, fv->val);
> + if (ret)
> + return ret;
> +
> + if (clkp->seq_post_set_freq) {
> + ret = regmap_multi_reg_write(clkp->clkr.regmap, clkp->seq_post_set_freq,
> + clkp->num_seq_post_set_freq);
> + if (ret)
> + return ret;
> + }
> +
> + if (is_power_on(clkp)) {
> + ret = wait_freq_ready(clkp);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int clk_pll_enable(struct clk_hw *hw)
> +{
> + struct clk_pll *clkp = to_clk_pll(hw);
> + int ret;
> +
> + if (!clkp->seq_power_on)
> + return 0;
> +
> + if (is_power_on(clkp))
> + return 0;
> +
> + ret = regmap_multi_reg_write(clkp->clkr.regmap, clkp->seq_power_on,
> + clkp->num_seq_power_on);
> + if (ret)
> + return ret;
> +
> + return wait_freq_ready(clkp);
> +}
> +
> +const struct clk_ops rtk_clk_pll_ops = {
> + .enable = clk_pll_enable,
> + .disable = clk_pll_disable,
> + .is_enabled = clk_pll_is_enabled,
> + .recalc_rate = clk_pll_recalc_rate,
> + .determine_rate = clk_pll_determine_rate,
> + .set_rate = clk_pll_set_rate,
> +};
> +EXPORT_SYMBOL_NS_GPL(rtk_clk_pll_ops, "REALTEK_CLK");
> +
> +const struct clk_ops rtk_clk_pll_ro_ops = {
> + .recalc_rate = clk_pll_recalc_rate,
> +};
> +EXPORT_SYMBOL_NS_GPL(rtk_clk_pll_ro_ops, "REALTEK_CLK");
> diff --git a/drivers/clk/realtek/clk-pll.h b/drivers/clk/realtek/clk-pll.h
> new file mode 100644
> index 000000000000..00884585a242
> --- /dev/null
> +++ b/drivers/clk/realtek/clk-pll.h
> @@ -0,0 +1,42 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) 2017-2019 Realtek Semiconductor Corporation
> + * Author: Cheng-Yu Lee <cylee12@realtek.com>
> + */
> +
> +#ifndef __CLK_REALTEK_CLK_PLL_H
> +#define __CLK_REALTEK_CLK_PLL_H
> +
> +#include "common.h"
> +#include "freq_table.h"
> +
> +struct reg_sequence;
> +
> +struct clk_pll {
> + struct clk_regmap clkr;
> + const struct reg_sequence *seq_power_on;
> + u32 num_seq_power_on;
> + const struct reg_sequence *seq_power_off;
> + u32 num_seq_power_off;
> + const struct reg_sequence *seq_pre_set_freq;
> + u32 num_seq_pre_set_freq;
> + const struct reg_sequence *seq_post_set_freq;
> + u32 num_seq_post_set_freq;
> + const struct freq_table *freq_tbl;
> + u32 freq_reg;
> + u32 freq_mask;
> + u32 freq_ready_valid;
> + u32 freq_ready_mask;
> + u32 freq_ready_reg;
> + u32 freq_ready_val;
> + u32 power_reg;
> + u32 power_mask;
> + u32 power_val_on;
> +};
> +
> +#define __clk_pll_hw(_ptr) __clk_regmap_hw(&(_ptr)->clkr)
> +
> +extern const struct clk_ops rtk_clk_pll_ops;
> +extern const struct clk_ops rtk_clk_pll_ro_ops;
> +
> +#endif /* __CLK_REALTEK_CLK_PLL_H */
> diff --git a/drivers/clk/realtek/freq_table.c b/drivers/clk/realtek/freq_table.c
> new file mode 100644
> index 000000000000..272a10e75a54
> --- /dev/null
> +++ b/drivers/clk/realtek/freq_table.c
> @@ -0,0 +1,36 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +#include <linux/bitops.h>
> +#include "freq_table.h"
> +
> +const struct freq_table *ftbl_find_by_rate(const struct freq_table *ftbl,
> + unsigned long rate)
> +{
> + unsigned long best_rate = 0;
> + const struct freq_table *best = NULL;
Put variables in reverse Christmas tree order.
> +
> + for (; !IS_FREQ_TABLE_END(ftbl); ftbl++) {
> + if (ftbl->rate == rate)
> + return ftbl;
> +
> + if (ftbl->rate > rate)
> + continue;
> +
> + if (ftbl->rate > best_rate) {
> + best_rate = ftbl->rate;
> + best = ftbl;
> + }
> + }
> +
> + return best;
> +}
> +
> +const struct freq_table *
> +ftbl_find_by_val_with_mask(const struct freq_table *ftbl, u32 mask, u32 value)
> +{
> + for (; !IS_FREQ_TABLE_END(ftbl); ftbl++) {
> + if ((ftbl->val & mask) == (value & mask))
> + return ftbl;
> + }
> + return NULL;
> +};
> diff --git a/drivers/clk/realtek/freq_table.h b/drivers/clk/realtek/freq_table.h
> new file mode 100644
> index 000000000000..6d9116651105
> --- /dev/null
> +++ b/drivers/clk/realtek/freq_table.h
> @@ -0,0 +1,21 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +
> +struct freq_table {
> + u32 val;
> + unsigned long rate;
> +};
> +
> +/* ofs check */
> +#define CLK_OFS_INVALID -1
> +#define CLK_OFS_IS_VALID(_ofs) ((_ofs) != CLK_OFS_INVALID)
Is this used anywhere?
Brian
> +
> +#define FREQ_TABLE_END \
> + { \
> + .rate = 0 \
> + }
> +#define IS_FREQ_TABLE_END(_f) ((_f)->rate == 0)
> +
> +const struct freq_table *ftbl_find_by_rate(const struct freq_table *ftbl,
> + unsigned long rate);
> +const struct freq_table *
> +ftbl_find_by_val_with_mask(const struct freq_table *ftbl, u32 mask, u32 value);
> --
> 2.34.1
>
^ permalink raw reply
* Re: [PATCH] ARM: mvebu: validate memory node device_type strings
From: Andrew Lunn @ 2026-04-03 14:31 UTC (permalink / raw)
To: Pengpeng Hou
Cc: Gregory Clement, Sebastian Hesselbarth, linux-arm-kernel,
linux-kernel
In-Reply-To: <20260403151504.4-dt-arm-mvebu-resend-pengpeng@iscas.ac.cn>
On Fri, Apr 03, 2026 at 10:42:46AM +0800, Pengpeng Hou wrote:
> mvebu_scan_mem() fetches the raw device_type property and immediately
> compares it with strcmp(). Flat DT properties are external boot input,
> and this path does not prove that the property is NUL-terminated within
> its declared bounds.
>
> Use fdt_stringlist_get() so malformed unterminated device_type
> properties are rejected before they are used as C strings.
>
> Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
> ---
> arch/arm/mach-mvebu/board-v7.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm/mach-mvebu/board-v7.c b/arch/arm/mach-mvebu/board-v7.c
> index a0740ab0dca9..f867f6233108 100644
> --- a/arch/arm/mach-mvebu/board-v7.c
> +++ b/arch/arm/mach-mvebu/board-v7.c
> @@ -11,6 +11,7 @@
>
> #include <linux/kernel.h>
> #include <linux/init.h>
> +#include <linux/libfdt.h>
> #include <linux/of_address.h>
> #include <linux/of_fdt.h>
> #include <linux/io.h>
> @@ -66,7 +67,8 @@ void __iomem *mvebu_get_scu_base(void)
> static int __init mvebu_scan_mem(unsigned long node, const char *uname,
> int depth, void *data)
> {
> - const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
> + const char *type = fdt_stringlist_get(initial_boot_params, node,
> + "device_type", 0, NULL);
> const __be32 *reg, *endp;
> int l;
Did you read the comment?
/*
* When returning from suspend, the platform goes through the
* bootloader, which executes its DDR3 training code. This code has
* the unfortunate idea of using the first 10 KB of each DRAM bank to
* exercise the RAM and calculate the optimal timings. Therefore, this
* area of RAM is overwritten, and shouldn't be used by the kernel if
* suspend/resume is supported.
*/
You probably want to make use of:
* -FDT_ERR_BADVALUE if the property value is not NUL-terminated
* -FDT_ERR_NOTFOUND if the property does not exist
Andrew
^ permalink raw reply
* Re: [PATCH bpf-next v12 2/5] bpf: Pass bpf_verifier_env to JIT
From: bot+bpf-ci @ 2026-04-03 14:30 UTC (permalink / raw)
To: xukuohai, bpf, linux-kernel, linux-arm-kernel
Cc: ast, daniel, andrii, martin.lau, eddyz87, yonghong.song, puranjay,
a.s.protopopov, alexis.lothore, list+bpf, linux, yangtiezhu,
hengqi.chen, johan.almbladh, paulburton, hbathini, chleroy,
naveen, luke.r.nels, xi.wang, bjorn, pulehui, iii, hca, gor,
davem, udknight, ast, andrii, daniel, martin.lau, eddyz87,
yonghong.song, clm, ihor.solodrai
In-Reply-To: <20260403132811.753894-3-xukuohai@huaweicloud.com>
[-- Attachment #1: Type: text/plain, Size: 2348 bytes --]
> diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
> index cc61fe57b98d..093ab0f68c81 100644
> --- a/kernel/bpf/core.c
> +++ b/kernel/bpf/core.c
[ ... ]
> @@ -2551,47 +2528,63 @@ static bool bpf_prog_select_interpreter(struct bpf_prog *fp)
> return select_interpreter;
> }
>
> -static struct bpf_prog *bpf_prog_jit_compile(struct bpf_prog *prog)
> +static struct bpf_prog *bpf_prog_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)
> {
> #ifdef CONFIG_BPF_JIT
> bool blinded = false;
> struct bpf_prog *orig_prog = prog;
> + struct bpf_insn_aux_data *orig_insn_aux;
>
> if (bpf_prog_need_blind(orig_prog)) {
> + if (env) {
> + /* If env is not NULL, we are called from the end of bpf_check(), at this
> + * point, only insn_aux_data is used after failure, so we only restore it
> + * here.
> + */
> + orig_insn_aux = bpf_dup_insn_aux_data(env);
> + if (!orig_insn_aux)
> + return orig_prog;
> + }
> + prog = bpf_jit_blind_constants(env, orig_prog);
[ ... ]
> + return prog;
> +
> +out_restore:
> + bpf_restore_insn_aux_data(env, orig_insn_aux);
> +out_free:
> + kvfree(orig_insn_aux);
> #endif
> return prog;
> }
When the out_restore label is reached (blinding or JIT failure with
env != NULL), bpf_restore_insn_aux_data() sets env->insn_aux_data to
point to orig_insn_aux:
kernel/bpf/verifier.c:bpf_restore_insn_aux_data() {
vfree(env->insn_aux_data);
env->insn_aux_data = orig_insn_aux;
}
The code then falls through to the out_free label, which calls
kvfree(orig_insn_aux). This frees the memory that env->insn_aux_data
now points to, leaving it as a dangling pointer.
Since this function is called from inside bpf_check() via
__bpf_prog_select_runtime(), the subsequent cleanup in bpf_check()
accesses the freed memory:
kernel/bpf/verifier.c:bpf_check() {
...
clear_insn_aux_data(env, 0, env->prog->len);
vfree(env->insn_aux_data);
}
Could the out_restore path return directly instead of falling through
to out_free, since after the restore env now owns orig_insn_aux and
bpf_check() will free it during normal cleanup?
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/23948799625
^ permalink raw reply
* Re: [PATCH bpf-next v12 1/5] bpf: Move constants blinding out of arch-specific JITs
From: bot+bpf-ci @ 2026-04-03 14:30 UTC (permalink / raw)
To: xukuohai, bpf, linux-kernel, linux-arm-kernel
Cc: ast, daniel, andrii, martin.lau, eddyz87, yonghong.song, puranjay,
a.s.protopopov, alexis.lothore, list+bpf, linux, yangtiezhu,
hengqi.chen, johan.almbladh, paulburton, hbathini, chleroy,
naveen, luke.r.nels, xi.wang, bjorn, pulehui, iii, hca, gor,
davem, udknight, ast, andrii, daniel, martin.lau, eddyz87,
yonghong.song, clm, ihor.solodrai
In-Reply-To: <20260403132811.753894-2-xukuohai@huaweicloud.com>
[-- Attachment #1: Type: text/plain, Size: 1947 bytes --]
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index a431b7d50e1b..66cef3744fde 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
[ ... ]
> +static void restore_insn_aux_data(struct bpf_verifier_env *env,
> + struct bpf_insn_aux_data *orig_insn_aux)
> +{
> + /* the expanded elements are zero-filled, so no special handling is required */
> + vfree(env->insn_aux_data);
> + env->insn_aux_data = orig_insn_aux;
> +}
[ ... ]
> +out_restore:
> + restore_subprog_starts(env, orig_subprog_starts);
> + restore_insn_aux_data(env, orig_insn_aux);
> + kvfree(orig_subprog_starts);
> +out_free_aux:
> + kvfree(orig_insn_aux);
> +out_cleanup:
> + /* cleanup main prog to be interpreted */
> + prog->jit_requested = 0;
> + prog->blinding_requested = 0;
> + return err;
Is there a double-free of env->insn_aux_data on the out_restore path?
restore_insn_aux_data() sets env->insn_aux_data = orig_insn_aux, then
out_restore falls through to out_free_aux which does
kvfree(orig_insn_aux), freeing the just-restored pointer.
Later, bpf_check() cleanup runs:
clear_insn_aux_data(env, 0, env->prog->len);
vfree(env->insn_aux_data);
which would be a use-after-free followed by a double-free.
The out_restore path is reached when bpf_jit_blind_constants() or
__jit_subprogs() fails while blinding was active, which can happen
under memory pressure with multi-subprog BPF programs.
It looks like out_restore needs a goto out_cleanup (or similar) after
kvfree(orig_subprog_starts) to skip the out_free_aux label, since after
restoring, orig_insn_aux is owned by env->insn_aux_data and must not be
freed here.
This regression is not fixed by later commits in the series.
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/23948799625
^ permalink raw reply
* [PATCH 3/3] arm64: dts: broadcom: rp1: Add PWM node
From: Andrea della Porta @ 2026-04-03 14:31 UTC (permalink / raw)
To: Uwe Kleine-König, linux-pwm, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Florian Fainelli,
Broadcom internal kernel review list, Andrea della Porta,
devicetree, linux-rpi-kernel, linux-arm-kernel, linux-kernel,
Naushir Patuck, Stanimir Varbanov
In-Reply-To: <cover.1775223441.git.andrea.porta@suse.com>
From: Stanimir Varbanov <svarbanov@suse.de>
The RP1 chipset used on the Raspberry Pi 5 features an integrated
PWM controller to drive the cooling fan.
Add the corresponding DT node for this PWM controller.
Signed-off-by: Stanimir Varbanov <svarbanov@suse.de>
Co-developed-by: Andrea della Porta <andrea.porta@suse.com>
Signed-off-by: Andrea della Porta <andrea.porta@suse.com>
---
arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts | 12 ++++++++++++
arch/arm64/boot/dts/broadcom/rp1-common.dtsi | 10 ++++++++++
2 files changed, 22 insertions(+)
diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts
index 2856082814462..a4e5ba23bf536 100644
--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts
+++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts
@@ -64,12 +64,24 @@ phy1: ethernet-phy@1 {
};
&rp1_gpio {
+ fan_pwm_default_state: fan-pwm-default-state {
+ function = "pwm1";
+ pins = "gpio45";
+ bias-pull-down;
+ };
+
usb_vbus_default_state: usb-vbus-default-state {
function = "vbus1";
groups = "vbus1";
};
};
+&rp1_pwm {
+ pinctrl-0 = <&fan_pwm_default_state>;
+ pinctrl-names = "default";
+ status = "okay";
+};
+
&rp1_usb0 {
pinctrl-0 = <&usb_vbus_default_state>;
pinctrl-names = "default";
diff --git a/arch/arm64/boot/dts/broadcom/rp1-common.dtsi b/arch/arm64/boot/dts/broadcom/rp1-common.dtsi
index 5a815c3797945..7e78501e62b0c 100644
--- a/arch/arm64/boot/dts/broadcom/rp1-common.dtsi
+++ b/arch/arm64/boot/dts/broadcom/rp1-common.dtsi
@@ -56,6 +56,16 @@ rp1_eth: ethernet@40100000 {
#size-cells = <0>;
};
+ rp1_pwm: pwm@4009c000 {
+ compatible = "raspberrypi,rp1-pwm";
+ reg = <0x00 0x4009c000 0x0 0x100>;
+ clocks = <&rp1_clocks RP1_CLK_PWM1>;
+ assigned-clocks = <&rp1_clocks RP1_CLK_PWM1>;
+ assigned-clock-rates = <50000000>;
+ #pwm-cells = <3>;
+ status = "disabled";
+ };
+
rp1_usb0: usb@40200000 {
compatible = "snps,dwc3";
reg = <0x00 0x40200000 0x0 0x100000>;
--
2.35.3
^ permalink raw reply related
* [PATCH 2/3] pwm: rp1: Add RP1 PWM controller driver
From: Andrea della Porta @ 2026-04-03 14:31 UTC (permalink / raw)
To: Uwe Kleine-König, linux-pwm, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Florian Fainelli,
Broadcom internal kernel review list, Andrea della Porta,
devicetree, linux-rpi-kernel, linux-arm-kernel, linux-kernel,
Naushir Patuck, Stanimir Varbanov
In-Reply-To: <cover.1775223441.git.andrea.porta@suse.com>
From: Naushir Patuck <naush@raspberrypi.com>
The Raspberry Pi RP1 southbridge features an embedded PWM
controller with 4 output channels, alongside an RPM interface
to read the fan speed on the Raspberry Pi 5.
Add the supporting driver.
Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Co-developed-by: Stanimir Varbanov <svarbanov@suse.de>
Signed-off-by: Stanimir Varbanov <svarbanov@suse.de>
Signed-off-by: Andrea della Porta <andrea.porta@suse.com>
---
drivers/pwm/Kconfig | 10 ++
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-rp1.c | 244 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 255 insertions(+)
create mode 100644 drivers/pwm/pwm-rp1.c
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 6f3147518376a..22e4fc6385da2 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -625,6 +625,16 @@ config PWM_ROCKCHIP
Generic PWM framework driver for the PWM controller found on
Rockchip SoCs.
+config PWM_RP1
+ tristate "RP1 PWM support"
+ depends on MISC_RP1 || COMPILE_TEST
+ depends on HWMON
+ help
+ PWM framework driver for Raspberry Pi RP1 controller
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-rp1.
+
config PWM_SAMSUNG
tristate "Samsung PWM support"
depends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 0dc0d2b69025d..895a7c42fe9c0 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -56,6 +56,7 @@ obj-$(CONFIG_PWM_RENESAS_RZG2L_GPT) += pwm-rzg2l-gpt.o
obj-$(CONFIG_PWM_RENESAS_RZ_MTU3) += pwm-rz-mtu3.o
obj-$(CONFIG_PWM_RENESAS_TPU) += pwm-renesas-tpu.o
obj-$(CONFIG_PWM_ROCKCHIP) += pwm-rockchip.o
+obj-$(CONFIG_PWM_RP1) += pwm-rp1.o
obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o
obj-$(CONFIG_PWM_SIFIVE) += pwm-sifive.o
obj-$(CONFIG_PWM_SL28CPLD) += pwm-sl28cpld.o
diff --git a/drivers/pwm/pwm-rp1.c b/drivers/pwm/pwm-rp1.c
new file mode 100644
index 0000000000000..0a1c1c1dd27e9
--- /dev/null
+++ b/drivers/pwm/pwm-rp1.c
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * pwm-rp1.c
+ *
+ * Raspberry Pi RP1 PWM.
+ *
+ * Copyright © 2026 Raspberry Pi Ltd.
+ *
+ * Author: Naushir Patuck (naush@raspberrypi.com)
+ *
+ * Based on the pwm-bcm2835 driver by:
+ * Bart Tanghe <bart.tanghe@thomasmore.be>
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+
+#define PWM_GLOBAL_CTRL 0x000
+#define PWM_CHANNEL_CTRL(x) (0x014 + ((x) * 0x10))
+#define PWM_RANGE(x) (0x018 + ((x) * 0x10))
+#define PWM_PHASE(x) (0x01C + ((x) * 0x10))
+#define PWM_DUTY(x) (0x020 + ((x) * 0x10))
+
+/* 8:FIFO_POP_MASK + 0:Trailing edge M/S modulation */
+#define PWM_CHANNEL_DEFAULT (BIT(8) + BIT(0))
+#define PWM_CHANNEL_ENABLE(x) BIT(x)
+#define PWM_POLARITY BIT(3)
+#define SET_UPDATE BIT(31)
+#define PWM_MODE_MASK GENMASK(1, 0)
+
+#define NUM_PWMS 4
+
+struct rp1_pwm {
+ void __iomem *base;
+ struct clk *clk;
+};
+
+static const struct hwmon_channel_info * const rp1_fan_hwmon_info[] = {
+ HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT),
+ NULL
+};
+
+static umode_t rp1_fan_hwmon_is_visible(const void *data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ umode_t mode = 0;
+
+ if (type == hwmon_fan && attr == hwmon_fan_input)
+ mode = 0444;
+
+ return mode;
+}
+
+static int rp1_fan_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct rp1_pwm *rp1 = dev_get_drvdata(dev);
+
+ if (type != hwmon_fan || attr != hwmon_fan_input)
+ return -EOPNOTSUPP;
+
+ *val = readl(rp1->base + PWM_PHASE(2));
+
+ return 0;
+}
+
+static const struct hwmon_ops rp1_fan_hwmon_ops = {
+ .is_visible = rp1_fan_hwmon_is_visible,
+ .read = rp1_fan_hwmon_read,
+};
+
+static const struct hwmon_chip_info rp1_fan_hwmon_chip_info = {
+ .ops = &rp1_fan_hwmon_ops,
+ .info = rp1_fan_hwmon_info,
+};
+
+static void rp1_pwm_apply_config(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
+ u32 value;
+
+ value = readl(rp1->base + PWM_GLOBAL_CTRL);
+ value |= SET_UPDATE;
+ writel(value, rp1->base + PWM_GLOBAL_CTRL);
+}
+
+static int rp1_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
+
+ writel(PWM_CHANNEL_DEFAULT, rp1->base + PWM_CHANNEL_CTRL(pwm->hwpwm));
+ return 0;
+}
+
+static void rp1_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
+ u32 value;
+
+ value = readl(rp1->base + PWM_CHANNEL_CTRL(pwm->hwpwm));
+ value &= ~PWM_MODE_MASK;
+ writel(value, rp1->base + PWM_CHANNEL_CTRL(pwm->hwpwm));
+
+ rp1_pwm_apply_config(chip, pwm);
+}
+
+static int rp1_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
+ unsigned long clk_rate = clk_get_rate(rp1->clk);
+ unsigned long clk_period;
+ u32 value;
+
+ if (!clk_rate) {
+ dev_err(&chip->dev, "failed to get clock rate\n");
+ return -EINVAL;
+ }
+
+ /* set period and duty cycle */
+ clk_period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, clk_rate);
+
+ writel(DIV_ROUND_CLOSEST(state->duty_cycle, clk_period),
+ rp1->base + PWM_DUTY(pwm->hwpwm));
+
+ writel(DIV_ROUND_CLOSEST(state->period, clk_period),
+ rp1->base + PWM_RANGE(pwm->hwpwm));
+
+ /* set polarity */
+ value = readl(rp1->base + PWM_CHANNEL_CTRL(pwm->hwpwm));
+ if (state->polarity == PWM_POLARITY_NORMAL)
+ value &= ~PWM_POLARITY;
+ else
+ value |= PWM_POLARITY;
+ writel(value, rp1->base + PWM_CHANNEL_CTRL(pwm->hwpwm));
+
+ /* enable/disable */
+ value = readl(rp1->base + PWM_GLOBAL_CTRL);
+ if (state->enabled)
+ value |= PWM_CHANNEL_ENABLE(pwm->hwpwm);
+ else
+ value &= ~PWM_CHANNEL_ENABLE(pwm->hwpwm);
+ writel(value, rp1->base + PWM_GLOBAL_CTRL);
+
+ rp1_pwm_apply_config(chip, pwm);
+
+ return 0;
+}
+
+static const struct pwm_ops rp1_pwm_ops = {
+ .request = rp1_pwm_request,
+ .free = rp1_pwm_free,
+ .apply = rp1_pwm_apply,
+};
+
+static int rp1_pwm_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device *hwmon_dev;
+ struct pwm_chip *chip;
+ struct rp1_pwm *rp1;
+ int ret;
+
+ chip = devm_pwmchip_alloc(dev, NUM_PWMS, sizeof(*rp1));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+
+ rp1 = pwmchip_get_drvdata(chip);
+
+ rp1->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(rp1->base))
+ return PTR_ERR(rp1->base);
+
+ rp1->clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(rp1->clk))
+ return dev_err_probe(dev, PTR_ERR(rp1->clk), "clock not found\n");
+
+ ret = devm_clk_rate_exclusive_get(dev, rp1->clk);
+ if (ret)
+ return dev_err_probe(dev, ret, "fail to get exclusive rate\n");
+
+ chip->ops = &rp1_pwm_ops;
+
+ platform_set_drvdata(pdev, chip);
+
+ ret = devm_pwmchip_add(dev, chip);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register PWM chip\n");
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, "rp1_fan_tach", rp1,
+ &rp1_fan_hwmon_chip_info,
+ NULL);
+
+ if (IS_ERR(hwmon_dev))
+ return dev_err_probe(dev, PTR_ERR(hwmon_dev),
+ "failed to register hwmon fan device\n");
+
+ return 0;
+}
+
+static int rp1_pwm_suspend(struct device *dev)
+{
+ struct rp1_pwm *rp1 = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(rp1->clk);
+
+ return 0;
+}
+
+static int rp1_pwm_resume(struct device *dev)
+{
+ struct rp1_pwm *rp1 = dev_get_drvdata(dev);
+
+ return clk_prepare_enable(rp1->clk);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(rp1_pwm_pm_ops, rp1_pwm_suspend, rp1_pwm_resume);
+
+static const struct of_device_id rp1_pwm_of_match[] = {
+ { .compatible = "raspberrypi,rp1-pwm" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rp1_pwm_of_match);
+
+static struct platform_driver rp1_pwm_driver = {
+ .probe = rp1_pwm_probe,
+ .driver = {
+ .name = "rp1-pwm",
+ .of_match_table = rp1_pwm_of_match,
+ .pm = pm_ptr(&rp1_pwm_pm_ops),
+ },
+};
+module_platform_driver(rp1_pwm_driver);
+
+MODULE_DESCRIPTION("RP1 PWM driver");
+MODULE_AUTHOR("Naushir Patuck <naush@raspberrypi.com>");
+MODULE_LICENSE("GPL");
--
2.35.3
^ permalink raw reply related
* [PATCH 1/3] dt-bindings: pwm: Add Raspberry Pi RP1 PWM controller
From: Andrea della Porta @ 2026-04-03 14:31 UTC (permalink / raw)
To: Uwe Kleine-König, linux-pwm, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Florian Fainelli,
Broadcom internal kernel review list, Andrea della Porta,
devicetree, linux-rpi-kernel, linux-arm-kernel, linux-kernel,
Naushir Patuck, Stanimir Varbanov
In-Reply-To: <cover.1775223441.git.andrea.porta@suse.com>
From: Naushir Patuck <naush@raspberrypi.com>
Add the devicetree binding documentation for the PWM
controller found in the Raspberry Pi RP1 chipset.
Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Co-developed-by: Stanimir Varbanov <svarbanov@suse.de>
Signed-off-by: Stanimir Varbanov <svarbanov@suse.de>
Signed-off-by: Andrea della Porta <andrea.porta@suse.com>
---
.../bindings/pwm/raspberrypi,rp1-pwm.yaml | 52 +++++++++++++++++++
1 file changed, 52 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pwm/raspberrypi,rp1-pwm.yaml
diff --git a/Documentation/devicetree/bindings/pwm/raspberrypi,rp1-pwm.yaml b/Documentation/devicetree/bindings/pwm/raspberrypi,rp1-pwm.yaml
new file mode 100644
index 0000000000000..d6b3f52561636
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/raspberrypi,rp1-pwm.yaml
@@ -0,0 +1,52 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pwm/raspberrypi,rp1-pwm.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Raspberry Pi RP1 PWM controller
+
+maintainers:
+ - Naushir Patuck <naush@raspberrypi.com>
+
+allOf:
+ - $ref: pwm.yaml#
+
+description: |
+ The PWM peripheral is a flexible waveform generator with a
+ variety of operational modes. It has the following features:
+ - four independent output channels
+ - 32-bit counter widths
+ - Seven output generation modes
+ - Optional per-channel output inversion
+ - Optional duty-cycle data FIFO with DMA support
+ - Optional sigma-delta noise shaping engine
+
+properties:
+ compatible:
+ const: raspberrypi,rp1-pwm
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ "#pwm-cells":
+ const: 3
+
+required:
+ - compatible
+ - reg
+ - clocks
+
+additionalProperties: false
+
+examples:
+ - |
+ pwm@98000 {
+ compatible = "raspberrypi,rp1-pwm";
+ reg = <0x98000 0x100>;
+ clocks = <&rp1_clocks 17>;
+ #pwm-cells = <3>;
+ };
--
2.35.3
^ permalink raw reply related
* [PATCH 0/3] Add RP1 PWM controller support
From: Andrea della Porta @ 2026-04-03 14:31 UTC (permalink / raw)
To: Uwe Kleine-König, linux-pwm, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Florian Fainelli,
Broadcom internal kernel review list, Andrea della Porta,
devicetree, linux-rpi-kernel, linux-arm-kernel, linux-kernel,
Naushir Patuck, Stanimir Varbanov
This patchset adds support for the PWM controller found on the
Raspberry Pi RP1 southbridge. This is necessary to operate the
cooling fan connected to one of the PWM channels.
The tachometer pin for the fan speed is managed by the firmware
running on the RP1's M-core. It uses the PHASE2 register
to report the RPM, which is then exported by this driver as a
hwmon device.
Subsequent patches will add the CPU thermal zone, which acts as
a consumer of the PWM device.
Best regards,
Andrea
Naushir Patuck (2):
dt-bindings: pwm: Add Raspberry Pi RP1 PWM controller
pwm: rp1: Add RP1 PWM controller driver
Stanimir Varbanov (1):
arm64: dts: broadcom: rp1: Add PWM node
.../bindings/pwm/raspberrypi,rp1-pwm.yaml | 52 ++++
.../boot/dts/broadcom/bcm2712-rpi-5-b.dts | 12 +
arch/arm64/boot/dts/broadcom/rp1-common.dtsi | 10 +
drivers/pwm/Kconfig | 10 +
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-rp1.c | 244 ++++++++++++++++++
6 files changed, 329 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pwm/raspberrypi,rp1-pwm.yaml
create mode 100644 drivers/pwm/pwm-rp1.c
--
2.35.3
^ permalink raw reply
* Re: [PATCH v6 03/10] clk: realtek: Introduce a common probe()
From: Brian Masney @ 2026-04-03 14:21 UTC (permalink / raw)
To: Yu-Chun Lin
Cc: mturquette, sboyd, robh, krzk+dt, conor+dt, p.zabel, cylee12,
afaerber, jyanchou, devicetree, linux-clk, linux-kernel,
linux-arm-kernel, linux-realtek-soc, james.tai, cy.huang,
stanley_chang
In-Reply-To: <20260402073957.2742459-4-eleanor.lin@realtek.com>
Hi Cheng-Yu,
On Thu, Apr 02, 2026 at 03:39:50PM +0800, Cheng-Yu Lee wrote:
> Add rtk_clk_probe() to set up the shared regmap, register clock hardware,
> and add the clock provider.
>
> Additionally, if the "#reset-cells" property is present in the device tree,
> it creates and registers an auxiliary device using the provided aux_name.
> This allows the dedicated reset driver to bind to this device, enabling
> both clock and reset drivers to share the same regmap.
>
> Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
> Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
> Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
> ---
> Changes in v6:
> - Replace direct reset controller initialization with auxiliary device creation.
> - Add aux_name parameter to rtk_clk_probe() to register the reset auxiliary device.
> - Simplify rtk_clk_desc because reset data is handled entirely by the auxiliary reset driver.
> - In Kconfig, change "depends on RESET_CONTROLLER" to "select RESET_CONTROLLER"
> - Remove unused includes headers and added <linux/auxiliary_bus.h>.
> ---
> MAINTAINERS | 1 +
> drivers/clk/Kconfig | 1 +
> drivers/clk/Makefile | 1 +
> drivers/clk/realtek/Kconfig | 28 +++++++++++++++
> drivers/clk/realtek/Makefile | 4 +++
> drivers/clk/realtek/common.c | 67 ++++++++++++++++++++++++++++++++++++
> drivers/clk/realtek/common.h | 37 ++++++++++++++++++++
> 7 files changed, 139 insertions(+)
> create mode 100644 drivers/clk/realtek/Kconfig
> create mode 100644 drivers/clk/realtek/Makefile
> create mode 100644 drivers/clk/realtek/common.c
> create mode 100644 drivers/clk/realtek/common.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 8f355896583b..8318156a02b5 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -22240,6 +22240,7 @@ L: devicetree@vger.kernel.org
> L: linux-clk@vger.kernel.org
> S: Supported
> F: Documentation/devicetree/bindings/clock/realtek*
> +F: drivers/clk/realtek/*
> F: drivers/reset/realtek/*
> F: include/dt-bindings/clock/realtek*
> F: include/dt-bindings/reset/realtek*
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index 3d803b4cf5c1..d60f6415b0a3 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -519,6 +519,7 @@ source "drivers/clk/nuvoton/Kconfig"
> source "drivers/clk/pistachio/Kconfig"
> source "drivers/clk/qcom/Kconfig"
> source "drivers/clk/ralink/Kconfig"
> +source "drivers/clk/realtek/Kconfig"
> source "drivers/clk/renesas/Kconfig"
> source "drivers/clk/rockchip/Kconfig"
> source "drivers/clk/samsung/Kconfig"
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index f7bce3951a30..69b84d1e7bcc 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -140,6 +140,7 @@ obj-$(CONFIG_COMMON_CLK_PISTACHIO) += pistachio/
> obj-$(CONFIG_COMMON_CLK_PXA) += pxa/
> obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/
> obj-y += ralink/
> +obj-$(CONFIG_COMMON_CLK_REALTEK) += realtek/
> obj-y += renesas/
> obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
> obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/
> diff --git a/drivers/clk/realtek/Kconfig b/drivers/clk/realtek/Kconfig
> new file mode 100644
> index 000000000000..bc47d3f1c452
> --- /dev/null
> +++ b/drivers/clk/realtek/Kconfig
> @@ -0,0 +1,28 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +config COMMON_CLK_REALTEK
> + bool "Clock driver for Realtek SoCs"
> + depends on ARCH_REALTEK || COMPILE_TEST
> + default ARCH_REALTEK
> + help
> + Enable the common clock framework infrastructure for Realtek
> + system-on-chip platforms.
> +
> + This provides the base support required by individual Realtek
> + clock controller drivers to expose clocks to peripheral devices.
> +
> + If you have a Realtek-based platform, say Y.
> +
> +if COMMON_CLK_REALTEK
> +
> +config RTK_CLK_COMMON
> + tristate "Realtek Clock Common"
> + select RESET_CONTROLLER
> + select RESET_RTK_COMMON
select AUXILIARY_BUS ?
> + help
> + Common helper code shared by Realtek clock controller drivers.
> +
> + This provides utility functions and data structures used by
> + multiple Realtek clock implementations, and include integration
> + with reset controllers where required.
> +
> +endif
> diff --git a/drivers/clk/realtek/Makefile b/drivers/clk/realtek/Makefile
> new file mode 100644
> index 000000000000..377ec776ee47
> --- /dev/null
> +++ b/drivers/clk/realtek/Makefile
> @@ -0,0 +1,4 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +obj-$(CONFIG_RTK_CLK_COMMON) += clk-rtk.o
> +
> +clk-rtk-y += common.o
> diff --git a/drivers/clk/realtek/common.c b/drivers/clk/realtek/common.c
> new file mode 100644
> index 000000000000..c5aea15a3714
> --- /dev/null
> +++ b/drivers/clk/realtek/common.c
> @@ -0,0 +1,67 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2019 Realtek Semiconductor Corporation
If you are making changes here, should the copyrights be updated to
include 2026?
> + * Author: Cheng-Yu Lee <cylee12@realtek.com>
> + */
> +
> +#include <linux/auxiliary_bus.h>
> +#include <linux/device.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include "common.h"
> +
> +static int rtk_reset_controller_register(struct device *dev, const char *aux_name)
> +{
> + struct auxiliary_device *adev;
> +
> + if (!of_property_present(dev->of_node, "#reset-cells"))
> + return 0;
> +
> + adev = devm_auxiliary_device_create(dev, aux_name, NULL);
> +
> + if (IS_ERR(adev))
> + return PTR_ERR(adev);
> + return 0;
Add newline before return.
> +}
> +
> +int rtk_clk_probe(struct platform_device *pdev, const struct rtk_clk_desc *desc,
> + const char *aux_name)
> +{
> + int i, ret;
> + struct regmap *regmap;
> + struct device *dev = &pdev->dev;
Put variables in reverse Christmas tree order.
> +
> + regmap = device_node_to_regmap(pdev->dev.of_node);
> + if (IS_ERR(regmap))
> + return dev_err_probe(dev, PTR_ERR(regmap), "failed to get regmap\n");
> +
> + for (i = 0; i < desc->num_clks; i++)
> + desc->clks[i]->regmap = regmap;
> +
> + for (i = 0; i < desc->clk_data->num; i++) {
> + struct clk_hw *hw = desc->clk_data->hws[i];
> +
> + if (!hw)
> + continue;
> +
> + ret = devm_clk_hw_register(dev, hw);
> +
> + if (ret) {
Remove newline before if.
> + dev_warn(dev, "failed to register hw of clk%d: %d\n", i,
> + ret);
> + desc->clk_data->hws[i] = NULL;
This chunk doesn't take into account probe deferrals.
Brian
^ permalink raw reply
* Re: [PATCH v8 08/10] ASoC: mediatek: mt8196: add platform driver
From: Mark Brown @ 2026-04-03 14:07 UTC (permalink / raw)
To: Cyril Chao
Cc: Liam Girdwood, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Matthias Brugger, AngeloGioacchino Del Regno, Jaroslav Kysela,
Takashi Iwai, linux-sound, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek,
Project_Global_Chrome_Upstream_Group, Darren Ye
In-Reply-To: <20260324015719.17543-9-Cyril.Chao@mediatek.com>
[-- Attachment #1: Type: text/plain, Size: 636 bytes --]
On Tue, Mar 24, 2026 at 09:56:49AM +0800, Cyril Chao wrote:
> +static int mt8196_set_cm(struct mtk_base_afe *afe, int id,
> + bool update, bool swap, unsigned int ch)
> +{
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> + unsigned int rate = afe_priv->cm_rate[id];
> + unsigned int rate_val = mt8196_rate_transform(afe->dev, rate);
> + unsigned int update_val = update ? ((((26000000 / rate) - 10) / (ch / 2)) - 1) : 0x64;
> + int reg = AFE_CM0_CON0 + 0x10 * id;
The driver looks like it supports mono so won't this trigger divide by
zero?
Also please write normal conditional statements, it's much more
leigible.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [PATCH] iommu/rockchip: fix page table allocation flags for v2 IOMMU
From: Midgy Balon @ 2026-04-03 14:02 UTC (permalink / raw)
To: Simon Xue
Cc: Jonas Karlman, iommu, joro, will, robin.murphy, Heiko Stuebner,
linux-arm-kernel, linux-rockchip, linux-kernel, stable
In-Reply-To: <5663593b-2c53-4632-ad2c-db9efa8e9ab2@rock-chips.com>
From: Midgy BALON <midgy971@gmail.com>
To: Simon Xue <xxm@rock-chips.com>
Cc: Jonas Karlman <jonas@kwiboo.se>, iommu@lists.linux.dev,
joro@8bytes.org, will@kernel.org, robin.murphy@arm.com,
heiko@sntech.de, linux-arm-kernel@lists.infradead.org,
linux-rockchip@lists.infradead.org, linux-kernel@vger.kernel.org,
stable@vger.kernel.org
In-Reply-To: <5663593b-2c53-4632-ad2c-db9efa8e9ab2@rock-chips.com>
References: <20260331075010.1463-1-midgy971@gmail.com>
<0f285782-b12a-4abd-bca7-b6c549bed59f@rock-chips.com>
<e622cc9e-8fb0-454a-b88e-dc13cf2ff507@kwiboo.se>
<89ed223d-1a2c-447d-9f21-76969e668855@rock-chips.com>
<5663593b-2c53-4632-ad2c-db9efa8e9ab2@rock-chips.com>
Subject: Re: [PATCH] iommu/rockchip: fix page table allocation flags
for v2 IOMMU
On 4/3/2026, Simon Xue wrote:
> We internally checked that the RK356x SoCs integrate two different
> IOMMU versions (v1.0 and v2.0), like NPU and ISP use the v1.0 IOMMU.
>
> Both versions can map 40-bit physical pages, but v1.0 does not support
> placing the first-level page table above 4 GB.
>
> To fix this, I think we need to land this patch first:
> https://lore.kernel.org/all/20260310105303.128859-1-xxm@rock-chips.com/
>
> Then on top of that, we can add a new compatible string to distinguish
> the IOMMU versions.
Thank you Simon and Jonas for the internal investigation. This explains
exactly what I observed.
To answer Simon's earlier question: the IP block hitting both failure
modes is the NPU IOMMU (rknpu_mmu, at 0xfde4b000), currently bound
to "rockchip,rk3568-iommu" in rk356x-base.dtsi. Both the downstream
rknpu driver and the upstream Rocket accel driver (drivers/accel/rocket/)
use this IOMMU.
The v1.0 first-level page table constraint explains both failure modes.
On boards with more than 4 GB of RAM the DTE table can be allocated
above 0x100000000, and the v1.0 hardware silently truncates or errors
on that address. The SWIOTLB bounce-buffer path is a consequence of
the same root cause: with DMA_BIT_MASK(32) on the NPU device, bounce
buffers land below 4 GB, phys_to_virt() of the bounce address is then
used as the PTE write target, and the subsequent
dma_sync_single_for_device(DMA_TO_DEVICE) overwrites those PTEs with
zeros from the original buffer.
Please consider my original patch withdrawn. Modifying iommu_data_ops_v2
was too broad and would have incorrectly constrained VOP2 MMU and all
other v2 IOMMU users.
I agree fully with the two-step approach. On top of your per-device-ops
patch [1], I plan to send:
[1/2] iommu/rockchip: Add "rockchip,rk3568-iommu-v1" compatible
for IOMMU v1.0 blocks (NPU, ISP/VICAP) on RK3568
— ops with .gfp_flags = GFP_DMA32,
.dma_bit_mask = DMA_BIT_MASK(40)
(v1.0 can still map 40-bit physical pages; only the DTE
table base must be below 4 GB)
[2/2] arm64: dts: rockchip: rk356x: Use "rockchip,rk3568-iommu-v1"
for rknpu_mmu (0xfde4b000) and vicap_mmu (0xfdfe0800)
One note on the SWIOTLB issue: with GFP_DMA32 in the new ops, page
table allocations never reach SWIOTLB, so the "track L2 base addresses"
approach you suggested should not be necessary — GFP_DMA32 prevents the
bounce-buffer poisoning at the source. Happy to be corrected if there
is another path where it is still needed.
I am happy to add Tested-by to your per-device-ops patch [1].
[1] https://lore.kernel.org/all/20260310105303.128859-1-xxm@rock-chips.com/
Regards,
Midgy BALON
Le ven. 3 avr. 2026 à 06:40, Simon Xue <xxm@rock-chips.com> a écrit :
>
>
> 在 2026/4/1 18:22, Simon Xue 写道:
> > Hi Jonas,
> >
> > 在 2026/4/1 16:41, Jonas Karlman 写道:
> >> Hi Simon,
> >>
> >> On 4/1/2026 9:48 AM, Simon wrote:
> >>> Hi Midgy,
> >>>
> >>> 在 2026/3/31 15:50, Midgy BALON 写道:
> >>>> commit 2a7e6400f72b ("iommu: rockchip: Allocate tables from all
> >>>> available memory for IOMMU v2") removed GFP_DMA32 from
> >>>> iommu_data_ops_v2, reasoning that RK356x and RK3588 IOMMU v2 hardware
> >>>> supports up to 40-bit physical addresses for page tables. However, the
> >>>> RK3568 IOMMU page-table walker uses a 32-bit AXI bus: it cannot access
> >>>> physical addresses above 4 GB regardless of the address encoding
> >>>> range.
> >>>>
> >>>> On boards with more than 4 GB of RAM (e.g. 8 GB LPDDR4X), removing
> >>>> GFP_DMA32 causes two distinct failure modes:
> >>>>
> >>>> 1. Direct allocation above 4 GB: iommu_alloc_pages_sz() may return
> >>>> memory above 0x100000000. The hardware page-table walker
> >>>> issues a
> >>>> bus error trying to dereference those addresses, causing an IOMMU
> >>>> fault on the first DMA transaction.
> >>> Which IP block is hitting this? We'd like to take a look on our end.
> >> I have seen reports that the NPU MMU on RK3568/RK3566 is having some
> >> issue using DTE/PTE with >32-bit addresses, maybe it uses a different
> >> MMU hw revision or has some hw errata?
> >>
> >> From my own testing at least the VOP2 MMU on RK3568 (and RK3588) was
> >> able to handle 40-bit addressable DTE/PTE, hence the original commit
> >> 2a7e6400f72b ("iommu: rockchip: Allocate tables from all available
> >> memory for IOMMU v2").
> >>
> >> As also mentioned in my reply at [1], maybe the NPU MMU has some hw
> >> limitation or errata and may need to use a different compatible.
> >
> > Yes, We are checking internally whether different IOMMU versions
> > integrated.
> >
> > I will share what we find once we have results.
> >
> We internally checked that the RK356x SoCs integrate two different IOMMU
> versions (v1.0 and v2.0), like NPU and ISP use the v1.0 IOMMU.
>
> Both versions can map 40-bit physical pages, but v1.0 does not support
> placing the first-level page table above 4 GB.
>
> To fix this, I think we need to land this patch first:
> https://lore.kernel.org/all/20260310105303.128859-1-xxm@rock-chips.com/
>
> Then on top of that, we can add a new compatible string to distinguish
> the IOMMU versions.
>
> >> [1]
> >> https://lore.kernel.org/r/3cd63b3d-1c5e-4a11-856e-c4aeb5d97d55@kwiboo.se/
> >>
> >> Regards,
> >> Jonas
> >>
> >>>> 2. SWIOTLB bounce-buffer poisoning: without GFP_DMA32, page tables
> >>>> land
> >>>> above the SWIOTLB window. dma_map_single() with DMA_BIT_MASK(32)
> >>>> then bounces them into a buffer below 4 GB.
> >>>> rk_dte_get_page_table()
> >>>> returns phys_to_virt() of the bounce buffer address; PTEs are
> >>>> written
> >>>> there; the next dma_sync_single_for_device(DMA_TO_DEVICE)
> >>>> copies the
> >>>> original (zero) data back over the bounce buffer, silently
> >>>> erasing the
> >>>> freshly written PTEs. The IOMMU faults because every PTE
> >>>> reads as zero.
> >>> This probably need a separate patch. One way to fix it would be to
> >>> track the
> >>> original L2 page table base addresses in struct rk_iommu_domain,
> >>> then have rk_dte_get_page_table() return the tracked address instead of
> >>> deriving it from the DTE.
> >>>
> >>>> Restore GFP_DMA32 (and DMA_BIT_MASK(32)) for iommu_data_ops_v2, which
> >>>> currently only serves "rockchip,rk3568-iommu" in mainline.
> >>>>
> >>>> Tested on Radxa ROCK 3B (RK3568, 8 GB LPDDR4X):
> >>>> - MobileNetV1 via RKNN: 5.8 ms/inference (IOMMU mode)
> >>>> - YOLOv5s 640x640 via RKNN: ~57 ms/inference (IOMMU mode)
> >>>> - No IOMMU faults, correct inference results
> >>>>
> >>>> Fixes: 2a7e6400f72b ("iommu: rockchip: Allocate tables from all
> >>>> available memory for IOMMU v2")
> >>>> Cc: stable@vger.kernel.org
> >>>> Cc: Jonas Karlman <jonas@kwiboo.se>
> >>>> Signed-off-by: Midgy BALON <midgy971@gmail.com>
> >>>> ---
> >>>> drivers/iommu/rockchip-iommu.c | 4 ++--
> >>>> 1 file changed, 2 insertions(+), 2 deletions(-)
> >>>>
> >>>> diff --git a/drivers/iommu/rockchip-iommu.c
> >>>> b/drivers/iommu/rockchip-iommu.c
> >>>> index 85f3667e797..8b45db29471 100644
> >>>> --- a/drivers/iommu/rockchip-iommu.c
> >>>> +++ b/drivers/iommu/rockchip-iommu.c
> >>>> @@ -1358,8 +1358,8 @@ static struct rk_iommu_ops iommu_data_ops_v2 = {
> >>>> .pt_address = &rk_dte_pt_address_v2,
> >>>> .mk_dtentries = &rk_mk_dte_v2,
> >>>> .mk_ptentries = &rk_mk_pte_v2,
> >>>> - .dma_bit_mask = DMA_BIT_MASK(40),
> >>>> - .gfp_flags = 0,
> >>>> + .dma_bit_mask = DMA_BIT_MASK(32),
> >>>> + .gfp_flags = GFP_DMA32,
> >>>> };
> >>>> static const struct of_device_id rk_iommu_dt_ids[] = {
> >>
^ permalink raw reply
* [PATCH bpf-next v12 1/5] bpf: Move constants blinding out of arch-specific JITs
From: Xu Kuohai @ 2026-04-03 13:28 UTC (permalink / raw)
To: bpf, linux-kernel, linux-arm-kernel
Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Martin KaFai Lau, Eduard Zingerman, Yonghong Song, Puranjay Mohan,
Anton Protopopov, Alexis Lothoré, Shahab Vahedi,
Russell King, Tiezhu Yang, Hengqi Chen, Johan Almbladh,
Paul Burton, Hari Bathini, Christophe Leroy, Naveen N Rao,
Luke Nelson, Xi Wang, Björn Töpel, Pu Lehui,
Ilya Leoshkevich, Heiko Carstens, Vasily Gorbik, David S . Miller,
Wang YanQing
In-Reply-To: <20260403132811.753894-1-xukuohai@huaweicloud.com>
From: Xu Kuohai <xukuohai@huawei.com>
During the JIT stage, constants blinding rewrites instructions but only
rewrites the private instruction copy of the JITed subprog, leaving the
global env->prog->insni and env->insn_aux_data untouched. This causes a
mismatch between subprog instructions and the global state, making it
difficult to use the global data in the JIT.
To avoid this mismatch, and given that all arch-specific JITs already
support constants blinding, move it to the generic verifier code, and
switch to rewrite the global env->prog->insnsi with the global states
adjusted, as other rewrites in the verifier do.
This removes the constants blinding calls in each JIT, which are largely
duplicated code across architectures.
Since constants blinding is only required for JIT, and there are two
JIT entry functions, jit_subprogs() for BPF programs with multiple
subprogs and bpf_prog_select_runtime() for programs with no subprogs,
move the constants blinding invocation into these two functions.
In the verifier path, bpf_patch_insn_data() is used to keep global
verifier auxiliary data in sync with patched instructions. A key
question is whether this global auxiliary data should be restored
on the failure path.
Besides instructions, bpf_patch_insn_data() adjusts:
- prog->aux->poke_tab
- env->insn_array_maps
- env->subprog_info
- env->insn_aux_data
For prog->aux->poke_tab, it is only used by JIT or only meaningful after
JIT succeeds, so it does not need to be restored on the failure path.
For env->insn_array_maps, when JIT fails, programs using insn arrays
are rejected by bpf_insn_array_ready() due to missing JIT addresses.
Hence, env->insn_array_maps is only meaningful for JIT and does not need
to be restored.
For subprog_info, if jit_subprogs fails and CONFIG_BPF_JIT_ALWAYS_ON
is not enabled, kernel falls back to interpreter. In this case,
env->subprog_info is used to determine subprogram stack depth. So it
must be restored on failure.
For env->insn_aux_data, it is freed by clean_insn_aux_data() at the
end of bpf_check(). Before freeing, clean_insn_aux_data() loops over
env->insn_aux_data to release jump targets recorded in it. The loop
uses env->prog->len as the array length, but this length no longer
matches the actual size of the adjusted env->insn_aux_data array after
constants blinding.
To address it, a simple approach is to keep insn_aux_data as adjusted
after failure, since it will be freed shortly, and record its actual size
for the loop in clean_insn_aux_data(). But since clean_insn_aux_data()
uses the same index to loop over both env->prog->insni and env->insn_aux_data,
this approach result in incorrect index for the insni array. So an
alternative approach is adopted: clone the original env->insn_aux_data
before blinding and restore it after failure, similar to env->prog.
For classic BPF programs, constants blinding works as before since it
is still invoked from bpf_prog_select_runtime().
Reviewed-by: Anton Protopopov <a.s.protopopov@gmail.com> # v8
Reviewed-by: Hari Bathini <hbathini@linux.ibm.com> # powerpc jit
Reviewed-by: Pu Lehui <pulehui@huawei.com> # riscv jit
Signed-off-by: Xu Kuohai <xukuohai@huawei.com>
---
arch/arc/net/bpf_jit_core.c | 39 ++++-----
arch/arm/net/bpf_jit_32.c | 41 ++--------
arch/arm64/net/bpf_jit_comp.c | 72 +++++-----------
arch/loongarch/net/bpf_jit.c | 59 ++++----------
arch/mips/net/bpf_jit_comp.c | 20 +----
arch/parisc/net/bpf_jit_core.c | 73 +++++++----------
arch/powerpc/net/bpf_jit_comp.c | 68 ++++++----------
arch/riscv/net/bpf_jit_core.c | 61 +++++---------
arch/s390/net/bpf_jit_comp.c | 59 +++++---------
arch/sparc/net/bpf_jit_comp_64.c | 61 +++++---------
arch/x86/net/bpf_jit_comp.c | 43 ++--------
arch/x86/net/bpf_jit_comp32.c | 33 +-------
include/linux/filter.h | 33 +++++++-
kernel/bpf/core.c | 67 +++++++++++++--
kernel/bpf/verifier.c | 136 +++++++++++++++++++++++++------
15 files changed, 390 insertions(+), 475 deletions(-)
diff --git a/arch/arc/net/bpf_jit_core.c b/arch/arc/net/bpf_jit_core.c
index 1421eeced0f5..973ceae48675 100644
--- a/arch/arc/net/bpf_jit_core.c
+++ b/arch/arc/net/bpf_jit_core.c
@@ -79,7 +79,6 @@ struct arc_jit_data {
* The JIT pertinent context that is used by different functions.
*
* prog: The current eBPF program being handled.
- * orig_prog: The original eBPF program before any possible change.
* jit: The JIT buffer and its length.
* bpf_header: The JITed program header. "jit.buf" points inside it.
* emit: If set, opcodes are written to memory; else, a dry-run.
@@ -94,12 +93,10 @@ struct arc_jit_data {
* need_extra_pass: A forecast if an "extra_pass" will occur.
* is_extra_pass: Indicates if the current pass is an extra pass.
* user_bpf_prog: True, if VM opcodes come from a real program.
- * blinded: True if "constant blinding" step returned a new "prog".
* success: Indicates if the whole JIT went OK.
*/
struct jit_context {
struct bpf_prog *prog;
- struct bpf_prog *orig_prog;
struct jit_buffer jit;
struct bpf_binary_header *bpf_header;
bool emit;
@@ -114,7 +111,6 @@ struct jit_context {
bool need_extra_pass;
bool is_extra_pass;
bool user_bpf_prog;
- bool blinded;
bool success;
};
@@ -161,13 +157,7 @@ static int jit_ctx_init(struct jit_context *ctx, struct bpf_prog *prog)
{
memset(ctx, 0, sizeof(*ctx));
- ctx->orig_prog = prog;
-
- /* If constant blinding was requested but failed, scram. */
- ctx->prog = bpf_jit_blind_constants(prog);
- if (IS_ERR(ctx->prog))
- return PTR_ERR(ctx->prog);
- ctx->blinded = (ctx->prog != ctx->orig_prog);
+ ctx->prog = prog;
/* If the verifier doesn't zero-extend, then we have to do it. */
ctx->do_zext = !ctx->prog->aux->verifier_zext;
@@ -214,14 +204,6 @@ static inline void maybe_free(struct jit_context *ctx, void **mem)
*/
static void jit_ctx_cleanup(struct jit_context *ctx)
{
- if (ctx->blinded) {
- /* if all went well, release the orig_prog. */
- if (ctx->success)
- bpf_jit_prog_release_other(ctx->prog, ctx->orig_prog);
- else
- bpf_jit_prog_release_other(ctx->orig_prog, ctx->prog);
- }
-
maybe_free(ctx, (void **)&ctx->bpf2insn);
maybe_free(ctx, (void **)&ctx->jit_data);
@@ -229,12 +211,19 @@ static void jit_ctx_cleanup(struct jit_context *ctx)
ctx->bpf2insn_valid = false;
/* Freeing "bpf_header" is enough. "jit.buf" is a sub-array of it. */
- if (!ctx->success && ctx->bpf_header) {
- bpf_jit_binary_free(ctx->bpf_header);
- ctx->bpf_header = NULL;
- ctx->jit.buf = NULL;
- ctx->jit.index = 0;
- ctx->jit.len = 0;
+ if (!ctx->success) {
+ if (ctx->bpf_header) {
+ bpf_jit_binary_free(ctx->bpf_header);
+ ctx->bpf_header = NULL;
+ ctx->jit.buf = NULL;
+ ctx->jit.index = 0;
+ ctx->jit.len = 0;
+ }
+ if (ctx->is_extra_pass) {
+ ctx->prog->bpf_func = NULL;
+ ctx->prog->jited = 0;
+ ctx->prog->jited_len = 0;
+ }
}
ctx->emit = false;
diff --git a/arch/arm/net/bpf_jit_32.c b/arch/arm/net/bpf_jit_32.c
index deeb8f292454..e6b1bb2de627 100644
--- a/arch/arm/net/bpf_jit_32.c
+++ b/arch/arm/net/bpf_jit_32.c
@@ -2144,9 +2144,7 @@ bool bpf_jit_needs_zext(void)
struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
{
- struct bpf_prog *tmp, *orig_prog = prog;
struct bpf_binary_header *header;
- bool tmp_blinded = false;
struct jit_ctx ctx;
unsigned int tmp_idx;
unsigned int image_size;
@@ -2156,20 +2154,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
* the interpreter.
*/
if (!prog->jit_requested)
- return orig_prog;
-
- /* If constant blinding was enabled and we failed during blinding
- * then we must fall back to the interpreter. Otherwise, we save
- * the new JITed code.
- */
- tmp = bpf_jit_blind_constants(prog);
-
- if (IS_ERR(tmp))
- return orig_prog;
- if (tmp != prog) {
- tmp_blinded = true;
- prog = tmp;
- }
+ return prog;
memset(&ctx, 0, sizeof(ctx));
ctx.prog = prog;
@@ -2179,10 +2164,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
* we must fall back to the interpreter
*/
ctx.offsets = kcalloc(prog->len, sizeof(int), GFP_KERNEL);
- if (ctx.offsets == NULL) {
- prog = orig_prog;
- goto out;
- }
+ if (ctx.offsets == NULL)
+ return prog;
/* 1) fake pass to find in the length of the JITed code,
* to compute ctx->offsets and other context variables
@@ -2194,10 +2177,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
* being successful in the second pass, so just fall back
* to the interpreter.
*/
- if (build_body(&ctx)) {
- prog = orig_prog;
+ if (build_body(&ctx))
goto out_off;
- }
tmp_idx = ctx.idx;
build_prologue(&ctx);
@@ -2213,10 +2194,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
ctx.idx += ctx.imm_count;
if (ctx.imm_count) {
ctx.imms = kcalloc(ctx.imm_count, sizeof(u32), GFP_KERNEL);
- if (ctx.imms == NULL) {
- prog = orig_prog;
+ if (ctx.imms == NULL)
goto out_off;
- }
}
#else
/* there's nothing about the epilogue on ARMv7 */
@@ -2238,10 +2217,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
/* Not able to allocate memory for the structure then
* we must fall back to the interpretation
*/
- if (header == NULL) {
- prog = orig_prog;
+ if (header == NULL)
goto out_imms;
- }
/* 2.) Actual pass to generate final JIT code */
ctx.target = (u32 *) image_ptr;
@@ -2278,16 +2255,12 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
#endif
out_off:
kfree(ctx.offsets);
-out:
- if (tmp_blinded)
- bpf_jit_prog_release_other(prog, prog == orig_prog ?
- tmp : orig_prog);
+
return prog;
out_free:
image_ptr = NULL;
bpf_jit_binary_free(header);
- prog = orig_prog;
goto out_imms;
}
diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c
index adf84962d579..cd5a72fff500 100644
--- a/arch/arm64/net/bpf_jit_comp.c
+++ b/arch/arm64/net/bpf_jit_comp.c
@@ -2009,14 +2009,12 @@ struct arm64_jit_data {
struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
{
int image_size, prog_size, extable_size, extable_align, extable_offset;
- struct bpf_prog *tmp, *orig_prog = prog;
struct bpf_binary_header *header;
struct bpf_binary_header *ro_header = NULL;
struct arm64_jit_data *jit_data;
void __percpu *priv_stack_ptr = NULL;
bool was_classic = bpf_prog_was_classic(prog);
int priv_stack_alloc_sz;
- bool tmp_blinded = false;
bool extra_pass = false;
struct jit_ctx ctx;
u8 *image_ptr;
@@ -2025,26 +2023,13 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
int exentry_idx;
if (!prog->jit_requested)
- return orig_prog;
-
- tmp = bpf_jit_blind_constants(prog);
- /* If blinding was requested and we failed during blinding,
- * we must fall back to the interpreter.
- */
- if (IS_ERR(tmp))
- return orig_prog;
- if (tmp != prog) {
- tmp_blinded = true;
- prog = tmp;
- }
+ return prog;
jit_data = prog->aux->jit_data;
if (!jit_data) {
jit_data = kzalloc_obj(*jit_data);
- if (!jit_data) {
- prog = orig_prog;
- goto out;
- }
+ if (!jit_data)
+ return prog;
prog->aux->jit_data = jit_data;
}
priv_stack_ptr = prog->aux->priv_stack_ptr;
@@ -2056,10 +2041,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
priv_stack_alloc_sz = round_up(prog->aux->stack_depth, 16) +
2 * PRIV_STACK_GUARD_SZ;
priv_stack_ptr = __alloc_percpu_gfp(priv_stack_alloc_sz, 16, GFP_KERNEL);
- if (!priv_stack_ptr) {
- prog = orig_prog;
+ if (!priv_stack_ptr)
goto out_priv_stack;
- }
priv_stack_init_guard(priv_stack_ptr, priv_stack_alloc_sz);
prog->aux->priv_stack_ptr = priv_stack_ptr;
@@ -2079,10 +2062,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
ctx.prog = prog;
ctx.offset = kvzalloc_objs(int, prog->len + 1);
- if (ctx.offset == NULL) {
- prog = orig_prog;
+ if (ctx.offset == NULL)
goto out_off;
- }
ctx.user_vm_start = bpf_arena_get_user_vm_start(prog->aux->arena);
ctx.arena_vm_start = bpf_arena_get_kern_vm_start(prog->aux->arena);
@@ -2095,15 +2076,11 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
* BPF line info needs ctx->offset[i] to be the offset of
* instruction[i] in jited image, so build prologue first.
*/
- if (build_prologue(&ctx, was_classic)) {
- prog = orig_prog;
+ if (build_prologue(&ctx, was_classic))
goto out_off;
- }
- if (build_body(&ctx, extra_pass)) {
- prog = orig_prog;
+ if (build_body(&ctx, extra_pass))
goto out_off;
- }
ctx.epilogue_offset = ctx.idx;
build_epilogue(&ctx, was_classic);
@@ -2121,10 +2098,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
ro_header = bpf_jit_binary_pack_alloc(image_size, &ro_image_ptr,
sizeof(u64), &header, &image_ptr,
jit_fill_hole);
- if (!ro_header) {
- prog = orig_prog;
+ if (!ro_header)
goto out_off;
- }
/* Pass 2: Determine jited position and result for each instruction */
@@ -2152,10 +2127,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
/* Dont write body instructions to memory for now */
ctx.write = false;
- if (build_body(&ctx, extra_pass)) {
- prog = orig_prog;
+ if (build_body(&ctx, extra_pass))
goto out_free_hdr;
- }
ctx.epilogue_offset = ctx.idx;
ctx.exentry_idx = exentry_idx;
@@ -2164,19 +2137,15 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
/* Pass 3: Adjust jump offset and write final image */
if (build_body(&ctx, extra_pass) ||
- WARN_ON_ONCE(ctx.idx != ctx.epilogue_offset)) {
- prog = orig_prog;
+ WARN_ON_ONCE(ctx.idx != ctx.epilogue_offset))
goto out_free_hdr;
- }
build_epilogue(&ctx, was_classic);
build_plt(&ctx);
/* Extra pass to validate JITed code. */
- if (validate_ctx(&ctx)) {
- prog = orig_prog;
+ if (validate_ctx(&ctx))
goto out_free_hdr;
- }
/* update the real prog size */
prog_size = sizeof(u32) * ctx.idx;
@@ -2193,16 +2162,13 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
if (extra_pass && ctx.idx > jit_data->ctx.idx) {
pr_err_once("multi-func JIT bug %d > %d\n",
ctx.idx, jit_data->ctx.idx);
- prog->bpf_func = NULL;
- prog->jited = 0;
- prog->jited_len = 0;
goto out_free_hdr;
}
if (WARN_ON(bpf_jit_binary_pack_finalize(ro_header, header))) {
- /* ro_header has been freed */
+ /* ro_header and header has been freed */
ro_header = NULL;
- prog = orig_prog;
- goto out_off;
+ header = NULL;
+ goto out_free_hdr;
}
/*
* The instructions have now been copied to the ROX region from
@@ -2245,13 +2211,15 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
kfree(jit_data);
prog->aux->jit_data = NULL;
}
-out:
- if (tmp_blinded)
- bpf_jit_prog_release_other(prog, prog == orig_prog ?
- tmp : orig_prog);
+
return prog;
out_free_hdr:
+ if (extra_pass) {
+ prog->bpf_func = NULL;
+ prog->jited = 0;
+ prog->jited_len = 0;
+ }
if (header) {
bpf_arch_text_copy(&ro_header->size, &header->size,
sizeof(header->size));
diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c
index 9cb796e16379..fcc8c0c29fb0 100644
--- a/arch/loongarch/net/bpf_jit.c
+++ b/arch/loongarch/net/bpf_jit.c
@@ -1922,43 +1922,26 @@ int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags,
struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
{
- bool tmp_blinded = false, extra_pass = false;
+ bool extra_pass = false;
u8 *image_ptr, *ro_image_ptr;
int image_size, prog_size, extable_size;
struct jit_ctx ctx;
struct jit_data *jit_data;
struct bpf_binary_header *header;
struct bpf_binary_header *ro_header;
- struct bpf_prog *tmp, *orig_prog = prog;
/*
* If BPF JIT was not enabled then we must fall back to
* the interpreter.
*/
if (!prog->jit_requested)
- return orig_prog;
-
- tmp = bpf_jit_blind_constants(prog);
- /*
- * If blinding was requested and we failed during blinding,
- * we must fall back to the interpreter. Otherwise, we save
- * the new JITed code.
- */
- if (IS_ERR(tmp))
- return orig_prog;
-
- if (tmp != prog) {
- tmp_blinded = true;
- prog = tmp;
- }
+ return prog;
jit_data = prog->aux->jit_data;
if (!jit_data) {
jit_data = kzalloc_obj(*jit_data);
- if (!jit_data) {
- prog = orig_prog;
- goto out;
- }
+ if (!jit_data)
+ return prog;
prog->aux->jit_data = jit_data;
}
if (jit_data->ctx.offset) {
@@ -1978,17 +1961,13 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
ctx.user_vm_start = bpf_arena_get_user_vm_start(prog->aux->arena);
ctx.offset = kvcalloc(prog->len + 1, sizeof(u32), GFP_KERNEL);
- if (ctx.offset == NULL) {
- prog = orig_prog;
+ if (ctx.offset == NULL)
goto out_offset;
- }
/* 1. Initial fake pass to compute ctx->idx and set ctx->flags */
build_prologue(&ctx);
- if (build_body(&ctx, extra_pass)) {
- prog = orig_prog;
+ if (build_body(&ctx, extra_pass))
goto out_offset;
- }
ctx.epilogue_offset = ctx.idx;
build_epilogue(&ctx);
@@ -2004,10 +1983,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
/* Now we know the size of the structure to make */
ro_header = bpf_jit_binary_pack_alloc(image_size, &ro_image_ptr, sizeof(u32),
&header, &image_ptr, jit_fill_hole);
- if (!ro_header) {
- prog = orig_prog;
+ if (!ro_header)
goto out_offset;
- }
/* 2. Now, the actual pass to generate final JIT code */
/*
@@ -2027,17 +2004,13 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
ctx.num_exentries = 0;
build_prologue(&ctx);
- if (build_body(&ctx, extra_pass)) {
- prog = orig_prog;
+ if (build_body(&ctx, extra_pass))
goto out_free;
- }
build_epilogue(&ctx);
/* 3. Extra pass to validate JITed code */
- if (validate_ctx(&ctx)) {
- prog = orig_prog;
+ if (validate_ctx(&ctx))
goto out_free;
- }
/* And we're done */
if (bpf_jit_enable > 1)
@@ -2050,9 +2023,9 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
goto out_free;
}
if (WARN_ON(bpf_jit_binary_pack_finalize(ro_header, header))) {
- /* ro_header has been freed */
+ /* ro_header and header have been freed */
ro_header = NULL;
- prog = orig_prog;
+ header = NULL;
goto out_free;
}
/*
@@ -2084,13 +2057,15 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
prog->aux->jit_data = NULL;
}
-out:
- if (tmp_blinded)
- bpf_jit_prog_release_other(prog, prog == orig_prog ? tmp : orig_prog);
-
return prog;
out_free:
+ if (extra_pass) {
+ prog->bpf_func = NULL;
+ prog->jited = 0;
+ prog->jited_len = 0;
+ }
+
if (header) {
bpf_arch_text_copy(&ro_header->size, &header->size, sizeof(header->size));
bpf_jit_binary_pack_free(ro_header, header);
diff --git a/arch/mips/net/bpf_jit_comp.c b/arch/mips/net/bpf_jit_comp.c
index e355dfca4400..d2b6c955f18e 100644
--- a/arch/mips/net/bpf_jit_comp.c
+++ b/arch/mips/net/bpf_jit_comp.c
@@ -911,10 +911,8 @@ bool bpf_jit_needs_zext(void)
struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
{
- struct bpf_prog *tmp, *orig_prog = prog;
struct bpf_binary_header *header = NULL;
struct jit_context ctx;
- bool tmp_blinded = false;
unsigned int tmp_idx;
unsigned int image_size;
u8 *image_ptr;
@@ -925,19 +923,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
* the interpreter.
*/
if (!prog->jit_requested)
- return orig_prog;
- /*
- * If constant blinding was enabled and we failed during blinding
- * then we must fall back to the interpreter. Otherwise, we save
- * the new JITed code.
- */
- tmp = bpf_jit_blind_constants(prog);
- if (IS_ERR(tmp))
- return orig_prog;
- if (tmp != prog) {
- tmp_blinded = true;
- prog = tmp;
- }
+ return prog;
memset(&ctx, 0, sizeof(ctx));
ctx.program = prog;
@@ -1025,14 +1011,10 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
prog->jited_len = image_size;
out:
- if (tmp_blinded)
- bpf_jit_prog_release_other(prog, prog == orig_prog ?
- tmp : orig_prog);
kfree(ctx.descriptors);
return prog;
out_err:
- prog = orig_prog;
if (header)
bpf_jit_binary_free(header);
goto out;
diff --git a/arch/parisc/net/bpf_jit_core.c b/arch/parisc/net/bpf_jit_core.c
index a5eb6b51e27a..35dca372b5df 100644
--- a/arch/parisc/net/bpf_jit_core.c
+++ b/arch/parisc/net/bpf_jit_core.c
@@ -44,30 +44,19 @@ bool bpf_jit_needs_zext(void)
struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
{
unsigned int prog_size = 0, extable_size = 0;
- bool tmp_blinded = false, extra_pass = false;
- struct bpf_prog *tmp, *orig_prog = prog;
+ bool extra_pass = false;
int pass = 0, prev_ninsns = 0, prologue_len, i;
struct hppa_jit_data *jit_data;
struct hppa_jit_context *ctx;
if (!prog->jit_requested)
- return orig_prog;
-
- tmp = bpf_jit_blind_constants(prog);
- if (IS_ERR(tmp))
- return orig_prog;
- if (tmp != prog) {
- tmp_blinded = true;
- prog = tmp;
- }
+ return prog;
jit_data = prog->aux->jit_data;
if (!jit_data) {
jit_data = kzalloc_obj(*jit_data);
- if (!jit_data) {
- prog = orig_prog;
- goto out;
- }
+ if (!jit_data)
+ return prog;
prog->aux->jit_data = jit_data;
}
@@ -81,10 +70,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
ctx->prog = prog;
ctx->offset = kzalloc_objs(int, prog->len);
- if (!ctx->offset) {
- prog = orig_prog;
- goto out_offset;
- }
+ if (!ctx->offset)
+ goto out_err;
for (i = 0; i < prog->len; i++) {
prev_ninsns += 20;
ctx->offset[i] = prev_ninsns;
@@ -93,10 +80,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
for (i = 0; i < NR_JIT_ITERATIONS; i++) {
pass++;
ctx->ninsns = 0;
- if (build_body(ctx, extra_pass, ctx->offset)) {
- prog = orig_prog;
- goto out_offset;
- }
+ if (build_body(ctx, extra_pass, ctx->offset))
+ goto out_err;
ctx->body_len = ctx->ninsns;
bpf_jit_build_prologue(ctx);
ctx->prologue_len = ctx->ninsns - ctx->body_len;
@@ -116,10 +101,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
&jit_data->image,
sizeof(long),
bpf_fill_ill_insns);
- if (!jit_data->header) {
- prog = orig_prog;
- goto out_offset;
- }
+ if (!jit_data->header)
+ goto out_err;
ctx->insns = (u32 *)jit_data->image;
/*
@@ -134,8 +117,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
pr_err("bpf-jit: image did not converge in <%d passes!\n", i);
if (jit_data->header)
bpf_jit_binary_free(jit_data->header);
- prog = orig_prog;
- goto out_offset;
+ goto out_err;
}
if (extable_size)
@@ -148,8 +130,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
bpf_jit_build_prologue(ctx);
if (build_body(ctx, extra_pass, NULL)) {
bpf_jit_binary_free(jit_data->header);
- prog = orig_prog;
- goto out_offset;
+ goto out_err;
}
bpf_jit_build_epilogue(ctx);
@@ -160,20 +141,19 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
{ extern int machine_restart(char *); machine_restart(""); }
}
+ if (!prog->is_func || extra_pass) {
+ if (bpf_jit_binary_lock_ro(jit_data->header)) {
+ bpf_jit_binary_free(jit_data->header);
+ goto out_err;
+ }
+ bpf_flush_icache(jit_data->header, ctx->insns + ctx->ninsns);
+ }
+
prog->bpf_func = (void *)ctx->insns;
prog->jited = 1;
prog->jited_len = prog_size;
- bpf_flush_icache(jit_data->header, ctx->insns + ctx->ninsns);
-
if (!prog->is_func || extra_pass) {
- if (bpf_jit_binary_lock_ro(jit_data->header)) {
- bpf_jit_binary_free(jit_data->header);
- prog->bpf_func = NULL;
- prog->jited = 0;
- prog->jited_len = 0;
- goto out_offset;
- }
prologue_len = ctx->epilogue_offset - ctx->body_len;
for (i = 0; i < prog->len; i++)
ctx->offset[i] += prologue_len;
@@ -183,14 +163,19 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
kfree(jit_data);
prog->aux->jit_data = NULL;
}
-out:
+
if (HPPA_JIT_REBOOT)
{ extern int machine_restart(char *); machine_restart(""); }
- if (tmp_blinded)
- bpf_jit_prog_release_other(prog, prog == orig_prog ?
- tmp : orig_prog);
return prog;
+
+out_err:
+ if (extra_pass) {
+ prog->bpf_func = NULL;
+ prog->jited = 0;
+ prog->jited_len = 0;
+ }
+ goto out_offset;
}
u64 hppa_div64(u64 div, u64 divisor)
diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c
index a62a9a92b7b5..711028bebea3 100644
--- a/arch/powerpc/net/bpf_jit_comp.c
+++ b/arch/powerpc/net/bpf_jit_comp.c
@@ -142,9 +142,6 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
int flen;
struct bpf_binary_header *fhdr = NULL;
struct bpf_binary_header *hdr = NULL;
- struct bpf_prog *org_fp = fp;
- struct bpf_prog *tmp_fp;
- bool bpf_blinded = false;
bool extra_pass = false;
u8 *fimage = NULL;
u32 *fcode_base;
@@ -152,24 +149,13 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
u32 fixup_len;
if (!fp->jit_requested)
- return org_fp;
-
- tmp_fp = bpf_jit_blind_constants(org_fp);
- if (IS_ERR(tmp_fp))
- return org_fp;
-
- if (tmp_fp != org_fp) {
- bpf_blinded = true;
- fp = tmp_fp;
- }
+ return fp;
jit_data = fp->aux->jit_data;
if (!jit_data) {
jit_data = kzalloc_obj(*jit_data);
- if (!jit_data) {
- fp = org_fp;
- goto out;
- }
+ if (!jit_data)
+ return fp;
fp->aux->jit_data = jit_data;
}
@@ -194,10 +180,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
}
addrs = kcalloc(flen + 1, sizeof(*addrs), GFP_KERNEL);
- if (addrs == NULL) {
- fp = org_fp;
- goto out_addrs;
- }
+ if (addrs == NULL)
+ goto out_err;
memset(&cgctx, 0, sizeof(struct codegen_context));
bpf_jit_init_reg_mapping(&cgctx);
@@ -211,11 +195,9 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
cgctx.exception_cb = fp->aux->exception_cb;
/* Scouting faux-generate pass 0 */
- if (bpf_jit_build_body(fp, NULL, NULL, &cgctx, addrs, 0, false)) {
+ if (bpf_jit_build_body(fp, NULL, NULL, &cgctx, addrs, 0, false))
/* We hit something illegal or unsupported. */
- fp = org_fp;
- goto out_addrs;
- }
+ goto out_err;
/*
* If we have seen a tail call, we need a second pass.
@@ -226,10 +208,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
*/
if (cgctx.seen & SEEN_TAILCALL || !is_offset_in_branch_range((long)cgctx.idx * 4)) {
cgctx.idx = 0;
- if (bpf_jit_build_body(fp, NULL, NULL, &cgctx, addrs, 0, false)) {
- fp = org_fp;
- goto out_addrs;
- }
+ if (bpf_jit_build_body(fp, NULL, NULL, &cgctx, addrs, 0, false))
+ goto out_err;
}
bpf_jit_realloc_regs(&cgctx);
@@ -250,10 +230,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
fhdr = bpf_jit_binary_pack_alloc(alloclen, &fimage, 4, &hdr, &image,
bpf_jit_fill_ill_insns);
- if (!fhdr) {
- fp = org_fp;
- goto out_addrs;
- }
+ if (!fhdr)
+ goto out_err;
if (extable_len)
fp->aux->extable = (void *)fimage + FUNCTION_DESCR_SIZE + proglen + fixup_len;
@@ -272,8 +250,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
extra_pass)) {
bpf_arch_text_copy(&fhdr->size, &hdr->size, sizeof(hdr->size));
bpf_jit_binary_pack_free(fhdr, hdr);
- fp = org_fp;
- goto out_addrs;
+ goto out_err;
}
bpf_jit_build_epilogue(code_base, &cgctx);
@@ -295,15 +272,16 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
((u64 *)image)[1] = local_paca->kernel_toc;
#endif
+ if (!fp->is_func || extra_pass) {
+ if (bpf_jit_binary_pack_finalize(fhdr, hdr))
+ goto out_err;
+ }
+
fp->bpf_func = (void *)fimage;
fp->jited = 1;
fp->jited_len = cgctx.idx * 4 + FUNCTION_DESCR_SIZE;
if (!fp->is_func || extra_pass) {
- if (bpf_jit_binary_pack_finalize(fhdr, hdr)) {
- fp = org_fp;
- goto out_addrs;
- }
bpf_prog_fill_jited_linfo(fp, addrs);
out_addrs:
kfree(addrs);
@@ -318,11 +296,15 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
jit_data->hdr = hdr;
}
-out:
- if (bpf_blinded)
- bpf_jit_prog_release_other(fp, fp == org_fp ? tmp_fp : org_fp);
-
return fp;
+
+out_err:
+ if (extra_pass) {
+ fp->bpf_func = NULL;
+ fp->jited = 0;
+ fp->jited_len = 0;
+ }
+ goto out_addrs;
}
/*
diff --git a/arch/riscv/net/bpf_jit_core.c b/arch/riscv/net/bpf_jit_core.c
index b3581e926436..527baa50dc68 100644
--- a/arch/riscv/net/bpf_jit_core.c
+++ b/arch/riscv/net/bpf_jit_core.c
@@ -44,29 +44,19 @@ bool bpf_jit_needs_zext(void)
struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
{
unsigned int prog_size = 0, extable_size = 0;
- bool tmp_blinded = false, extra_pass = false;
- struct bpf_prog *tmp, *orig_prog = prog;
+ bool extra_pass = false;
int pass = 0, prev_ninsns = 0, i;
struct rv_jit_data *jit_data;
struct rv_jit_context *ctx;
if (!prog->jit_requested)
- return orig_prog;
-
- tmp = bpf_jit_blind_constants(prog);
- if (IS_ERR(tmp))
- return orig_prog;
- if (tmp != prog) {
- tmp_blinded = true;
- prog = tmp;
- }
+ return prog;
jit_data = prog->aux->jit_data;
if (!jit_data) {
jit_data = kzalloc_obj(*jit_data);
if (!jit_data) {
- prog = orig_prog;
- goto out;
+ return prog;
}
prog->aux->jit_data = jit_data;
}
@@ -83,15 +73,11 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
ctx->user_vm_start = bpf_arena_get_user_vm_start(prog->aux->arena);
ctx->prog = prog;
ctx->offset = kzalloc_objs(int, prog->len);
- if (!ctx->offset) {
- prog = orig_prog;
+ if (!ctx->offset)
goto out_offset;
- }
- if (build_body(ctx, extra_pass, NULL)) {
- prog = orig_prog;
+ if (build_body(ctx, extra_pass, NULL))
goto out_offset;
- }
for (i = 0; i < prog->len; i++) {
prev_ninsns += 32;
@@ -105,10 +91,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
bpf_jit_build_prologue(ctx, bpf_is_subprog(prog));
ctx->prologue_len = ctx->ninsns;
- if (build_body(ctx, extra_pass, ctx->offset)) {
- prog = orig_prog;
+ if (build_body(ctx, extra_pass, ctx->offset))
goto out_offset;
- }
ctx->epilogue_offset = ctx->ninsns;
bpf_jit_build_epilogue(ctx);
@@ -126,10 +110,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
&jit_data->ro_image, sizeof(u32),
&jit_data->header, &jit_data->image,
bpf_fill_ill_insns);
- if (!jit_data->ro_header) {
- prog = orig_prog;
+ if (!jit_data->ro_header)
goto out_offset;
- }
/*
* Use the image(RW) for writing the JITed instructions. But also save
@@ -150,7 +132,6 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
if (i == NR_JIT_ITERATIONS) {
pr_err("bpf-jit: image did not converge in <%d passes!\n", i);
- prog = orig_prog;
goto out_free_hdr;
}
@@ -163,26 +144,27 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
ctx->nexentries = 0;
bpf_jit_build_prologue(ctx, bpf_is_subprog(prog));
- if (build_body(ctx, extra_pass, NULL)) {
- prog = orig_prog;
+ if (build_body(ctx, extra_pass, NULL))
goto out_free_hdr;
- }
bpf_jit_build_epilogue(ctx);
if (bpf_jit_enable > 1)
bpf_jit_dump(prog->len, prog_size, pass, ctx->insns);
- prog->bpf_func = (void *)ctx->ro_insns + cfi_get_offset();
- prog->jited = 1;
- prog->jited_len = prog_size - cfi_get_offset();
-
if (!prog->is_func || extra_pass) {
if (WARN_ON(bpf_jit_binary_pack_finalize(jit_data->ro_header, jit_data->header))) {
/* ro_header has been freed */
jit_data->ro_header = NULL;
- prog = orig_prog;
- goto out_offset;
+ jit_data->header = NULL;
+ goto out_free_hdr;
}
+ }
+
+ prog->bpf_func = (void *)ctx->ro_insns + cfi_get_offset();
+ prog->jited = 1;
+ prog->jited_len = prog_size - cfi_get_offset();
+
+ if (!prog->is_func || extra_pass) {
/*
* The instructions have now been copied to the ROX region from
* where they will execute.
@@ -198,14 +180,15 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
kfree(jit_data);
prog->aux->jit_data = NULL;
}
-out:
- if (tmp_blinded)
- bpf_jit_prog_release_other(prog, prog == orig_prog ?
- tmp : orig_prog);
return prog;
out_free_hdr:
+ if (extra_pass) {
+ prog->bpf_func = NULL;
+ prog->jited = 0;
+ prog->jited_len = 0;
+ }
if (jit_data->header) {
bpf_arch_text_copy(&jit_data->ro_header->size, &jit_data->header->size,
sizeof(jit_data->header->size));
diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c
index d08d159b6319..2dfc279b1be2 100644
--- a/arch/s390/net/bpf_jit_comp.c
+++ b/arch/s390/net/bpf_jit_comp.c
@@ -2314,36 +2314,20 @@ static struct bpf_binary_header *bpf_jit_alloc(struct bpf_jit *jit,
*/
struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
{
- struct bpf_prog *tmp, *orig_fp = fp;
struct bpf_binary_header *header;
struct s390_jit_data *jit_data;
- bool tmp_blinded = false;
bool extra_pass = false;
struct bpf_jit jit;
int pass;
if (!fp->jit_requested)
- return orig_fp;
-
- tmp = bpf_jit_blind_constants(fp);
- /*
- * If blinding was requested and we failed during blinding,
- * we must fall back to the interpreter.
- */
- if (IS_ERR(tmp))
- return orig_fp;
- if (tmp != fp) {
- tmp_blinded = true;
- fp = tmp;
- }
+ return fp;
jit_data = fp->aux->jit_data;
if (!jit_data) {
jit_data = kzalloc_obj(*jit_data);
- if (!jit_data) {
- fp = orig_fp;
- goto out;
- }
+ if (!jit_data)
+ return fp;
fp->aux->jit_data = jit_data;
}
if (jit_data->ctx.addrs) {
@@ -2356,34 +2340,27 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
memset(&jit, 0, sizeof(jit));
jit.addrs = kvcalloc(fp->len + 1, sizeof(*jit.addrs), GFP_KERNEL);
- if (jit.addrs == NULL) {
- fp = orig_fp;
- goto free_addrs;
- }
+ if (jit.addrs == NULL)
+ goto out_err;
/*
* Three initial passes:
* - 1/2: Determine clobbered registers
* - 3: Calculate program size and addrs array
*/
for (pass = 1; pass <= 3; pass++) {
- if (bpf_jit_prog(&jit, fp, extra_pass)) {
- fp = orig_fp;
- goto free_addrs;
- }
+ if (bpf_jit_prog(&jit, fp, extra_pass))
+ goto out_err;
}
/*
* Final pass: Allocate and generate program
*/
header = bpf_jit_alloc(&jit, fp);
- if (!header) {
- fp = orig_fp;
- goto free_addrs;
- }
+ if (!header)
+ goto out_err;
skip_init_ctx:
if (bpf_jit_prog(&jit, fp, extra_pass)) {
bpf_jit_binary_free(header);
- fp = orig_fp;
- goto free_addrs;
+ goto out_err;
}
if (bpf_jit_enable > 1) {
bpf_jit_dump(fp->len, jit.size, pass, jit.prg_buf);
@@ -2392,8 +2369,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
if (!fp->is_func || extra_pass) {
if (bpf_jit_binary_lock_ro(header)) {
bpf_jit_binary_free(header);
- fp = orig_fp;
- goto free_addrs;
+ goto out_err;
}
} else {
jit_data->header = header;
@@ -2411,11 +2387,16 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
kfree(jit_data);
fp->aux->jit_data = NULL;
}
-out:
- if (tmp_blinded)
- bpf_jit_prog_release_other(fp, fp == orig_fp ?
- tmp : orig_fp);
+
return fp;
+
+out_err:
+ if (extra_pass) {
+ fp->bpf_func = NULL;
+ fp->jited = 0;
+ fp->jited_len = 0;
+ }
+ goto free_addrs;
}
bool bpf_jit_supports_kfunc_call(void)
diff --git a/arch/sparc/net/bpf_jit_comp_64.c b/arch/sparc/net/bpf_jit_comp_64.c
index b23d1c645ae5..e83e29137566 100644
--- a/arch/sparc/net/bpf_jit_comp_64.c
+++ b/arch/sparc/net/bpf_jit_comp_64.c
@@ -1479,37 +1479,22 @@ struct sparc64_jit_data {
struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
{
- struct bpf_prog *tmp, *orig_prog = prog;
struct sparc64_jit_data *jit_data;
struct bpf_binary_header *header;
u32 prev_image_size, image_size;
- bool tmp_blinded = false;
bool extra_pass = false;
struct jit_ctx ctx;
u8 *image_ptr;
int pass, i;
if (!prog->jit_requested)
- return orig_prog;
-
- tmp = bpf_jit_blind_constants(prog);
- /* If blinding was requested and we failed during blinding,
- * we must fall back to the interpreter.
- */
- if (IS_ERR(tmp))
- return orig_prog;
- if (tmp != prog) {
- tmp_blinded = true;
- prog = tmp;
- }
+ return prog;
jit_data = prog->aux->jit_data;
if (!jit_data) {
jit_data = kzalloc_obj(*jit_data);
- if (!jit_data) {
- prog = orig_prog;
- goto out;
- }
+ if (!jit_data)
+ return prog;
prog->aux->jit_data = jit_data;
}
if (jit_data->ctx.offset) {
@@ -1527,10 +1512,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
ctx.prog = prog;
ctx.offset = kmalloc_array(prog->len, sizeof(unsigned int), GFP_KERNEL);
- if (ctx.offset == NULL) {
- prog = orig_prog;
- goto out_off;
- }
+ if (ctx.offset == NULL)
+ goto out_err;
/* Longest sequence emitted is for bswap32, 12 instructions. Pre-cook
* the offset array so that we converge faster.
@@ -1543,10 +1526,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
ctx.idx = 0;
build_prologue(&ctx);
- if (build_body(&ctx)) {
- prog = orig_prog;
- goto out_off;
- }
+ if (build_body(&ctx))
+ goto out_err;
build_epilogue(&ctx);
if (bpf_jit_enable > 1)
@@ -1569,10 +1550,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
image_size = sizeof(u32) * ctx.idx;
header = bpf_jit_binary_alloc(image_size, &image_ptr,
sizeof(u32), jit_fill_hole);
- if (header == NULL) {
- prog = orig_prog;
- goto out_off;
- }
+ if (header == NULL)
+ goto out_err;
ctx.image = (u32 *)image_ptr;
skip_init_ctx:
@@ -1582,8 +1561,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
if (build_body(&ctx)) {
bpf_jit_binary_free(header);
- prog = orig_prog;
- goto out_off;
+ goto out_err;
}
build_epilogue(&ctx);
@@ -1592,8 +1570,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
pr_err("bpf_jit: Failed to converge, prev_size=%u size=%d\n",
prev_image_size, ctx.idx * 4);
bpf_jit_binary_free(header);
- prog = orig_prog;
- goto out_off;
+ goto out_err;
}
if (bpf_jit_enable > 1)
@@ -1604,8 +1581,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
if (!prog->is_func || extra_pass) {
if (bpf_jit_binary_lock_ro(header)) {
bpf_jit_binary_free(header);
- prog = orig_prog;
- goto out_off;
+ goto out_err;
}
} else {
jit_data->ctx = ctx;
@@ -1624,9 +1600,14 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
kfree(jit_data);
prog->aux->jit_data = NULL;
}
-out:
- if (tmp_blinded)
- bpf_jit_prog_release_other(prog, prog == orig_prog ?
- tmp : orig_prog);
+
return prog;
+
+out_err:
+ if (extra_pass) {
+ prog->bpf_func = NULL;
+ prog->jited = 0;
+ prog->jited_len = 0;
+ }
+ goto out_off;
}
diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
index e9b78040d703..77d00a8dec87 100644
--- a/arch/x86/net/bpf_jit_comp.c
+++ b/arch/x86/net/bpf_jit_comp.c
@@ -3717,13 +3717,11 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
{
struct bpf_binary_header *rw_header = NULL;
struct bpf_binary_header *header = NULL;
- struct bpf_prog *tmp, *orig_prog = prog;
void __percpu *priv_stack_ptr = NULL;
struct x64_jit_data *jit_data;
int priv_stack_alloc_sz;
int proglen, oldproglen = 0;
struct jit_context ctx = {};
- bool tmp_blinded = false;
bool extra_pass = false;
bool padding = false;
u8 *rw_image = NULL;
@@ -3733,27 +3731,13 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
int i;
if (!prog->jit_requested)
- return orig_prog;
-
- tmp = bpf_jit_blind_constants(prog);
- /*
- * If blinding was requested and we failed during blinding,
- * we must fall back to the interpreter.
- */
- if (IS_ERR(tmp))
- return orig_prog;
- if (tmp != prog) {
- tmp_blinded = true;
- prog = tmp;
- }
+ return prog;
jit_data = prog->aux->jit_data;
if (!jit_data) {
jit_data = kzalloc_obj(*jit_data);
- if (!jit_data) {
- prog = orig_prog;
- goto out;
- }
+ if (!jit_data)
+ return prog;
prog->aux->jit_data = jit_data;
}
priv_stack_ptr = prog->aux->priv_stack_ptr;
@@ -3765,10 +3749,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
priv_stack_alloc_sz = round_up(prog->aux->stack_depth, 8) +
2 * PRIV_STACK_GUARD_SZ;
priv_stack_ptr = __alloc_percpu_gfp(priv_stack_alloc_sz, 8, GFP_KERNEL);
- if (!priv_stack_ptr) {
- prog = orig_prog;
+ if (!priv_stack_ptr)
goto out_priv_stack;
- }
priv_stack_init_guard(priv_stack_ptr, priv_stack_alloc_sz);
prog->aux->priv_stack_ptr = priv_stack_ptr;
@@ -3786,10 +3768,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
goto skip_init_addrs;
}
addrs = kvmalloc_objs(*addrs, prog->len + 1);
- if (!addrs) {
- prog = orig_prog;
+ if (!addrs)
goto out_addrs;
- }
/*
* Before first pass, make a rough estimation of addrs[]
@@ -3820,8 +3800,6 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
sizeof(rw_header->size));
bpf_jit_binary_pack_free(header, rw_header);
}
- /* Fall back to interpreter mode */
- prog = orig_prog;
if (extra_pass) {
prog->bpf_func = NULL;
prog->jited = 0;
@@ -3852,10 +3830,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
header = bpf_jit_binary_pack_alloc(roundup(proglen, align) + extable_size,
&image, align, &rw_header, &rw_image,
jit_fill_hole);
- if (!header) {
- prog = orig_prog;
+ if (!header)
goto out_addrs;
- }
prog->aux->extable = (void *) image + roundup(proglen, align);
}
oldproglen = proglen;
@@ -3908,8 +3884,6 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
prog->bpf_func = (void *)image + cfi_get_offset();
prog->jited = 1;
prog->jited_len = proglen - cfi_get_offset();
- } else {
- prog = orig_prog;
}
if (!image || !prog->is_func || extra_pass) {
@@ -3925,10 +3899,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
kfree(jit_data);
prog->aux->jit_data = NULL;
}
-out:
- if (tmp_blinded)
- bpf_jit_prog_release_other(prog, prog == orig_prog ?
- tmp : orig_prog);
+
return prog;
}
diff --git a/arch/x86/net/bpf_jit_comp32.c b/arch/x86/net/bpf_jit_comp32.c
index dda423025c3d..5f259577614a 100644
--- a/arch/x86/net/bpf_jit_comp32.c
+++ b/arch/x86/net/bpf_jit_comp32.c
@@ -2521,35 +2521,19 @@ bool bpf_jit_needs_zext(void)
struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
{
struct bpf_binary_header *header = NULL;
- struct bpf_prog *tmp, *orig_prog = prog;
int proglen, oldproglen = 0;
struct jit_context ctx = {};
- bool tmp_blinded = false;
u8 *image = NULL;
int *addrs;
int pass;
int i;
if (!prog->jit_requested)
- return orig_prog;
-
- tmp = bpf_jit_blind_constants(prog);
- /*
- * If blinding was requested and we failed during blinding,
- * we must fall back to the interpreter.
- */
- if (IS_ERR(tmp))
- return orig_prog;
- if (tmp != prog) {
- tmp_blinded = true;
- prog = tmp;
- }
+ return prog;
addrs = kmalloc_objs(*addrs, prog->len);
- if (!addrs) {
- prog = orig_prog;
- goto out;
- }
+ if (!addrs)
+ return prog;
/*
* Before first pass, make a rough estimation of addrs[]
@@ -2574,7 +2558,6 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
image = NULL;
if (header)
bpf_jit_binary_free(header);
- prog = orig_prog;
goto out_addrs;
}
if (image) {
@@ -2588,10 +2571,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
if (proglen == oldproglen) {
header = bpf_jit_binary_alloc(proglen, &image,
1, jit_fill_hole);
- if (!header) {
- prog = orig_prog;
+ if (!header)
goto out_addrs;
- }
}
oldproglen = proglen;
cond_resched();
@@ -2604,16 +2585,10 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
prog->bpf_func = (void *)image;
prog->jited = 1;
prog->jited_len = proglen;
- } else {
- prog = orig_prog;
}
out_addrs:
kfree(addrs);
-out:
- if (tmp_blinded)
- bpf_jit_prog_release_other(prog, prog == orig_prog ?
- tmp : orig_prog);
return prog;
}
diff --git a/include/linux/filter.h b/include/linux/filter.h
index e40d4071a345..d396e55c9a1d 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -1183,6 +1183,18 @@ static inline bool bpf_dump_raw_ok(const struct cred *cred)
struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
const struct bpf_insn *patch, u32 len);
+
+#ifdef CONFIG_BPF_SYSCALL
+struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,
+ const struct bpf_insn *patch, u32 len);
+#else
+static inline struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,
+ const struct bpf_insn *patch, u32 len)
+{
+ return ERR_PTR(-ENOTSUPP);
+}
+#endif /* CONFIG_BPF_SYSCALL */
+
int bpf_remove_insns(struct bpf_prog *prog, u32 off, u32 cnt);
static inline bool xdp_return_frame_no_direct(void)
@@ -1309,9 +1321,14 @@ int bpf_jit_get_func_addr(const struct bpf_prog *prog,
const char *bpf_jit_get_prog_name(struct bpf_prog *prog);
-struct bpf_prog *bpf_jit_blind_constants(struct bpf_prog *fp);
+struct bpf_prog *bpf_jit_blind_constants(struct bpf_verifier_env *env, struct bpf_prog *prog);
void bpf_jit_prog_release_other(struct bpf_prog *fp, struct bpf_prog *fp_other);
+static inline bool bpf_prog_need_blind(const struct bpf_prog *prog)
+{
+ return prog->blinding_requested && !prog->blinded;
+}
+
static inline void bpf_jit_dump(unsigned int flen, unsigned int proglen,
u32 pass, void *image)
{
@@ -1450,6 +1467,20 @@ static inline void bpf_prog_kallsyms_del(struct bpf_prog *fp)
{
}
+static inline bool bpf_prog_need_blind(const struct bpf_prog *prog)
+{
+ return false;
+}
+
+static inline
+struct bpf_prog *bpf_jit_blind_constants(struct bpf_verifier_env *env, struct bpf_prog *prog)
+{
+ return prog;
+}
+
+static inline void bpf_jit_prog_release_other(struct bpf_prog *fp, struct bpf_prog *fp_other)
+{
+}
#endif /* CONFIG_BPF_JIT */
void bpf_prog_kallsyms_del_all(struct bpf_prog *fp);
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index 1af5fb3f21d9..cc61fe57b98d 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -1506,7 +1506,10 @@ static void adjust_insn_arrays(struct bpf_prog *prog, u32 off, u32 len)
#endif
}
-struct bpf_prog *bpf_jit_blind_constants(struct bpf_prog *prog)
+/* Now this function is used only to blind the main prog and must be invoked only when
+ * bpf_prog_need_blind() returns true.
+ */
+struct bpf_prog *bpf_jit_blind_constants(struct bpf_verifier_env *env, struct bpf_prog *prog)
{
struct bpf_insn insn_buff[16], aux[2];
struct bpf_prog *clone, *tmp;
@@ -1514,13 +1517,17 @@ struct bpf_prog *bpf_jit_blind_constants(struct bpf_prog *prog)
struct bpf_insn *insn;
int i, rewritten;
- if (!prog->blinding_requested || prog->blinded)
- return prog;
+ if (env)
+ prog = env->prog;
clone = bpf_prog_clone_create(prog, GFP_USER);
if (!clone)
return ERR_PTR(-ENOMEM);
+ /* make sure bpf_patch_insn_data() patches the correct prog */
+ if (env)
+ env->prog = clone;
+
insn_cnt = clone->len;
insn = clone->insnsi;
@@ -1548,21 +1555,34 @@ struct bpf_prog *bpf_jit_blind_constants(struct bpf_prog *prog)
if (!rewritten)
continue;
- tmp = bpf_patch_insn_single(clone, i, insn_buff, rewritten);
- if (IS_ERR(tmp)) {
+ if (env)
+ tmp = bpf_patch_insn_data(env, i, insn_buff, rewritten);
+ else
+ tmp = bpf_patch_insn_single(clone, i, insn_buff, rewritten);
+
+ if (IS_ERR_OR_NULL(tmp)) {
+ if (env)
+ /* restore the original prog */
+ env->prog = prog;
/* Patching may have repointed aux->prog during
* realloc from the original one, so we need to
* fix it up here on error.
*/
bpf_jit_prog_release_other(prog, clone);
- return tmp;
+ return IS_ERR(tmp) ? tmp : ERR_PTR(-ENOMEM);
}
clone = tmp;
insn_delta = rewritten - 1;
- /* Instructions arrays must be updated using absolute xlated offsets */
- adjust_insn_arrays(clone, prog->aux->subprog_start + i, rewritten);
+ if (env)
+ env->prog = clone;
+ else
+ /* Instructions arrays must be updated using absolute xlated offsets.
+ * The arrays have already been adjusted by bpf_patch_insn_data() when
+ * env is not NULL.
+ */
+ adjust_insn_arrays(clone, i, rewritten);
/* Walk new program and skip insns we just inserted. */
insn = clone->insnsi + i + insn_delta;
@@ -2531,6 +2551,35 @@ static bool bpf_prog_select_interpreter(struct bpf_prog *fp)
return select_interpreter;
}
+static struct bpf_prog *bpf_prog_jit_compile(struct bpf_prog *prog)
+{
+#ifdef CONFIG_BPF_JIT
+ bool blinded = false;
+ struct bpf_prog *orig_prog = prog;
+
+ if (bpf_prog_need_blind(orig_prog)) {
+ prog = bpf_jit_blind_constants(NULL, orig_prog);
+ /* If blinding was requested and we failed during blinding, we must fall
+ * back to the interpreter.
+ */
+ if (IS_ERR(prog))
+ return orig_prog;
+ blinded = true;
+ }
+
+ prog = bpf_int_jit_compile(prog);
+ if (blinded) {
+ if (!prog->jited) {
+ bpf_jit_prog_release_other(orig_prog, prog);
+ prog = orig_prog;
+ } else {
+ bpf_jit_prog_release_other(prog, orig_prog);
+ }
+ }
+#endif
+ return prog;
+}
+
/**
* bpf_prog_select_runtime - select exec runtime for BPF program
* @fp: bpf_prog populated with BPF program
@@ -2570,7 +2619,7 @@ struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err)
if (*err)
return fp;
- fp = bpf_int_jit_compile(fp);
+ fp = bpf_prog_jit_compile(fp);
bpf_prog_jit_attempt_done(fp);
if (!fp->jited && jit_needed) {
*err = -ENOTSUPP;
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index a431b7d50e1b..66cef3744fde 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -22215,8 +22215,8 @@ static void adjust_poke_descs(struct bpf_prog *prog, u32 off, u32 len)
}
}
-static struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,
- const struct bpf_insn *patch, u32 len)
+struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,
+ const struct bpf_insn *patch, u32 len)
{
struct bpf_prog *new_prog;
struct bpf_insn_aux_data *new_data = NULL;
@@ -22983,7 +22983,41 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
return 0;
}
-static int jit_subprogs(struct bpf_verifier_env *env)
+static u32 *dup_subprog_starts(struct bpf_verifier_env *env)
+{
+ u32 *starts = NULL;
+
+ starts = kvmalloc_objs(u32, env->subprog_cnt, GFP_KERNEL_ACCOUNT);
+ if (!starts)
+ return NULL;
+ for (int i = 0; i < env->subprog_cnt; i++)
+ starts[i] = env->subprog_info[i].start;
+ return starts;
+}
+
+static void restore_subprog_starts(struct bpf_verifier_env *env, u32 *orig_starts)
+{
+ for (int i = 0; i < env->subprog_cnt; i++)
+ env->subprog_info[i].start = orig_starts[i];
+}
+
+static struct bpf_insn_aux_data *dup_insn_aux_data(struct bpf_verifier_env *env)
+{
+ size_t size;
+
+ size = array_size(sizeof(struct bpf_insn_aux_data), env->prog->len);
+ return kvmemdup(env->insn_aux_data, size, GFP_KERNEL_ACCOUNT);
+}
+
+static void restore_insn_aux_data(struct bpf_verifier_env *env,
+ struct bpf_insn_aux_data *orig_insn_aux)
+{
+ /* the expanded elements are zero-filled, so no special handling is required */
+ vfree(env->insn_aux_data);
+ env->insn_aux_data = orig_insn_aux;
+}
+
+static int __jit_subprogs(struct bpf_verifier_env *env)
{
struct bpf_prog *prog = env->prog, **func, *tmp;
int i, j, subprog_start, subprog_end = 0, len, subprog;
@@ -22991,10 +23025,6 @@ static int jit_subprogs(struct bpf_verifier_env *env)
struct bpf_insn *insn;
void *old_bpf_func;
int err, num_exentries;
- int old_len, subprog_start_adjustment = 0;
-
- if (env->subprog_cnt <= 1)
- return 0;
for (i = 0, insn = prog->insnsi; i < prog->len; i++, insn++) {
if (!bpf_pseudo_func(insn) && !bpf_pseudo_call(insn))
@@ -23063,10 +23093,11 @@ static int jit_subprogs(struct bpf_verifier_env *env)
goto out_free;
func[i]->is_func = 1;
func[i]->sleepable = prog->sleepable;
+ func[i]->blinded = prog->blinded;
func[i]->aux->func_idx = i;
/* Below members will be freed only at prog->aux */
func[i]->aux->btf = prog->aux->btf;
- func[i]->aux->subprog_start = subprog_start + subprog_start_adjustment;
+ func[i]->aux->subprog_start = subprog_start;
func[i]->aux->func_info = prog->aux->func_info;
func[i]->aux->func_info_cnt = prog->aux->func_info_cnt;
func[i]->aux->poke_tab = prog->aux->poke_tab;
@@ -23122,15 +23153,7 @@ static int jit_subprogs(struct bpf_verifier_env *env)
func[i]->aux->might_sleep = env->subprog_info[i].might_sleep;
if (!i)
func[i]->aux->exception_boundary = env->seen_exception;
-
- /*
- * To properly pass the absolute subprog start to jit
- * all instruction adjustments should be accumulated
- */
- old_len = func[i]->len;
func[i] = bpf_int_jit_compile(func[i]);
- subprog_start_adjustment += func[i]->len - old_len;
-
if (!func[i]->jited) {
err = -ENOTSUPP;
goto out_free;
@@ -23256,16 +23279,83 @@ static int jit_subprogs(struct bpf_verifier_env *env)
}
kfree(func);
out_undo_insn:
+ bpf_prog_jit_attempt_done(prog);
+ return err;
+}
+
+static int jit_subprogs(struct bpf_verifier_env *env)
+{
+ int err, i;
+ bool blinded = false;
+ struct bpf_insn *insn;
+ struct bpf_prog *prog, *orig_prog;
+ struct bpf_insn_aux_data *orig_insn_aux;
+ u32 *orig_subprog_starts;
+
+ if (env->subprog_cnt <= 1)
+ return 0;
+
+ prog = orig_prog = env->prog;
+ if (bpf_prog_need_blind(orig_prog)) {
+ orig_insn_aux = dup_insn_aux_data(env);
+ if (!orig_insn_aux) {
+ err = -ENOMEM;
+ goto out_cleanup;
+ }
+ orig_subprog_starts = dup_subprog_starts(env);
+ if (!orig_subprog_starts) {
+ err = -ENOMEM;
+ goto out_free_aux;
+ }
+ prog = bpf_jit_blind_constants(env, NULL);
+ if (IS_ERR(prog)) {
+ err = -ENOMEM;
+ prog = orig_prog;
+ goto out_restore;
+ }
+ blinded = true;
+ }
+
+ err = __jit_subprogs(env);
+ if (blinded) {
+ if (err) {
+ bpf_jit_prog_release_other(orig_prog, prog);
+ /* roll back to the clean original prog */
+ prog = env->prog = orig_prog;
+ goto out_restore;
+ } else {
+ bpf_jit_prog_release_other(prog, orig_prog);
+ kvfree(orig_subprog_starts);
+ kvfree(orig_insn_aux);
+ }
+ } else if (err) {
+ /* We will fall back to interpreter mode when err is not -EFAULT, before
+ * that, insn->off and insn->imm should be restored to their original values
+ * since they were modified by __jit_subprogs.
+ */
+ if (err != -EFAULT) {
+ for (i = 0, insn = prog->insnsi; i < prog->len; i++, insn++) {
+ if (!bpf_pseudo_call(insn))
+ continue;
+ insn->off = 0;
+ insn->imm = env->insn_aux_data[i].call_imm;
+ }
+ }
+ goto out_cleanup;
+ }
+
+ return 0;
+
+out_restore:
+ restore_subprog_starts(env, orig_subprog_starts);
+ restore_insn_aux_data(env, orig_insn_aux);
+ kvfree(orig_subprog_starts);
+out_free_aux:
+ kvfree(orig_insn_aux);
+out_cleanup:
/* cleanup main prog to be interpreted */
prog->jit_requested = 0;
prog->blinding_requested = 0;
- for (i = 0, insn = prog->insnsi; i < prog->len; i++, insn++) {
- if (!bpf_pseudo_call(insn))
- continue;
- insn->off = 0;
- insn->imm = env->insn_aux_data[i].call_imm;
- }
- bpf_prog_jit_attempt_done(prog);
return err;
}
--
2.43.0
^ permalink raw reply related
* [PATCH bpf-next v12 3/5] bpf: Add helper to detect indirect jump targets
From: Xu Kuohai @ 2026-04-03 13:28 UTC (permalink / raw)
To: bpf, linux-kernel, linux-arm-kernel
Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Martin KaFai Lau, Eduard Zingerman, Yonghong Song, Puranjay Mohan,
Anton Protopopov, Alexis Lothoré, Shahab Vahedi,
Russell King, Tiezhu Yang, Hengqi Chen, Johan Almbladh,
Paul Burton, Hari Bathini, Christophe Leroy, Naveen N Rao,
Luke Nelson, Xi Wang, Björn Töpel, Pu Lehui,
Ilya Leoshkevich, Heiko Carstens, Vasily Gorbik, David S . Miller,
Wang YanQing
In-Reply-To: <20260403132811.753894-1-xukuohai@huaweicloud.com>
From: Xu Kuohai <xukuohai@huawei.com>
Introduce helper bpf_insn_is_indirect_target to check whether a BPF
instruction is an indirect jump target.
Since the verifier knows which instructions are indirect jump targets,
add a new flag indirect_target to struct bpf_insn_aux_data to mark
them. The verifier sets this flag when verifying an indirect jump target
instruction, and the helper checks the flag to determine whether an
instruction is an indirect jump target.
Reviewed-by: Anton Protopopov <a.s.protopopov@gmail.com>
Signed-off-by: Xu Kuohai <xukuohai@huawei.com>
---
include/linux/bpf.h | 2 ++
include/linux/bpf_verifier.h | 9 +++++----
kernel/bpf/core.c | 9 +++++++++
kernel/bpf/verifier.c | 18 ++++++++++++++++++
4 files changed, 34 insertions(+), 4 deletions(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 05b34a6355b0..90760e250865 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1541,6 +1541,8 @@ bool bpf_has_frame_pointer(unsigned long ip);
int bpf_jit_charge_modmem(u32 size);
void bpf_jit_uncharge_modmem(u32 size);
bool bpf_prog_has_trampoline(const struct bpf_prog *prog);
+bool bpf_insn_is_indirect_target(const struct bpf_verifier_env *env, const struct bpf_prog *prog,
+ int insn_idx);
#else
static inline int bpf_trampoline_link_prog(struct bpf_tramp_link *link,
struct bpf_trampoline *tr,
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index b129e0aaee20..cc53877639a5 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -578,16 +578,17 @@ struct bpf_insn_aux_data {
/* below fields are initialized once */
unsigned int orig_idx; /* original instruction index */
- bool jmp_point;
- bool prune_point;
+ u32 jmp_point:1;
+ u32 prune_point:1;
/* ensure we check state equivalence and save state checkpoint and
* this instruction, regardless of any heuristics
*/
- bool force_checkpoint;
+ u32 force_checkpoint:1;
/* true if instruction is a call to a helper function that
* accepts callback function as a parameter.
*/
- bool calls_callback;
+ u32 calls_callback:1;
+ u32 indirect_target:1; /* if it is an indirect jump target */
/*
* CFG strongly connected component this instruction belongs to,
* zero if it is a singleton SCC.
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index 093ab0f68c81..439575fa6976 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -1570,6 +1570,15 @@ struct bpf_prog *bpf_jit_blind_constants(struct bpf_verifier_env *env, struct bp
clone->blinded = 1;
return clone;
}
+
+bool bpf_insn_is_indirect_target(const struct bpf_verifier_env *env, const struct bpf_prog *prog,
+ int insn_idx)
+{
+ if (!env)
+ return false;
+ insn_idx += prog->aux->subprog_start;
+ return env->insn_aux_data[insn_idx].indirect_target;
+}
#endif /* CONFIG_BPF_JIT */
/* Base function for offset calculation. Needs to go into .text section,
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 5084a754a748..e078e6ad5b00 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -4049,6 +4049,11 @@ static bool is_jmp_point(struct bpf_verifier_env *env, int insn_idx)
return env->insn_aux_data[insn_idx].jmp_point;
}
+static void mark_indirect_target(struct bpf_verifier_env *env, int idx)
+{
+ env->insn_aux_data[idx].indirect_target = true;
+}
+
#define LR_FRAMENO_BITS 3
#define LR_SPI_BITS 6
#define LR_ENTRY_BITS (LR_SPI_BITS + LR_FRAMENO_BITS + 1)
@@ -21227,12 +21232,14 @@ static int check_indirect_jump(struct bpf_verifier_env *env, struct bpf_insn *in
}
for (i = 0; i < n - 1; i++) {
+ mark_indirect_target(env, env->gotox_tmp_buf->items[i]);
other_branch = push_stack(env, env->gotox_tmp_buf->items[i],
env->insn_idx, env->cur_state->speculative);
if (IS_ERR(other_branch))
return PTR_ERR(other_branch);
}
env->insn_idx = env->gotox_tmp_buf->items[n-1];
+ mark_indirect_target(env, env->insn_idx);
return 0;
}
@@ -22158,6 +22165,17 @@ static void adjust_insn_aux_data(struct bpf_verifier_env *env,
data[i].seen = old_seen;
data[i].zext_dst = insn_has_def32(insn + i);
}
+
+ /* The indirect_target flag of the original instruction was moved to the last of the
+ * new instructions by the above memmove and memset, but the indirect jump target is
+ * actually the first instruction, so move it back. This also matches with the behavior
+ * of bpf_insn_array_adjust(), which preserves xlated_off to point to the first new
+ * instruction.
+ */
+ if (data[off + cnt - 1].indirect_target) {
+ data[off].indirect_target = 1;
+ data[off + cnt - 1].indirect_target = 0;
+ }
}
static void adjust_subprog_starts(struct bpf_verifier_env *env, u32 off, u32 len)
--
2.43.0
^ permalink raw reply related
* [PATCH bpf-next v12 2/5] bpf: Pass bpf_verifier_env to JIT
From: Xu Kuohai @ 2026-04-03 13:28 UTC (permalink / raw)
To: bpf, linux-kernel, linux-arm-kernel
Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Martin KaFai Lau, Eduard Zingerman, Yonghong Song, Puranjay Mohan,
Anton Protopopov, Alexis Lothoré, Shahab Vahedi,
Russell King, Tiezhu Yang, Hengqi Chen, Johan Almbladh,
Paul Burton, Hari Bathini, Christophe Leroy, Naveen N Rao,
Luke Nelson, Xi Wang, Björn Töpel, Pu Lehui,
Ilya Leoshkevich, Heiko Carstens, Vasily Gorbik, David S . Miller,
Wang YanQing
In-Reply-To: <20260403132811.753894-1-xukuohai@huaweicloud.com>
From: Xu Kuohai <xukuohai@huawei.com>
Pass bpf_verifier_env to bpf_int_jit_compile(). The follow-up patch will
use env->insn_aux_data in the JIT stage to detect indirect jump targets.
Since bpf_prog_select_runtime() can be called by cbpf and lib/test_bpf.c
code without verifier, introduce helper __bpf_prog_select_runtime()
to accept the env parameter.
Remove the call to bpf_prog_select_runtime() in bpf_prog_load(), and
switch to call __bpf_prog_select_runtime() in the verifier, with env
variable passed. The original bpf_prog_select_runtime() is preserved for
cbpf and lib/test_bpf.c, where env is NULL.
Now all constants blinding calls are moved into the verifier, except
the cbpf and lib/test_bpf.c cases. The instructions arrays are adjusted
by bpf_patch_insn_data() function for normal cases, so there is no need
to call adjust_insn_arrays() in bpf_jit_blind_constants(). Remove it.
Reviewed-by: Anton Protopopov <a.s.protopopov@gmail.com>
Signed-off-by: Xu Kuohai <xukuohai@huawei.com>
---
arch/arc/net/bpf_jit_core.c | 2 +-
arch/arm/net/bpf_jit_32.c | 2 +-
arch/arm64/net/bpf_jit_comp.c | 2 +-
arch/loongarch/net/bpf_jit.c | 2 +-
arch/mips/net/bpf_jit_comp.c | 2 +-
arch/parisc/net/bpf_jit_core.c | 2 +-
arch/powerpc/net/bpf_jit_comp.c | 2 +-
arch/riscv/net/bpf_jit_core.c | 2 +-
arch/s390/net/bpf_jit_comp.c | 2 +-
arch/sparc/net/bpf_jit_comp_64.c | 2 +-
arch/x86/net/bpf_jit_comp.c | 2 +-
arch/x86/net/bpf_jit_comp32.c | 2 +-
include/linux/filter.h | 17 +++++-
kernel/bpf/core.c | 93 +++++++++++++++++---------------
kernel/bpf/syscall.c | 4 --
kernel/bpf/verifier.c | 36 +++++++------
16 files changed, 98 insertions(+), 76 deletions(-)
diff --git a/arch/arc/net/bpf_jit_core.c b/arch/arc/net/bpf_jit_core.c
index 973ceae48675..639a2736f029 100644
--- a/arch/arc/net/bpf_jit_core.c
+++ b/arch/arc/net/bpf_jit_core.c
@@ -1400,7 +1400,7 @@ static struct bpf_prog *do_extra_pass(struct bpf_prog *prog)
* (re)locations involved that their addresses are not known
* during the first run.
*/
-struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
+struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)
{
vm_dump(prog);
diff --git a/arch/arm/net/bpf_jit_32.c b/arch/arm/net/bpf_jit_32.c
index e6b1bb2de627..1628b6fc70a4 100644
--- a/arch/arm/net/bpf_jit_32.c
+++ b/arch/arm/net/bpf_jit_32.c
@@ -2142,7 +2142,7 @@ bool bpf_jit_needs_zext(void)
return true;
}
-struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
+struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)
{
struct bpf_binary_header *header;
struct jit_ctx ctx;
diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c
index cd5a72fff500..7212ec89dfe3 100644
--- a/arch/arm64/net/bpf_jit_comp.c
+++ b/arch/arm64/net/bpf_jit_comp.c
@@ -2006,7 +2006,7 @@ struct arm64_jit_data {
struct jit_ctx ctx;
};
-struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
+struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)
{
int image_size, prog_size, extable_size, extable_align, extable_offset;
struct bpf_binary_header *header;
diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c
index fcc8c0c29fb0..5149ce4cef7e 100644
--- a/arch/loongarch/net/bpf_jit.c
+++ b/arch/loongarch/net/bpf_jit.c
@@ -1920,7 +1920,7 @@ int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags,
return ret < 0 ? ret : ret * LOONGARCH_INSN_SIZE;
}
-struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
+struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)
{
bool extra_pass = false;
u8 *image_ptr, *ro_image_ptr;
diff --git a/arch/mips/net/bpf_jit_comp.c b/arch/mips/net/bpf_jit_comp.c
index d2b6c955f18e..6ee4abe6a1f7 100644
--- a/arch/mips/net/bpf_jit_comp.c
+++ b/arch/mips/net/bpf_jit_comp.c
@@ -909,7 +909,7 @@ bool bpf_jit_needs_zext(void)
return true;
}
-struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
+struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)
{
struct bpf_binary_header *header = NULL;
struct jit_context ctx;
diff --git a/arch/parisc/net/bpf_jit_core.c b/arch/parisc/net/bpf_jit_core.c
index 35dca372b5df..172770132440 100644
--- a/arch/parisc/net/bpf_jit_core.c
+++ b/arch/parisc/net/bpf_jit_core.c
@@ -41,7 +41,7 @@ bool bpf_jit_needs_zext(void)
return true;
}
-struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
+struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)
{
unsigned int prog_size = 0, extable_size = 0;
bool extra_pass = false;
diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c
index 711028bebea3..27fecb4cc063 100644
--- a/arch/powerpc/net/bpf_jit_comp.c
+++ b/arch/powerpc/net/bpf_jit_comp.c
@@ -129,7 +129,7 @@ bool bpf_jit_needs_zext(void)
return true;
}
-struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
+struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *fp)
{
u32 proglen;
u32 alloclen;
diff --git a/arch/riscv/net/bpf_jit_core.c b/arch/riscv/net/bpf_jit_core.c
index 527baa50dc68..768ac686b359 100644
--- a/arch/riscv/net/bpf_jit_core.c
+++ b/arch/riscv/net/bpf_jit_core.c
@@ -41,7 +41,7 @@ bool bpf_jit_needs_zext(void)
return true;
}
-struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
+struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)
{
unsigned int prog_size = 0, extable_size = 0;
bool extra_pass = false;
diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c
index 2dfc279b1be2..94128fe6be23 100644
--- a/arch/s390/net/bpf_jit_comp.c
+++ b/arch/s390/net/bpf_jit_comp.c
@@ -2312,7 +2312,7 @@ static struct bpf_binary_header *bpf_jit_alloc(struct bpf_jit *jit,
/*
* Compile eBPF program "fp"
*/
-struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
+struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *fp)
{
struct bpf_binary_header *header;
struct s390_jit_data *jit_data;
diff --git a/arch/sparc/net/bpf_jit_comp_64.c b/arch/sparc/net/bpf_jit_comp_64.c
index e83e29137566..2fa0e9375127 100644
--- a/arch/sparc/net/bpf_jit_comp_64.c
+++ b/arch/sparc/net/bpf_jit_comp_64.c
@@ -1477,7 +1477,7 @@ struct sparc64_jit_data {
struct jit_ctx ctx;
};
-struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
+struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)
{
struct sparc64_jit_data *jit_data;
struct bpf_binary_header *header;
diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
index 77d00a8dec87..72d9a5faa230 100644
--- a/arch/x86/net/bpf_jit_comp.c
+++ b/arch/x86/net/bpf_jit_comp.c
@@ -3713,7 +3713,7 @@ struct x64_jit_data {
#define MAX_PASSES 20
#define PADDING_PASSES (MAX_PASSES - 5)
-struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
+struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)
{
struct bpf_binary_header *rw_header = NULL;
struct bpf_binary_header *header = NULL;
diff --git a/arch/x86/net/bpf_jit_comp32.c b/arch/x86/net/bpf_jit_comp32.c
index 5f259577614a..852baf2e4db4 100644
--- a/arch/x86/net/bpf_jit_comp32.c
+++ b/arch/x86/net/bpf_jit_comp32.c
@@ -2518,7 +2518,7 @@ bool bpf_jit_needs_zext(void)
return true;
}
-struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
+struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)
{
struct bpf_binary_header *header = NULL;
int proglen, oldproglen = 0;
diff --git a/include/linux/filter.h b/include/linux/filter.h
index d396e55c9a1d..83f37d38c5c1 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -1107,6 +1107,8 @@ static inline int sk_filter_reason(struct sock *sk, struct sk_buff *skb,
return sk_filter_trim_cap(sk, skb, 1, reason);
}
+struct bpf_prog *__bpf_prog_select_runtime(struct bpf_verifier_env *env, struct bpf_prog *fp,
+ int *err);
struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err);
void bpf_prog_free(struct bpf_prog *fp);
@@ -1152,7 +1154,7 @@ u64 __bpf_call_base(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5);
((u64 (*)(u64, u64, u64, u64, u64, const struct bpf_insn *)) \
(void *)__bpf_call_base)
-struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog);
+struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog);
void bpf_jit_compile(struct bpf_prog *prog);
bool bpf_jit_needs_zext(void);
bool bpf_jit_inlines_helper_call(s32 imm);
@@ -1187,12 +1189,25 @@ struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
#ifdef CONFIG_BPF_SYSCALL
struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,
const struct bpf_insn *patch, u32 len);
+struct bpf_insn_aux_data *bpf_dup_insn_aux_data(struct bpf_verifier_env *env);
+void bpf_restore_insn_aux_data(struct bpf_verifier_env *env,
+ struct bpf_insn_aux_data *orig_insn_aux);
#else
static inline struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,
const struct bpf_insn *patch, u32 len)
{
return ERR_PTR(-ENOTSUPP);
}
+
+static inline struct bpf_insn_aux_data *bpf_dup_insn_aux_data(struct bpf_verifier_env *env)
+{
+ return NULL;
+}
+
+static inline void bpf_restore_insn_aux_data(struct bpf_verifier_env *env,
+ struct bpf_insn_aux_data *orig_insn_aux)
+{
+}
#endif /* CONFIG_BPF_SYSCALL */
int bpf_remove_insns(struct bpf_prog *prog, u32 off, u32 cnt);
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index cc61fe57b98d..093ab0f68c81 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -1489,23 +1489,6 @@ void bpf_jit_prog_release_other(struct bpf_prog *fp, struct bpf_prog *fp_other)
bpf_prog_clone_free(fp_other);
}
-static void adjust_insn_arrays(struct bpf_prog *prog, u32 off, u32 len)
-{
-#ifdef CONFIG_BPF_SYSCALL
- struct bpf_map *map;
- int i;
-
- if (len <= 1)
- return;
-
- for (i = 0; i < prog->aux->used_map_cnt; i++) {
- map = prog->aux->used_maps[i];
- if (map->map_type == BPF_MAP_TYPE_INSN_ARRAY)
- bpf_insn_array_adjust(map, off, len);
- }
-#endif
-}
-
/* Now this function is used only to blind the main prog and must be invoked only when
* bpf_prog_need_blind() returns true.
*/
@@ -1577,12 +1560,6 @@ struct bpf_prog *bpf_jit_blind_constants(struct bpf_verifier_env *env, struct bp
if (env)
env->prog = clone;
- else
- /* Instructions arrays must be updated using absolute xlated offsets.
- * The arrays have already been adjusted by bpf_patch_insn_data() when
- * env is not NULL.
- */
- adjust_insn_arrays(clone, i, rewritten);
/* Walk new program and skip insns we just inserted. */
insn = clone->insnsi + i + insn_delta;
@@ -2551,47 +2528,63 @@ static bool bpf_prog_select_interpreter(struct bpf_prog *fp)
return select_interpreter;
}
-static struct bpf_prog *bpf_prog_jit_compile(struct bpf_prog *prog)
+static struct bpf_prog *bpf_prog_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)
{
#ifdef CONFIG_BPF_JIT
bool blinded = false;
struct bpf_prog *orig_prog = prog;
+ struct bpf_insn_aux_data *orig_insn_aux;
if (bpf_prog_need_blind(orig_prog)) {
- prog = bpf_jit_blind_constants(NULL, orig_prog);
+ if (env) {
+ /* If env is not NULL, we are called from the end of bpf_check(), at this
+ * point, only insn_aux_data is used after failure, so we only restore it
+ * here.
+ */
+ orig_insn_aux = bpf_dup_insn_aux_data(env);
+ if (!orig_insn_aux)
+ return orig_prog;
+ }
+ prog = bpf_jit_blind_constants(env, orig_prog);
/* If blinding was requested and we failed during blinding, we must fall
* back to the interpreter.
*/
- if (IS_ERR(prog))
- return orig_prog;
+ if (IS_ERR(prog)) {
+ prog = orig_prog;
+ if (env)
+ goto out_restore;
+ else
+ return prog;
+ }
blinded = true;
}
- prog = bpf_int_jit_compile(prog);
+ prog = bpf_int_jit_compile(env, prog);
if (blinded) {
if (!prog->jited) {
bpf_jit_prog_release_other(orig_prog, prog);
prog = orig_prog;
+ if (env)
+ goto out_restore;
} else {
bpf_jit_prog_release_other(prog, orig_prog);
+ if (env)
+ goto out_free;
}
}
+
+ return prog;
+
+out_restore:
+ bpf_restore_insn_aux_data(env, orig_insn_aux);
+out_free:
+ kvfree(orig_insn_aux);
#endif
return prog;
}
-/**
- * bpf_prog_select_runtime - select exec runtime for BPF program
- * @fp: bpf_prog populated with BPF program
- * @err: pointer to error variable
- *
- * Try to JIT eBPF program, if JIT is not available, use interpreter.
- * The BPF program will be executed via bpf_prog_run() function.
- *
- * Return: the &fp argument along with &err set to 0 for success or
- * a negative errno code on failure
- */
-struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err)
+struct bpf_prog *__bpf_prog_select_runtime(struct bpf_verifier_env *env, struct bpf_prog *fp,
+ int *err)
{
/* In case of BPF to BPF calls, verifier did all the prep
* work with regards to JITing, etc.
@@ -2619,7 +2612,7 @@ struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err)
if (*err)
return fp;
- fp = bpf_prog_jit_compile(fp);
+ fp = bpf_prog_jit_compile(env, fp);
bpf_prog_jit_attempt_done(fp);
if (!fp->jited && jit_needed) {
*err = -ENOTSUPP;
@@ -2645,6 +2638,22 @@ struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err)
return fp;
}
+
+/**
+ * bpf_prog_select_runtime - select exec runtime for BPF program
+ * @fp: bpf_prog populated with BPF program
+ * @err: pointer to error variable
+ *
+ * Try to JIT eBPF program, if JIT is not available, use interpreter.
+ * The BPF program will be executed via bpf_prog_run() function.
+ *
+ * Return: the &fp argument along with &err set to 0 for success or
+ * a negative errno code on failure
+ */
+struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err)
+{
+ return __bpf_prog_select_runtime(NULL, fp, err);
+}
EXPORT_SYMBOL_GPL(bpf_prog_select_runtime);
static unsigned int __bpf_prog_ret1(const void *ctx,
@@ -3132,7 +3141,7 @@ const struct bpf_func_proto bpf_tail_call_proto = {
* It is encouraged to implement bpf_int_jit_compile() instead, so that
* eBPF and implicitly also cBPF can get JITed!
*/
-struct bpf_prog * __weak bpf_int_jit_compile(struct bpf_prog *prog)
+struct bpf_prog * __weak bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)
{
return prog;
}
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index e1505c9cd09e..553dca175640 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -3090,10 +3090,6 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
if (err < 0)
goto free_used_maps;
- prog = bpf_prog_select_runtime(prog, &err);
- if (err < 0)
- goto free_used_maps;
-
err = bpf_prog_mark_insn_arrays_ready(prog);
if (err < 0)
goto free_used_maps;
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 66cef3744fde..5084a754a748 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -22983,7 +22983,7 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
return 0;
}
-static u32 *dup_subprog_starts(struct bpf_verifier_env *env)
+static u32 *bpf_dup_subprog_starts(struct bpf_verifier_env *env)
{
u32 *starts = NULL;
@@ -22995,13 +22995,13 @@ static u32 *dup_subprog_starts(struct bpf_verifier_env *env)
return starts;
}
-static void restore_subprog_starts(struct bpf_verifier_env *env, u32 *orig_starts)
+static void bpf_restore_subprog_starts(struct bpf_verifier_env *env, u32 *orig_starts)
{
for (int i = 0; i < env->subprog_cnt; i++)
env->subprog_info[i].start = orig_starts[i];
}
-static struct bpf_insn_aux_data *dup_insn_aux_data(struct bpf_verifier_env *env)
+struct bpf_insn_aux_data *bpf_dup_insn_aux_data(struct bpf_verifier_env *env)
{
size_t size;
@@ -23009,8 +23009,8 @@ static struct bpf_insn_aux_data *dup_insn_aux_data(struct bpf_verifier_env *env)
return kvmemdup(env->insn_aux_data, size, GFP_KERNEL_ACCOUNT);
}
-static void restore_insn_aux_data(struct bpf_verifier_env *env,
- struct bpf_insn_aux_data *orig_insn_aux)
+void bpf_restore_insn_aux_data(struct bpf_verifier_env *env,
+ struct bpf_insn_aux_data *orig_insn_aux)
{
/* the expanded elements are zero-filled, so no special handling is required */
vfree(env->insn_aux_data);
@@ -23153,7 +23153,7 @@ static int __jit_subprogs(struct bpf_verifier_env *env)
func[i]->aux->might_sleep = env->subprog_info[i].might_sleep;
if (!i)
func[i]->aux->exception_boundary = env->seen_exception;
- func[i] = bpf_int_jit_compile(func[i]);
+ func[i] = bpf_int_jit_compile(env, func[i]);
if (!func[i]->jited) {
err = -ENOTSUPP;
goto out_free;
@@ -23197,7 +23197,7 @@ static int __jit_subprogs(struct bpf_verifier_env *env)
}
for (i = 0; i < env->subprog_cnt; i++) {
old_bpf_func = func[i]->bpf_func;
- tmp = bpf_int_jit_compile(func[i]);
+ tmp = bpf_int_jit_compile(env, func[i]);
if (tmp != func[i] || func[i]->bpf_func != old_bpf_func) {
verbose(env, "JIT doesn't support bpf-to-bpf calls\n");
err = -ENOTSUPP;
@@ -23297,12 +23297,12 @@ static int jit_subprogs(struct bpf_verifier_env *env)
prog = orig_prog = env->prog;
if (bpf_prog_need_blind(orig_prog)) {
- orig_insn_aux = dup_insn_aux_data(env);
+ orig_insn_aux = bpf_dup_insn_aux_data(env);
if (!orig_insn_aux) {
err = -ENOMEM;
goto out_cleanup;
}
- orig_subprog_starts = dup_subprog_starts(env);
+ orig_subprog_starts = bpf_dup_subprog_starts(env);
if (!orig_subprog_starts) {
err = -ENOMEM;
goto out_free_aux;
@@ -23347,8 +23347,8 @@ static int jit_subprogs(struct bpf_verifier_env *env)
return 0;
out_restore:
- restore_subprog_starts(env, orig_subprog_starts);
- restore_insn_aux_data(env, orig_insn_aux);
+ bpf_restore_subprog_starts(env, orig_subprog_starts);
+ bpf_restore_insn_aux_data(env, orig_insn_aux);
kvfree(orig_subprog_starts);
out_free_aux:
kvfree(orig_insn_aux);
@@ -26523,6 +26523,14 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr, __u3
adjust_btf_func(env);
+ /* extension progs temporarily inherit the attach_type of their targets
+ for verification purposes, so set it back to zero before returning
+ */
+ if (env->prog->type == BPF_PROG_TYPE_EXT)
+ env->prog->expected_attach_type = 0;
+
+ env->prog = __bpf_prog_select_runtime(env, env->prog, &ret);
+
err_release_maps:
if (ret)
release_insn_arrays(env);
@@ -26534,12 +26542,6 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr, __u3
if (!env->prog->aux->used_btfs)
release_btfs(env);
- /* extension progs temporarily inherit the attach_type of their targets
- for verification purposes, so set it back to zero before returning
- */
- if (env->prog->type == BPF_PROG_TYPE_EXT)
- env->prog->expected_attach_type = 0;
-
*prog = env->prog;
module_put(env->attach_btf_mod);
--
2.43.0
^ permalink raw reply related
* [PATCH bpf-next v12 5/5] bpf, arm64: Emit BTI for indirect jump target
From: Xu Kuohai @ 2026-04-03 13:28 UTC (permalink / raw)
To: bpf, linux-kernel, linux-arm-kernel
Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Martin KaFai Lau, Eduard Zingerman, Yonghong Song, Puranjay Mohan,
Anton Protopopov, Alexis Lothoré, Shahab Vahedi,
Russell King, Tiezhu Yang, Hengqi Chen, Johan Almbladh,
Paul Burton, Hari Bathini, Christophe Leroy, Naveen N Rao,
Luke Nelson, Xi Wang, Björn Töpel, Pu Lehui,
Ilya Leoshkevich, Heiko Carstens, Vasily Gorbik, David S . Miller,
Wang YanQing
In-Reply-To: <20260403132811.753894-1-xukuohai@huaweicloud.com>
From: Xu Kuohai <xukuohai@huawei.com>
On CPUs that support BTI, the indirect jump selftest triggers a kernel
panic because there is no BTI instructions at the indirect jump targets.
Fix it by emitting a BTI instruction for each indirect jump target.
For reference, below is a sample panic log.
Internal error: Oops - BTI: 0000000036000003 [#1] SMP
...
Call trace:
bpf_prog_2e5f1c71c13ac3e0_big_jump_table+0x54/0xf8 (P)
bpf_prog_run_pin_on_cpu+0x140/0x468
bpf_prog_test_run_syscall+0x280/0x3b8
bpf_prog_test_run+0x22c/0x2c0
Fixes: f4a66cf1cb14 ("bpf: arm64: Add support for indirect jumps")
Reviewed-by: Anton Protopopov <a.s.protopopov@gmail.com>
Acked-by: Leon Hwang <leon.hwang@linux.dev>
Signed-off-by: Xu Kuohai <xukuohai@huawei.com>
---
arch/arm64/net/bpf_jit_comp.c | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c
index 7212ec89dfe3..aa5cd240cdda 100644
--- a/arch/arm64/net/bpf_jit_comp.c
+++ b/arch/arm64/net/bpf_jit_comp.c
@@ -1198,8 +1198,8 @@ static int add_exception_handler(const struct bpf_insn *insn,
* >0 - successfully JITed a 16-byte eBPF instruction.
* <0 - failed to JIT.
*/
-static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
- bool extra_pass)
+static int build_insn(const struct bpf_verifier_env *env, const struct bpf_insn *insn,
+ struct jit_ctx *ctx, bool extra_pass)
{
const u8 code = insn->code;
u8 dst = bpf2a64[insn->dst_reg];
@@ -1224,6 +1224,9 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
int ret;
bool sign_extend;
+ if (bpf_insn_is_indirect_target(env, ctx->prog, i))
+ emit_bti(A64_BTI_J, ctx);
+
switch (code) {
/* dst = src */
case BPF_ALU | BPF_MOV | BPF_X:
@@ -1899,7 +1902,7 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
return 0;
}
-static int build_body(struct jit_ctx *ctx, bool extra_pass)
+static int build_body(struct bpf_verifier_env *env, struct jit_ctx *ctx, bool extra_pass)
{
const struct bpf_prog *prog = ctx->prog;
int i;
@@ -1918,7 +1921,7 @@ static int build_body(struct jit_ctx *ctx, bool extra_pass)
int ret;
ctx->offset[i] = ctx->idx;
- ret = build_insn(insn, ctx, extra_pass);
+ ret = build_insn(env, insn, ctx, extra_pass);
if (ret > 0) {
i++;
ctx->offset[i] = ctx->idx;
@@ -2079,7 +2082,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr
if (build_prologue(&ctx, was_classic))
goto out_off;
- if (build_body(&ctx, extra_pass))
+ if (build_body(env, &ctx, extra_pass))
goto out_off;
ctx.epilogue_offset = ctx.idx;
@@ -2127,7 +2130,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr
/* Dont write body instructions to memory for now */
ctx.write = false;
- if (build_body(&ctx, extra_pass))
+ if (build_body(env, &ctx, extra_pass))
goto out_free_hdr;
ctx.epilogue_offset = ctx.idx;
@@ -2136,7 +2139,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr
ctx.write = true;
/* Pass 3: Adjust jump offset and write final image */
- if (build_body(&ctx, extra_pass) ||
+ if (build_body(env, &ctx, extra_pass) ||
WARN_ON_ONCE(ctx.idx != ctx.epilogue_offset))
goto out_free_hdr;
--
2.43.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox