Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

remoteproc: mediatek: Break lock dependency to `prepare_lock`

A potential circular locking dependency (ABBA deadlock) exists between
`ec_dev->lock` and the clock framework's `prepare_lock`.

The first order (A -> B) occurs when scp_ipi_send() is called while
`ec_dev->lock` is held (e.g., within cros_ec_cmd_xfer()):
1. cros_ec_cmd_xfer() acquires `ec_dev->lock` and calls scp_ipi_send().
2. scp_ipi_send() calls clk_prepare_enable(), which acquires
`prepare_lock`.
See #0 in the following example calling trace.
(Lock Order: `ec_dev->lock` -> `prepare_lock`)

The reverse order (B -> A) is more complex and has been observed
(learned) by lockdep. It involves the clock prepare operation
triggering power domain changes, which then propagates through sysfs
and power supply uevents, eventually calling back into the ChromeOS EC
driver and attempting to acquire `ec_dev->lock`:
1. Something calls clk_prepare(), which acquires `prepare_lock`. It
then triggers genpd operations like genpd_runtime_resume(), which
takes `&genpd->mlock`.
2. Power domain changes can trigger regulator changes; regulator
changes can then trigger device link changes; device link changes
can then trigger sysfs changes. Eventually, power_supply_uevent()
is called.
3. This leads to calls like cros_usbpd_charger_get_prop(), which calls
cros_ec_cmd_xfer_status(), which then attempts to acquire
`ec_dev->lock`.
See #1 ~ #6 in the following example calling trace.
(Lock Order: `prepare_lock` -> `&genpd->mlock` -> ... -> `&ec_dev->lock`)

Move the clk_prepare()/clk_unprepare() operations for `scp->clk` to the
remoteproc prepare()/unprepare() callbacks. This ensures `prepare_lock`
is only acquired in prepare()/unprepare() callbacks. Since
`ec_dev->lock` is not involved in the callbacks, the dependency loop is
broken.

This means the clock is always "prepared" when the SCP is running. The
prolonged "prepared time" for the clock should be acceptable as SCP is
designed to be a very power efficient processor. The power consumption
impact can be negligible.

A simplified calling trace reported by lockdep:
> -> #6 (&ec_dev->lock)
> cros_ec_cmd_xfer
> cros_ec_cmd_xfer_status
> cros_usbpd_charger_get_port_status
> cros_usbpd_charger_get_prop
> power_supply_get_property
> power_supply_show_property
> power_supply_uevent
> dev_uevent
> uevent_show
> dev_attr_show
> sysfs_kf_seq_show
> kernfs_seq_show
> -> #5 (kn->active#2)
> kernfs_drain
> __kernfs_remove
> kernfs_remove_by_name_ns
> sysfs_remove_file_ns
> device_del
> __device_link_del
> device_links_driver_bound
> -> #4 (device_links_lock)
> device_link_remove
> _regulator_put
> regulator_put
> -> #3 (regulator_list_mutex)
> regulator_lock_dependent
> regulator_disable
> scpsys_power_off
> _genpd_power_off
> genpd_power_off
> -> #2 (&genpd->mlock/1)
> genpd_add_subdomain
> pm_genpd_add_subdomain
> scpsys_add_subdomain
> scpsys_probe
> -> #1 (&genpd->mlock)
> genpd_runtime_resume
> __rpm_callback
> rpm_callback
> rpm_resume
> __pm_runtime_resume
> clk_core_prepare
> clk_prepare
> -> #0 (prepare_lock)
> clk_prepare
> scp_ipi_send
> scp_send_ipi
> mtk_rpmsg_send
> rpmsg_send
> cros_ec_pkt_xfer_rpmsg

Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
Reviewed-by: Chen-Yu Tsai <wenst@chromium.org>
Tested-by: Chen-Yu Tsai <wenst@chromium.org>
Link: https://lore.kernel.org/r/20260112110755.2435899-1-tzungbi@kernel.org
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>

authored by

Tzung-Bi Shih and committed by
Mathieu Poirier
d935187c 1aab44c0

