From: Sasha Levin Date: Mon, 14 Apr 2025 23:18:30 +0000 (-0400) Subject: Fixes for 6.13 X-Git-Tag: v6.12.24~102 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=52b05e9dbd3f0135a6263d71e796e144e20e12c7;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for 6.13 Signed-off-by: Sasha Levin --- diff --git a/queue-6.13/bpf-support-skf_net_off-and-skf_ll_off-on-skb-frags.patch b/queue-6.13/bpf-support-skf_net_off-and-skf_ll_off-on-skb-frags.patch new file mode 100644 index 0000000000..d688dddf76 --- /dev/null +++ b/queue-6.13/bpf-support-skf_net_off-and-skf_ll_off-on-skb-frags.patch @@ -0,0 +1,182 @@ +From d2ced648766665af0074125f556c15d44a1662f8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 8 Apr 2025 09:27:48 -0400 +Subject: bpf: support SKF_NET_OFF and SKF_LL_OFF on skb frags +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Willem de Bruijn + +[ Upstream commit d4bac0288a2b444e468e6df9cb4ed69479ddf14a ] + +Classic BPF socket filters with SKB_NET_OFF and SKB_LL_OFF fail to +read when these offsets extend into frags. + +This has been observed with iwlwifi and reproduced with tun with +IFF_NAPI_FRAGS. The below straightforward socket filter on UDP port, +applied to a RAW socket, will silently miss matching packets. + + const int offset_proto = offsetof(struct ip6_hdr, ip6_nxt); + const int offset_dport = sizeof(struct ip6_hdr) + offsetof(struct udphdr, dest); + struct sock_filter filter_code[] = { + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, SKF_AD_OFF + SKF_AD_PKTTYPE), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, PACKET_HOST, 0, 4), + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, SKF_NET_OFF + offset_proto), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 2), + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, SKF_NET_OFF + offset_dport), + +This is unexpected behavior. Socket filter programs should be +consistent regardless of environment. Silent misses are +particularly concerning as hard to detect. + +Use skb_copy_bits for offsets outside linear, same as done for +non-SKF_(LL|NET) offsets. + +Offset is always positive after subtracting the reference threshold +SKB_(LL|NET)_OFF, so is always >= skb_(mac|network)_offset. The sum of +the two is an offset against skb->data, and may be negative, but it +cannot point before skb->head, as skb_(mac|network)_offset would too. + +This appears to go back to when frag support was introduced to +sk_run_filter in linux-2.4.4, before the introduction of git. + +The amount of code change and 8/16/32 bit duplication are unfortunate. +But any attempt I made to be smarter saved very few LoC while +complicating the code. + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Link: https://lore.kernel.org/netdev/20250122200402.3461154-1-maze@google.com/ +Link: https://elixir.bootlin.com/linux/2.4.4/source/net/core/filter.c#L244 +Reported-by: Matt Moeller +Co-developed-by: Maciej Żenczykowski +Signed-off-by: Maciej Żenczykowski +Signed-off-by: Willem de Bruijn +Acked-by: Stanislav Fomichev +Link: https://lore.kernel.org/r/20250408132833.195491-2-willemdebruijn.kernel@gmail.com +Signed-off-by: Alexei Starovoitov +Signed-off-by: Sasha Levin +--- + net/core/filter.c | 80 ++++++++++++++++++++++++++--------------------- + 1 file changed, 44 insertions(+), 36 deletions(-) + +diff --git a/net/core/filter.c b/net/core/filter.c +index d59a7ea646cad..e61c47765435a 100644 +--- a/net/core/filter.c ++++ b/net/core/filter.c +@@ -218,24 +218,36 @@ BPF_CALL_3(bpf_skb_get_nlattr_nest, struct sk_buff *, skb, u32, a, u32, x) + return 0; + } + ++static int bpf_skb_load_helper_convert_offset(const struct sk_buff *skb, int offset) ++{ ++ if (likely(offset >= 0)) ++ return offset; ++ ++ if (offset >= SKF_NET_OFF) ++ return offset - SKF_NET_OFF + skb_network_offset(skb); ++ ++ if (offset >= SKF_LL_OFF && skb_mac_header_was_set(skb)) ++ return offset - SKF_LL_OFF + skb_mac_offset(skb); ++ ++ return INT_MIN; ++} ++ + BPF_CALL_4(bpf_skb_load_helper_8, const struct sk_buff *, skb, const void *, + data, int, headlen, int, offset) + { +- u8 tmp, *ptr; ++ u8 tmp; + const int len = sizeof(tmp); + +- if (offset >= 0) { +- if (headlen - offset >= len) +- return *(u8 *)(data + offset); +- if (!skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) +- return tmp; +- } else { +- ptr = bpf_internal_load_pointer_neg_helper(skb, offset, len); +- if (likely(ptr)) +- return *(u8 *)ptr; +- } ++ offset = bpf_skb_load_helper_convert_offset(skb, offset); ++ if (offset == INT_MIN) ++ return -EFAULT; + +- return -EFAULT; ++ if (headlen - offset >= len) ++ return *(u8 *)(data + offset); ++ if (!skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) ++ return tmp; ++ else ++ return -EFAULT; + } + + BPF_CALL_2(bpf_skb_load_helper_8_no_cache, const struct sk_buff *, skb, +@@ -248,21 +260,19 @@ BPF_CALL_2(bpf_skb_load_helper_8_no_cache, const struct sk_buff *, skb, + BPF_CALL_4(bpf_skb_load_helper_16, const struct sk_buff *, skb, const void *, + data, int, headlen, int, offset) + { +- __be16 tmp, *ptr; ++ __be16 tmp; + const int len = sizeof(tmp); + +- if (offset >= 0) { +- if (headlen - offset >= len) +- return get_unaligned_be16(data + offset); +- if (!skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) +- return be16_to_cpu(tmp); +- } else { +- ptr = bpf_internal_load_pointer_neg_helper(skb, offset, len); +- if (likely(ptr)) +- return get_unaligned_be16(ptr); +- } ++ offset = bpf_skb_load_helper_convert_offset(skb, offset); ++ if (offset == INT_MIN) ++ return -EFAULT; + +- return -EFAULT; ++ if (headlen - offset >= len) ++ return get_unaligned_be16(data + offset); ++ if (!skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) ++ return be16_to_cpu(tmp); ++ else ++ return -EFAULT; + } + + BPF_CALL_2(bpf_skb_load_helper_16_no_cache, const struct sk_buff *, skb, +@@ -275,21 +285,19 @@ BPF_CALL_2(bpf_skb_load_helper_16_no_cache, const struct sk_buff *, skb, + BPF_CALL_4(bpf_skb_load_helper_32, const struct sk_buff *, skb, const void *, + data, int, headlen, int, offset) + { +- __be32 tmp, *ptr; ++ __be32 tmp; + const int len = sizeof(tmp); + +- if (likely(offset >= 0)) { +- if (headlen - offset >= len) +- return get_unaligned_be32(data + offset); +- if (!skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) +- return be32_to_cpu(tmp); +- } else { +- ptr = bpf_internal_load_pointer_neg_helper(skb, offset, len); +- if (likely(ptr)) +- return get_unaligned_be32(ptr); +- } ++ offset = bpf_skb_load_helper_convert_offset(skb, offset); ++ if (offset == INT_MIN) ++ return -EFAULT; + +- return -EFAULT; ++ if (headlen - offset >= len) ++ return get_unaligned_be32(data + offset); ++ if (!skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) ++ return be32_to_cpu(tmp); ++ else ++ return -EFAULT; + } + + BPF_CALL_2(bpf_skb_load_helper_32_no_cache, const struct sk_buff *, skb, +-- +2.39.5 + diff --git a/queue-6.13/erofs-set-error-to-bio-if-file-backed-io-fails.patch b/queue-6.13/erofs-set-error-to-bio-if-file-backed-io-fails.patch new file mode 100644 index 0000000000..d3fe727641 --- /dev/null +++ b/queue-6.13/erofs-set-error-to-bio-if-file-backed-io-fails.patch @@ -0,0 +1,41 @@ +From a0fdba58767268c2ccc5608926d8dbfcf7b61d07 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 8 Apr 2025 20:23:50 +0800 +Subject: erofs: set error to bio if file-backed IO fails + +From: Sheng Yong + +[ Upstream commit 1595f15391b81815e4ef91c339991913d556c1b6 ] + +If a file-backed IO fails before submitting the bio to the lower +filesystem, an error is returned, but the bio->bi_status is not +marked as an error. However, the error information should be passed +to the end_io handler. Otherwise, the IO request will be treated as +successful. + +Fixes: 283213718f5d ("erofs: support compressed inodes for fileio") +Signed-off-by: Sheng Yong +Reviewed-by: Gao Xiang +Link: https://lore.kernel.org/r/20250408122351.2104507-1-shengyong1@xiaomi.com +Signed-off-by: Gao Xiang +Signed-off-by: Sasha Levin +--- + fs/erofs/fileio.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/fs/erofs/fileio.c b/fs/erofs/fileio.c +index 33f8539dda4ae..17aed5f6c5490 100644 +--- a/fs/erofs/fileio.c ++++ b/fs/erofs/fileio.c +@@ -32,6 +32,8 @@ static void erofs_fileio_ki_complete(struct kiocb *iocb, long ret) + ret = 0; + } + if (rq->bio.bi_end_io) { ++ if (ret < 0 && !rq->bio.bi_status) ++ rq->bio.bi_status = errno_to_blk_status(ret); + rq->bio.bi_end_io(&rq->bio); + } else { + bio_for_each_folio_all(fi, &rq->bio) { +-- +2.39.5 + diff --git a/queue-6.13/ext4-don-t-treat-fhandle-lookup-of-ea_inode-as-fs-co.patch b/queue-6.13/ext4-don-t-treat-fhandle-lookup-of-ea_inode-as-fs-co.patch new file mode 100644 index 0000000000..04852f566e --- /dev/null +++ b/queue-6.13/ext4-don-t-treat-fhandle-lookup-of-ea_inode-as-fs-co.patch @@ -0,0 +1,144 @@ +From ef1c354cc9222245a3191fad2ac9d0690a2f8260 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 Nov 2024 21:20:53 +0100 +Subject: ext4: don't treat fhandle lookup of ea_inode as FS corruption + +From: Jann Horn + +[ Upstream commit 642335f3ea2b3fd6dba03e57e01fa9587843a497 ] + +A file handle that userspace provides to open_by_handle_at() can +legitimately contain an outdated inode number that has since been reused +for another purpose - that's why the file handle also contains a generation +number. + +But if the inode number has been reused for an ea_inode, check_igot_inode() +will notice, __ext4_iget() will go through ext4_error_inode(), and if the +inode was newly created, it will also be marked as bad by iget_failed(). +This all happens before the point where the inode generation is checked. + +ext4_error_inode() is supposed to only be used on filesystem corruption; it +should not be used when userspace just got unlucky with a stale file +handle. So when this happens, let __ext4_iget() just return an error. + +Fixes: b3e6bcb94590 ("ext4: add EA_INODE checking to ext4_iget()") +Signed-off-by: Jann Horn +Reviewed-by: Jan Kara +Link: https://patch.msgid.link/20241129-ext4-ignore-ea-fhandle-v1-1-e532c0d1cee0@google.com +Signed-off-by: Theodore Ts'o +Signed-off-by: Sasha Levin +--- + fs/ext4/inode.c | 68 ++++++++++++++++++++++++++++++++++--------------- + 1 file changed, 48 insertions(+), 20 deletions(-) + +diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c +index 89aade6f45f62..8a8cc29b211c8 100644 +--- a/fs/ext4/inode.c ++++ b/fs/ext4/inode.c +@@ -4705,22 +4705,43 @@ static inline void ext4_inode_set_iversion_queried(struct inode *inode, u64 val) + inode_set_iversion_queried(inode, val); + } + +-static const char *check_igot_inode(struct inode *inode, ext4_iget_flags flags) +- ++static int check_igot_inode(struct inode *inode, ext4_iget_flags flags, ++ const char *function, unsigned int line) + { ++ const char *err_str; ++ + if (flags & EXT4_IGET_EA_INODE) { +- if (!(EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) +- return "missing EA_INODE flag"; ++ if (!(EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) { ++ err_str = "missing EA_INODE flag"; ++ goto error; ++ } + if (ext4_test_inode_state(inode, EXT4_STATE_XATTR) || +- EXT4_I(inode)->i_file_acl) +- return "ea_inode with extended attributes"; ++ EXT4_I(inode)->i_file_acl) { ++ err_str = "ea_inode with extended attributes"; ++ goto error; ++ } + } else { +- if ((EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) +- return "unexpected EA_INODE flag"; ++ if ((EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) { ++ /* ++ * open_by_handle_at() could provide an old inode number ++ * that has since been reused for an ea_inode; this does ++ * not indicate filesystem corruption ++ */ ++ if (flags & EXT4_IGET_HANDLE) ++ return -ESTALE; ++ err_str = "unexpected EA_INODE flag"; ++ goto error; ++ } ++ } ++ if (is_bad_inode(inode) && !(flags & EXT4_IGET_BAD)) { ++ err_str = "unexpected bad inode w/o EXT4_IGET_BAD"; ++ goto error; + } +- if (is_bad_inode(inode) && !(flags & EXT4_IGET_BAD)) +- return "unexpected bad inode w/o EXT4_IGET_BAD"; +- return NULL; ++ return 0; ++ ++error: ++ ext4_error_inode(inode, function, line, 0, err_str); ++ return -EFSCORRUPTED; + } + + struct inode *__ext4_iget(struct super_block *sb, unsigned long ino, +@@ -4732,7 +4753,6 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino, + struct ext4_inode_info *ei; + struct ext4_super_block *es = EXT4_SB(sb)->s_es; + struct inode *inode; +- const char *err_str; + journal_t *journal = EXT4_SB(sb)->s_journal; + long ret; + loff_t size; +@@ -4761,10 +4781,10 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino, + if (!inode) + return ERR_PTR(-ENOMEM); + if (!(inode->i_state & I_NEW)) { +- if ((err_str = check_igot_inode(inode, flags)) != NULL) { +- ext4_error_inode(inode, function, line, 0, err_str); ++ ret = check_igot_inode(inode, flags, function, line); ++ if (ret) { + iput(inode); +- return ERR_PTR(-EFSCORRUPTED); ++ return ERR_PTR(ret); + } + return inode; + } +@@ -5036,13 +5056,21 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino, + ret = -EFSCORRUPTED; + goto bad_inode; + } +- if ((err_str = check_igot_inode(inode, flags)) != NULL) { +- ext4_error_inode(inode, function, line, 0, err_str); +- ret = -EFSCORRUPTED; +- goto bad_inode; ++ ret = check_igot_inode(inode, flags, function, line); ++ /* ++ * -ESTALE here means there is nothing inherently wrong with the inode, ++ * it's just not an inode we can return for an fhandle lookup. ++ */ ++ if (ret == -ESTALE) { ++ brelse(iloc.bh); ++ unlock_new_inode(inode); ++ iput(inode); ++ return ERR_PTR(-ESTALE); + } +- ++ if (ret) ++ goto bad_inode; + brelse(iloc.bh); ++ + unlock_new_inode(inode); + return inode; + +-- +2.39.5 + diff --git a/queue-6.13/pwm-fsl-ftm-handle-clk_get_rate-returning-0.patch b/queue-6.13/pwm-fsl-ftm-handle-clk_get_rate-returning-0.patch new file mode 100644 index 0000000000..f6d027712a --- /dev/null +++ b/queue-6.13/pwm-fsl-ftm-handle-clk_get_rate-returning-0.patch @@ -0,0 +1,53 @@ +From d641aea369a45a8aa668e1b0731a5abc755d60f1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 1 Apr 2025 12:29:01 +0200 +Subject: pwm: fsl-ftm: Handle clk_get_rate() returning 0 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Uwe Kleine-König + +[ Upstream commit 928446a5302eee30ebb32075c0db5dda5a138fb7 ] + +Considering that the driver doesn't enable the used clocks (and also +that clk_get_rate() returns 0 if CONFIG_HAVE_CLK is unset) better check +the return value of clk_get_rate() for being non-zero before dividing by +it. + +Fixes: 3479bbd1e1f8 ("pwm: fsl-ftm: More relaxed permissions for updating period") +Signed-off-by: Uwe Kleine-König +Link: https://lore.kernel.org/r/b68351a51017035651bc62ad3146afcb706874f0.1743501688.git.u.kleine-koenig@baylibre.com +Signed-off-by: Uwe Kleine-König +Signed-off-by: Sasha Levin +--- + drivers/pwm/pwm-fsl-ftm.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/drivers/pwm/pwm-fsl-ftm.c b/drivers/pwm/pwm-fsl-ftm.c +index 2510c10ca4730..c45a5fca4cbbd 100644 +--- a/drivers/pwm/pwm-fsl-ftm.c ++++ b/drivers/pwm/pwm-fsl-ftm.c +@@ -118,6 +118,9 @@ static unsigned int fsl_pwm_ticks_to_ns(struct fsl_pwm_chip *fpc, + unsigned long long exval; + + rate = clk_get_rate(fpc->clk[fpc->period.clk_select]); ++ if (rate >> fpc->period.clk_ps == 0) ++ return 0; ++ + exval = ticks; + exval *= 1000000000UL; + do_div(exval, rate >> fpc->period.clk_ps); +@@ -190,6 +193,9 @@ static unsigned int fsl_pwm_calculate_duty(struct fsl_pwm_chip *fpc, + unsigned int period = fpc->period.mod_period + 1; + unsigned int period_ns = fsl_pwm_ticks_to_ns(fpc, period); + ++ if (!period_ns) ++ return 0; ++ + duty = (unsigned long long)duty_ns * period; + do_div(duty, period_ns); + +-- +2.39.5 + diff --git a/queue-6.13/pwm-mediatek-prevent-divide-by-zero-in-pwm_mediatek_.patch b/queue-6.13/pwm-mediatek-prevent-divide-by-zero-in-pwm_mediatek_.patch new file mode 100644 index 0000000000..756ede9467 --- /dev/null +++ b/queue-6.13/pwm-mediatek-prevent-divide-by-zero-in-pwm_mediatek_.patch @@ -0,0 +1,75 @@ +From eedd683b551888a6066bf8dcd61013025197968e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 1 Apr 2025 12:28:59 +0200 +Subject: pwm: mediatek: Prevent divide-by-zero in pwm_mediatek_config() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Josh Poimboeuf + +[ Upstream commit 7ca59947b5fcf94e7ea4029d1bd0f7c41500a161 ] + +With CONFIG_COMPILE_TEST && !CONFIG_HAVE_CLK, pwm_mediatek_config() has a +divide-by-zero in the following line: + + do_div(resolution, clk_get_rate(pc->clk_pwms[pwm->hwpwm])); + +due to the fact that the !CONFIG_HAVE_CLK version of clk_get_rate() +returns zero. + +This is presumably just a theoretical problem: COMPILE_TEST overrides +the dependency on RALINK which would select COMMON_CLK. Regardless it's +a good idea to check for the error explicitly to avoid divide-by-zero. + +Fixes the following warning: + + drivers/pwm/pwm-mediatek.o: warning: objtool: .text: unexpected end of section + +Signed-off-by: Josh Poimboeuf +Link: https://lore.kernel.org/r/fb56444939325cc173e752ba199abd7aeae3bf12.1742852847.git.jpoimboe@kernel.org +[ukleinek: s/CONFIG_CLK/CONFIG_HAVE_CLK/] +Fixes: caf065f8fd58 ("pwm: Add MediaTek PWM support") +Signed-off-by: Uwe Kleine-König +Link: https://lore.kernel.org/r/9e78a0796acba3435553ed7db1c7965dcffa6215.1743501688.git.u.kleine-koenig@baylibre.com +Signed-off-by: Uwe Kleine-König +Signed-off-by: Sasha Levin +--- + drivers/pwm/pwm-mediatek.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c +index 01dfa0fab80a4..7eaab58314995 100644 +--- a/drivers/pwm/pwm-mediatek.c ++++ b/drivers/pwm/pwm-mediatek.c +@@ -121,21 +121,25 @@ static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip); + u32 clkdiv = 0, cnt_period, cnt_duty, reg_width = PWMDWIDTH, + reg_thres = PWMTHRES; ++ unsigned long clk_rate; + u64 resolution; + int ret; + + ret = pwm_mediatek_clk_enable(chip, pwm); +- + if (ret < 0) + return ret; + ++ clk_rate = clk_get_rate(pc->clk_pwms[pwm->hwpwm]); ++ if (!clk_rate) ++ return -EINVAL; ++ + /* Make sure we use the bus clock and not the 26MHz clock */ + if (pc->soc->has_ck_26m_sel) + writel(0, pc->regs + PWM_CK_26M_SEL); + + /* Using resolution in picosecond gets accuracy higher */ + resolution = (u64)NSEC_PER_SEC * 1000; +- do_div(resolution, clk_get_rate(pc->clk_pwms[pwm->hwpwm])); ++ do_div(resolution, clk_rate); + + cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, resolution); + while (cnt_period > 8191) { +-- +2.39.5 + diff --git a/queue-6.13/pwm-rcar-improve-register-calculation.patch b/queue-6.13/pwm-rcar-improve-register-calculation.patch new file mode 100644 index 0000000000..803c0b9813 --- /dev/null +++ b/queue-6.13/pwm-rcar-improve-register-calculation.patch @@ -0,0 +1,93 @@ +From 0af635d2a1b5767eb99496ac6418f3558a4b71ad Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 1 Apr 2025 12:29:00 +0200 +Subject: pwm: rcar: Improve register calculation +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Uwe Kleine-König + +[ Upstream commit e7327c193014a4d8666e9c1cda09cf2c060518e8 ] + +There were several issues in the function rcar_pwm_set_counter(): + + - The u64 values period_ns and duty_ns were cast to int on function + call which might loose bits on 32 bit architectures. + Fix: Make parameters to rcar_pwm_set_counter() u64 + - The algorithm divided by the result of a division which looses + precision. + Fix: Make use of mul_u64_u64_div_u64() + - The calculated values were just masked to fit the respective register + fields which again might loose bits. + Fix: Explicitly check for overlow + +Implement the respective fixes. + +A side effect of fixing the 2nd issue is that there is no division by 0 +if clk_get_rate() returns 0. + +Fixes: ed6c1476bf7f ("pwm: Add support for R-Car PWM Timer") +Signed-off-by: Uwe Kleine-König +Link: https://lore.kernel.org/r/ab3dac794b2216cc1cc56d65c93dd164f8bd461b.1743501688.git.u.kleine-koenig@baylibre.com +[ukleinek: Added an explicit #include to please the +0day build bot] +Link: https://lore.kernel.org/oe-kbuild-all/202504031354.VJtxScP5-lkp@intel.com/ +Reviewed-by: Geert Uytterhoeven +Signed-off-by: Uwe Kleine-König +Signed-off-by: Sasha Levin +--- + drivers/pwm/pwm-rcar.c | 24 +++++++++++++----------- + 1 file changed, 13 insertions(+), 11 deletions(-) + +diff --git a/drivers/pwm/pwm-rcar.c b/drivers/pwm/pwm-rcar.c +index 2261789cc27da..578dbdd2d5a72 100644 +--- a/drivers/pwm/pwm-rcar.c ++++ b/drivers/pwm/pwm-rcar.c +@@ -8,6 +8,7 @@ + * - The hardware cannot generate a 0% duty cycle. + */ + ++#include + #include + #include + #include +@@ -102,23 +103,24 @@ static void rcar_pwm_set_clock_control(struct rcar_pwm_chip *rp, + rcar_pwm_write(rp, value, RCAR_PWMCR); + } + +-static int rcar_pwm_set_counter(struct rcar_pwm_chip *rp, int div, int duty_ns, +- int period_ns) ++static int rcar_pwm_set_counter(struct rcar_pwm_chip *rp, int div, u64 duty_ns, ++ u64 period_ns) + { +- unsigned long long one_cycle, tmp; /* 0.01 nanoseconds */ ++ unsigned long long tmp; + unsigned long clk_rate = clk_get_rate(rp->clk); + u32 cyc, ph; + +- one_cycle = NSEC_PER_SEC * 100ULL << div; +- do_div(one_cycle, clk_rate); ++ /* div <= 24 == RCAR_PWM_MAX_DIVISION, so the shift doesn't overflow. */ ++ tmp = mul_u64_u64_div_u64(period_ns, clk_rate, (u64)NSEC_PER_SEC << div); ++ if (tmp > FIELD_MAX(RCAR_PWMCNT_CYC0_MASK)) ++ tmp = FIELD_MAX(RCAR_PWMCNT_CYC0_MASK); + +- tmp = period_ns * 100ULL; +- do_div(tmp, one_cycle); +- cyc = (tmp << RCAR_PWMCNT_CYC0_SHIFT) & RCAR_PWMCNT_CYC0_MASK; ++ cyc = FIELD_PREP(RCAR_PWMCNT_CYC0_MASK, tmp); + +- tmp = duty_ns * 100ULL; +- do_div(tmp, one_cycle); +- ph = tmp & RCAR_PWMCNT_PH0_MASK; ++ tmp = mul_u64_u64_div_u64(duty_ns, clk_rate, (u64)NSEC_PER_SEC << div); ++ if (tmp > FIELD_MAX(RCAR_PWMCNT_PH0_MASK)) ++ tmp = FIELD_MAX(RCAR_PWMCNT_PH0_MASK); ++ ph = FIELD_PREP(RCAR_PWMCNT_PH0_MASK, tmp); + + /* Avoid prohibited setting */ + if (cyc == 0 || ph == 0) +-- +2.39.5 + diff --git a/queue-6.13/pwm-stm32-search-an-appropriate-duty_cycle-if-period.patch b/queue-6.13/pwm-stm32-search-an-appropriate-duty_cycle-if-period.patch new file mode 100644 index 0000000000..3bf1fce139 --- /dev/null +++ b/queue-6.13/pwm-stm32-search-an-appropriate-duty_cycle-if-period.patch @@ -0,0 +1,63 @@ +From 2200a865e05e31367b8bfbd60eab71522007ee31 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 5 Apr 2025 11:27:13 +0200 +Subject: pwm: stm32: Search an appropriate duty_cycle if period cannot be + modified +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Uwe Kleine-König + +[ Upstream commit fda6e0034e9da64e1cec31f4539b6c7abd9ed8be ] + +If another channel is already enabled period must not be modified. If +the requested period is smaller than this unchangable period the driver +is still supposed to search a duty_cycle according to the usual rounding +rules. + +So don't set the duty_cycle to 0 but continue to determine an +appropriate value for ccr. + +Fixes: deaba9cff809 ("pwm: stm32: Implementation of the waveform callbacks") +Signed-off-by: Uwe Kleine-König +Link: https://lore.kernel.org/r/f0c50df31daa3d6069bfa8d7fb3e71fae241b026.1743844730.git.u.kleine-koenig@baylibre.com +Signed-off-by: Uwe Kleine-König +Signed-off-by: Sasha Levin +--- + drivers/pwm/pwm-stm32.c | 12 +++--------- + 1 file changed, 3 insertions(+), 9 deletions(-) + +diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c +index a59de4de18b6e..ec2c05c9ee7a6 100644 +--- a/drivers/pwm/pwm-stm32.c ++++ b/drivers/pwm/pwm-stm32.c +@@ -103,22 +103,16 @@ static int stm32_pwm_round_waveform_tohw(struct pwm_chip *chip, + if (ret) + goto out; + +- /* +- * calculate the best value for ARR for the given PSC, refuse if +- * the resulting period gets bigger than the requested one. +- */ + arr = mul_u64_u64_div_u64(wf->period_length_ns, rate, + (u64)NSEC_PER_SEC * (wfhw->psc + 1)); + if (arr <= wfhw->arr) { + /* +- * requested period is small than the currently ++ * requested period is smaller than the currently + * configured and unchangable period, report back the smallest +- * possible period, i.e. the current state; Initialize +- * ccr to anything valid. ++ * possible period, i.e. the current state and return 1 ++ * to indicate the wrong rounding direction. + */ +- wfhw->ccr = 0; + ret = 1; +- goto out; + } + + } else { +-- +2.39.5 + diff --git a/queue-6.13/series b/queue-6.13/series index dd4c9ee804..11a881e41d 100644 --- a/queue-6.13/series +++ b/queue-6.13/series @@ -163,3 +163,10 @@ tracing-probe-events-add-comments-about-entry-data-s.patch ktest-fix-test-failures-due-to-missing-log_file-dire.patch tpm-tpm_tis-workaround-failed-command-reception-on-i.patch tpm-end-any-active-auth-session-before-shutdown.patch +pwm-mediatek-prevent-divide-by-zero-in-pwm_mediatek_.patch +pwm-rcar-improve-register-calculation.patch +pwm-fsl-ftm-handle-clk_get_rate-returning-0.patch +pwm-stm32-search-an-appropriate-duty_cycle-if-period.patch +erofs-set-error-to-bio-if-file-backed-io-fails.patch +bpf-support-skf_net_off-and-skf_ll_off-on-skb-frags.patch +ext4-don-t-treat-fhandle-lookup-of-ea_inode-as-fs-co.patch