From b407460ee99033503993ac7437d593451fcdfe44 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 2 Jun 2023 10:50:36 +0200 Subject: iopoll: Call cpu_relax() in busy loops It is considered good practice to call cpu_relax() in busy loops, see Documentation/process/volatile-considered-harmful.rst. This can not only lower CPU power consumption or yield to a hyperthreaded twin processor, but also allows an architecture to mitigate hardware issues (e.g. ARM Erratum 754327 for Cortex-A9 prior to r2p0) in the architecture-specific cpu_relax() implementation. In addition, cpu_relax() is also a compiler barrier. It is not immediately obvious that the @op argument "function" will result in an actual function call (e.g. in case of inlining). Where a function call is a C sequence point, this is lost on inlining. Therefore, with agressive enough optimization it might be possible for the compiler to hoist the: (val) = op(args); "load" out of the loop because it doesn't see the value changing. The addition of cpu_relax() would inhibit this. As the iopoll helpers lack calls to cpu_relax(), people are sometimes reluctant to use them, and may fall back to open-coded polling loops (including cpu_relax() calls) instead. Fix this by adding calls to cpu_relax() to the iopoll helpers: - For the non-atomic case, it is sufficient to call cpu_relax() in case of a zero sleep-between-reads value, as a call to usleep_range() is a safe barrier otherwise. However, it doesn't hurt to add the call regardless, for simplicity, and for similarity with the atomic case below. - For the atomic case, cpu_relax() must be called regardless of the sleep-between-reads value, as there is no guarantee all architecture-specific implementations of udelay() handle this. Signed-off-by: Geert Uytterhoeven Acked-by: Peter Zijlstra (Intel) Acked-by: Arnd Bergmann Reviewed-by: Tony Lindgren Reviewed-by: Ulf Hansson Link: https://lore.kernel.org/r/45c87bec3397fdd704376807f0eec5cc71be440f.1685692810.git.geert+renesas@glider.be --- include/linux/iopoll.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/iopoll.h b/include/linux/iopoll.h index 2c8860e406bd..0417360a6db9 100644 --- a/include/linux/iopoll.h +++ b/include/linux/iopoll.h @@ -53,6 +53,7 @@ } \ if (__sleep_us) \ usleep_range((__sleep_us >> 2) + 1, __sleep_us); \ + cpu_relax(); \ } \ (cond) ? 0 : -ETIMEDOUT; \ }) @@ -95,6 +96,7 @@ } \ if (__delay_us) \ udelay(__delay_us); \ + cpu_relax(); \ } \ (cond) ? 0 : -ETIMEDOUT; \ }) -- cgit v1.2.3 From 7349a69cf3125e92d48e442d9f400ba446fa314f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 2 Jun 2023 10:50:37 +0200 Subject: iopoll: Do not use timekeeping in read_poll_timeout_atomic() read_poll_timeout_atomic() uses ktime_get() to implement the timeout feature, just like its non-atomic counterpart. However, there are several issues with this, due to its use in atomic contexts: 1. When called in the s2ram path (as typically done by clock or PM domain drivers), timekeeping may be suspended, triggering the WARN_ON(timekeeping_suspended) in ktime_get(): WARNING: CPU: 0 PID: 654 at kernel/time/timekeeping.c:843 ktime_get+0x28/0x78 Calling ktime_get_mono_fast_ns() instead of ktime_get() would get rid of that warning. However, that would break timeout handling, as (at least on systems with an ARM architectured timer), the time returned by ktime_get_mono_fast_ns() does not advance while timekeeping is suspended. Interestingly, (on the same ARM systems) the time returned by ktime_get() does advance while timekeeping is suspended, despite the warning. 2. Depending on the actual clock source, and especially before a high-resolution clocksource (e.g. the ARM architectured timer) becomes available, time may not advance in atomic contexts, thus breaking timeout handling. Fix this by abandoning the idea that one can rely on timekeeping to implement timeout handling in all atomic contexts, and switch from a global time-based to a locally-estimated timeout handling. In most (all?) cases the timeout condition is exceptional and an error condition, hence any additional delays due to underestimating wall clock time are irrelevant. Signed-off-by: Geert Uytterhoeven Acked-by: Arnd Bergmann Reviewed-by: Tony Lindgren Reviewed-by: Ulf Hansson Link: https://lore.kernel.org/r/3d2a2f4e553489392d871108797c3be08f88300b.1685692810.git.geert+renesas@glider.be --- include/linux/iopoll.h | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/iopoll.h b/include/linux/iopoll.h index 0417360a6db9..19a7b00baff4 100644 --- a/include/linux/iopoll.h +++ b/include/linux/iopoll.h @@ -74,6 +74,10 @@ * Returns 0 on success and -ETIMEDOUT upon a timeout. In either * case, the last read value at @args is stored in @val. * + * This macro does not rely on timekeeping. Hence it is safe to call even when + * timekeeping is suspended, at the expense of an underestimation of wall clock + * time, which is rather minimal with a non-zero delay_us. + * * When available, you'll probably want to use one of the specialized * macros defined below rather than this macro directly. */ @@ -81,22 +85,30 @@ delay_before_read, args...) \ ({ \ u64 __timeout_us = (timeout_us); \ + s64 __left_ns = __timeout_us * NSEC_PER_USEC; \ unsigned long __delay_us = (delay_us); \ - ktime_t __timeout = ktime_add_us(ktime_get(), __timeout_us); \ - if (delay_before_read && __delay_us) \ + u64 __delay_ns = __delay_us * NSEC_PER_USEC; \ + if (delay_before_read && __delay_us) { \ udelay(__delay_us); \ + if (__timeout_us) \ + __left_ns -= __delay_ns; \ + } \ for (;;) { \ (val) = op(args); \ if (cond) \ break; \ - if (__timeout_us && \ - ktime_compare(ktime_get(), __timeout) > 0) { \ + if (__timeout_us && __left_ns < 0) { \ (val) = op(args); \ break; \ } \ - if (__delay_us) \ + if (__delay_us) { \ udelay(__delay_us); \ + if (__timeout_us) \ + __left_ns -= __delay_ns; \ + } \ cpu_relax(); \ + if (__timeout_us) \ + __left_ns--; \ } \ (cond) ? 0 : -ETIMEDOUT; \ }) -- cgit v1.2.3 From 33b70fbc4f815f0acb327fa506c988ef25cd943d Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 5 May 2023 13:25:06 +0200 Subject: clk: Introduce clk_hw_determine_rate_no_reparent() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some clock drivers do not want to allow any reparenting on a given clock, but usually do so by not providing any determine_rate implementation. Whenever we call clk_round_rate() or clk_set_rate(), this leads to clk_core_can_round() returning false and thus the rest of the function either forwarding the rate request to its current parent if CLK_SET_RATE_PARENT is set, or just returning the current clock rate. This behaviour happens implicitly, and as we move forward to making a determine_rate implementation required for muxes, we need some way to explicitly opt-in for that behaviour. Fortunately, this is exactly what the clk_core_determine_rate_no_reparent() function is doing, so we can simply make it available to drivers. Cc: Abel Vesa Cc: Alessandro Zummo Cc: Alexandre Belloni Cc: Alexandre Torgue Cc: "Andreas Färber" Cc: AngeloGioacchino Del Regno Cc: Baolin Wang Cc: Charles Keepax Cc: Chen-Yu Tsai Cc: Chen-Yu Tsai Cc: Chunyan Zhang Cc: Claudiu Beznea Cc: Daniel Vetter Cc: David Airlie Cc: David Lechner Cc: Dinh Nguyen Cc: Fabio Estevam Cc: Geert Uytterhoeven Cc: Jaroslav Kysela Cc: Jernej Skrabec Cc: Jonathan Hunter Cc: Kishon Vijay Abraham I Cc: Liam Girdwood Cc: Linus Walleij Cc: Luca Ceresoli Cc: Manivannan Sadhasivam Cc: Mark Brown Cc: Markus Schneider-Pargmann Cc: Max Filippov Cc: Maxime Coquelin Cc: Mikko Perttunen Cc: Miles Chen Cc: Nicolas Ferre Cc: Orson Zhai Cc: Paul Cercueil Cc: Peng Fan Cc: Peter De Schrijver Cc: Prashant Gaikwad Cc: Richard Fitzgerald Cc: Samuel Holland Cc: Sascha Hauer Cc: Sekhar Nori Cc: Shawn Guo Cc: Takashi Iwai Cc: Thierry Reding Cc: Ulf Hansson Cc: Vinod Koul Cc: dri-devel@lists.freedesktop.org Cc: linux-actions@lists.infradead.org Cc: linux-arm-kernel@lists.infradead.org Cc: linux-mips@vger.kernel.org Cc: linux-phy@lists.infradead.org Cc: linux-renesas-soc@vger.kernel.org Cc: linux-rtc@vger.kernel.org Cc: linux-stm32@st-md-mailman.stormreply.com Cc: linux-sunxi@lists.linux.dev Cc: linux-tegra@vger.kernel.org Cc: NXP Linux Team Cc: patches@opensource.cirrus.com Cc: Pengutronix Kernel Team Signed-off-by: Stephen Boyd Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20221018-clk-range-checks-fixes-v4-4-971d5077e7d2@cerno.tech | Reported-by: kernel test robot : --- drivers/clk/clk.c | 19 ++++++ drivers/clk/clk_test.c | 152 +++++++++++++++++++++++++++++++++++++++++++ include/linux/clk-provider.h | 2 + 3 files changed, 173 insertions(+) (limited to 'include/linux') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index f57f821a5e5a..dcc5378f8920 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -783,6 +783,25 @@ int __clk_mux_determine_rate_closest(struct clk_hw *hw, } EXPORT_SYMBOL_GPL(__clk_mux_determine_rate_closest); +/* + * clk_hw_determine_rate_no_reparent - clk_ops::determine_rate implementation for a clk that doesn't reparent + * @hw: mux type clk to determine rate on + * @req: rate request, also used to return preferred frequency + * + * Helper for finding best parent rate to provide a given frequency. + * This can be used directly as a determine_rate callback (e.g. for a + * mux), or from a more complex clock that may combine a mux with other + * operations. + * + * Returns: 0 on success, -EERROR value on error + */ +int clk_hw_determine_rate_no_reparent(struct clk_hw *hw, + struct clk_rate_request *req) +{ + return clk_core_determine_rate_no_reparent(hw, req); +} +EXPORT_SYMBOL_GPL(clk_hw_determine_rate_no_reparent); + /*** clk api ***/ static void clk_core_rate_unprotect(struct clk_core *core) diff --git a/drivers/clk/clk_test.c b/drivers/clk/clk_test.c index 2cb51153750d..b3ed3b0e4c31 100644 --- a/drivers/clk/clk_test.c +++ b/drivers/clk/clk_test.c @@ -141,6 +141,12 @@ static const struct clk_ops clk_multiple_parents_mux_ops = { .determine_rate = __clk_mux_determine_rate_closest, }; +static const struct clk_ops clk_multiple_parents_no_reparent_mux_ops = { + .determine_rate = clk_hw_determine_rate_no_reparent, + .get_parent = clk_multiple_parents_mux_get_parent, + .set_parent = clk_multiple_parents_mux_set_parent, +}; + static int clk_test_init_with_ops(struct kunit *test, const struct clk_ops *ops) { struct clk_dummy_context *ctx; @@ -2395,10 +2401,156 @@ static struct kunit_suite clk_mux_notifier_test_suite = { .test_cases = clk_mux_notifier_test_cases, }; +static int +clk_mux_no_reparent_test_init(struct kunit *test) +{ + struct clk_multiple_parent_ctx *ctx; + const char *parents[2] = { "parent-0", "parent-1"}; + int ret; + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + test->priv = ctx; + + ctx->parents_ctx[0].hw.init = CLK_HW_INIT_NO_PARENT("parent-0", + &clk_dummy_rate_ops, + 0); + ctx->parents_ctx[0].rate = DUMMY_CLOCK_RATE_1; + ret = clk_hw_register(NULL, &ctx->parents_ctx[0].hw); + if (ret) + return ret; + + ctx->parents_ctx[1].hw.init = CLK_HW_INIT_NO_PARENT("parent-1", + &clk_dummy_rate_ops, + 0); + ctx->parents_ctx[1].rate = DUMMY_CLOCK_RATE_2; + ret = clk_hw_register(NULL, &ctx->parents_ctx[1].hw); + if (ret) + return ret; + + ctx->current_parent = 0; + ctx->hw.init = CLK_HW_INIT_PARENTS("test-mux", parents, + &clk_multiple_parents_no_reparent_mux_ops, + 0); + ret = clk_hw_register(NULL, &ctx->hw); + if (ret) + return ret; + + return 0; +} + +static void +clk_mux_no_reparent_test_exit(struct kunit *test) +{ + struct clk_multiple_parent_ctx *ctx = test->priv; + + clk_hw_unregister(&ctx->hw); + clk_hw_unregister(&ctx->parents_ctx[0].hw); + clk_hw_unregister(&ctx->parents_ctx[1].hw); +} + +/* + * Test that if the we have a mux that cannot change parent and we call + * clk_round_rate() on it with a rate that should cause it to change + * parent, it won't. + */ +static void clk_mux_no_reparent_round_rate(struct kunit *test) +{ + struct clk_multiple_parent_ctx *ctx = test->priv; + struct clk_hw *hw = &ctx->hw; + struct clk *clk = clk_hw_get_clk(hw, NULL); + struct clk *other_parent, *parent; + unsigned long other_parent_rate; + unsigned long parent_rate; + long rounded_rate; + + parent = clk_get_parent(clk); + KUNIT_ASSERT_PTR_NE(test, parent, NULL); + + parent_rate = clk_get_rate(parent); + KUNIT_ASSERT_GT(test, parent_rate, 0); + + other_parent = clk_hw_get_clk(&ctx->parents_ctx[1].hw, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, other_parent); + KUNIT_ASSERT_FALSE(test, clk_is_match(parent, other_parent)); + + other_parent_rate = clk_get_rate(other_parent); + KUNIT_ASSERT_GT(test, other_parent_rate, 0); + clk_put(other_parent); + + rounded_rate = clk_round_rate(clk, other_parent_rate); + KUNIT_ASSERT_GT(test, rounded_rate, 0); + KUNIT_EXPECT_EQ(test, rounded_rate, parent_rate); + + clk_put(clk); +} + +/* + * Test that if the we have a mux that cannot change parent and we call + * clk_set_rate() on it with a rate that should cause it to change + * parent, it won't. + */ +static void clk_mux_no_reparent_set_rate(struct kunit *test) +{ + struct clk_multiple_parent_ctx *ctx = test->priv; + struct clk_hw *hw = &ctx->hw; + struct clk *clk = clk_hw_get_clk(hw, NULL); + struct clk *other_parent, *parent; + unsigned long other_parent_rate; + unsigned long parent_rate; + unsigned long rate; + int ret; + + parent = clk_get_parent(clk); + KUNIT_ASSERT_PTR_NE(test, parent, NULL); + + parent_rate = clk_get_rate(parent); + KUNIT_ASSERT_GT(test, parent_rate, 0); + + other_parent = clk_hw_get_clk(&ctx->parents_ctx[1].hw, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, other_parent); + KUNIT_ASSERT_FALSE(test, clk_is_match(parent, other_parent)); + + other_parent_rate = clk_get_rate(other_parent); + KUNIT_ASSERT_GT(test, other_parent_rate, 0); + clk_put(other_parent); + + ret = clk_set_rate(clk, other_parent_rate); + KUNIT_ASSERT_EQ(test, ret, 0); + + rate = clk_get_rate(clk); + KUNIT_ASSERT_GT(test, rate, 0); + KUNIT_EXPECT_EQ(test, rate, parent_rate); + + clk_put(clk); +} + +static struct kunit_case clk_mux_no_reparent_test_cases[] = { + KUNIT_CASE(clk_mux_no_reparent_round_rate), + KUNIT_CASE(clk_mux_no_reparent_set_rate), + {} +}; + +/* + * Test suite for a clock mux that isn't allowed to change parent, using + * the clk_hw_determine_rate_no_reparent() helper. + * + * These tests exercise that helper, and the proper selection of + * rates and parents. + */ +static struct kunit_suite clk_mux_no_reparent_test_suite = { + .name = "clk-mux-no-reparent", + .init = clk_mux_no_reparent_test_init, + .exit = clk_mux_no_reparent_test_exit, + .test_cases = clk_mux_no_reparent_test_cases, +}; + kunit_test_suites( &clk_leaf_mux_set_rate_parent_test_suite, &clk_test_suite, &clk_multiple_parents_mux_test_suite, + &clk_mux_no_reparent_test_suite, &clk_mux_notifier_test_suite, &clk_orphan_transparent_multiple_parent_mux_test_suite, &clk_orphan_transparent_single_parent_test_suite, diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 28ff6f1a6ada..f8f220fb5dab 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -1333,6 +1333,8 @@ int __clk_mux_determine_rate_closest(struct clk_hw *hw, int clk_mux_determine_rate_flags(struct clk_hw *hw, struct clk_rate_request *req, unsigned long flags); +int clk_hw_determine_rate_no_reparent(struct clk_hw *hw, + struct clk_rate_request *req); void clk_hw_reparent(struct clk_hw *hw, struct clk_hw *new_parent); void clk_hw_get_rate_range(struct clk_hw *hw, unsigned long *min_rate, unsigned long *max_rate); -- cgit v1.2.3