+30 -13
+28 -11
drivers/remoteproc/mtk_scp.c
··· 283 283 struct mtk_scp *scp = priv; 284 284 int ret; 285 285 286 - ret = clk_prepare_enable(scp->clk); 286 + ret = clk_enable(scp->clk); 287 287 if (ret) { 288 288 dev_err(scp->dev, "failed to enable clocks\n"); 289 289 return IRQ_NONE; ··· 291 291 292 292 scp->data->scp_irq_handler(scp); 293 293 294 - clk_disable_unprepare(scp->clk); 294 + clk_disable(scp->clk); 295 295 296 296 return IRQ_HANDLED; 297 297 } ··· 665 665 struct device *dev = scp->dev; 666 666 int ret; 667 667 668 - ret = clk_prepare_enable(scp->clk); 668 + ret = clk_enable(scp->clk); 669 669 if (ret) { 670 670 dev_err(dev, "failed to enable clocks\n"); 671 671 return ret; ··· 680 680 681 681 ret = scp_elf_load_segments(rproc, fw); 682 682 leave: 683 - clk_disable_unprepare(scp->clk); 683 + clk_disable(scp->clk); 684 684 685 685 return ret; 686 686 } ··· 691 691 struct device *dev = scp->dev; 692 692 int ret; 693 693 694 - ret = clk_prepare_enable(scp->clk); 694 + ret = clk_enable(scp->clk); 695 695 if (ret) { 696 696 dev_err(dev, "failed to enable clocks\n"); 697 697 return ret; 698 698 } 699 699 700 700 ret = scp_ipi_init(scp, fw); 701 - clk_disable_unprepare(scp->clk); 701 + clk_disable(scp->clk); 702 702 return ret; 703 703 } 704 704 ··· 709 709 struct scp_run *run = &scp->run; 710 710 int ret; 711 711 712 - ret = clk_prepare_enable(scp->clk); 712 + ret = clk_enable(scp->clk); 713 713 if (ret) { 714 714 dev_err(dev, "failed to enable clocks\n"); 715 715 return ret; ··· 734 734 goto stop; 735 735 } 736 736 737 - clk_disable_unprepare(scp->clk); 737 + clk_disable(scp->clk); 738 738 dev_info(dev, "SCP is ready. FW version %s\n", run->fw_ver); 739 739 740 740 return 0; 741 741 742 742 stop: 743 743 scp->data->scp_reset_assert(scp); 744 - clk_disable_unprepare(scp->clk); 744 + clk_disable(scp->clk); 745 745 return ret; 746 746 } 747 747 ··· 909 909 struct mtk_scp *scp = rproc->priv; 910 910 int ret; 911 911 912 - ret = clk_prepare_enable(scp->clk); 912 + ret = clk_enable(scp->clk); 913 913 if (ret) { 914 914 dev_err(scp->dev, "failed to enable clocks\n"); 915 915 return ret; ··· 917 917 918 918 scp->data->scp_reset_assert(scp); 919 919 scp->data->scp_stop(scp); 920 - clk_disable_unprepare(scp->clk); 920 + clk_disable(scp->clk); 921 921 922 922 return 0; 923 923 } 924 924 925 + static int scp_prepare(struct rproc *rproc) 926 + { 927 + struct mtk_scp *scp = rproc->priv; 928 + 929 + return clk_prepare(scp->clk); 930 + } 931 + 932 + static int scp_unprepare(struct rproc *rproc) 933 + { 934 + struct mtk_scp *scp = rproc->priv; 935 + 936 + clk_unprepare(scp->clk); 937 + return 0; 938 + } 939 + 925 940 static const struct rproc_ops scp_ops = { 941 + .prepare = scp_prepare, 942 + .unprepare = scp_unprepare, 926 943 .start = scp_start, 927 944 .stop = scp_stop, 928 945 .load = scp_load,
+2 -2
drivers/remoteproc/mtk_scp_ipi.c
··· 171 171 WARN_ON(len > scp_sizes->ipi_share_buffer_size) || WARN_ON(!buf)) 172 172 return -EINVAL; 173 173 174 - ret = clk_prepare_enable(scp->clk); 174 + ret = clk_enable(scp->clk); 175 175 if (ret) { 176 176 dev_err(scp->dev, "failed to enable clock\n"); 177 177 return ret; ··· 211 211 212 212 unlock_mutex: 213 213 mutex_unlock(&scp->send_lock); 214 - clk_disable_unprepare(scp->clk); 214 + clk_disable(scp->clk); 215 215 216 216 return ret; 217 217 }