Commit 632d3106 authored by Pin-yen Lin's avatar Pin-yen Lin Committed by Rafael J. Wysocki
Browse files

PM: sleep: Do not wait on SYNC_STATE_ONLY device links



Device links with DL_FLAG_SYNC_STATE_ONLY should not affect system
suspend and resume, and functions like device_reorder_to_tail() and
device_link_add() don't try to reorder the consumers with that flag.

However, dpm_wait_for_consumers() and dpm_wait_for_suppliers() don't
check thas flag before triggering dpm_wait(), leading to potential hang
during suspend/resume.

This can be reproduced on MT8186 Corsola Chromebook with devicetree like:

usb-a-connector {
        compatible = "usb-a-connector";
        port {
                usb_a_con: endpoint {
                        remote-endpoint = <&usb_hs>;
                };
        };
};

usb_host {
        compatible = "mediatek,mt8186-xhci", "mediatek,mtk-xhci";
        port {
                usb_hs: endpoint {
                        remote-endpoint = <&usb_a_con>;
                };
        };
};

In this case, the two nodes form a cycle and a SYNC_STATE_ONLY devlink
between usb_host (supplier) and usb-a-connector (consumer) is created.

Address this by exporting device_link_flag_is_sync_state_only() and
making dpm_wait_for_consumers() and dpm_wait_for_suppliers() use it
when deciding if dpm_wait() should be called.

Fixes: 05ef983e ("driver core: Add device link support for SYNC_STATE_ONLY flag")
Signed-off-by: default avatarPin-yen Lin <treapking@chromium.org>
Link: https://patch.msgid.link/20250926102320.4053167-1-treapking@chromium.org


[ rjw: Subject and changelog edits ]
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 3ce3f569
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -248,6 +248,7 @@ void device_links_driver_cleanup(struct device *dev);
void device_links_no_driver(struct device *dev);
bool device_links_busy(struct device *dev);
void device_links_unbind_consumers(struct device *dev);
bool device_link_flag_is_sync_state_only(u32 flags);
void fw_devlink_drivers_done(void);
void fw_devlink_probing_done(void);

+1 −1
Original line number Diff line number Diff line
@@ -287,7 +287,7 @@ static bool device_is_ancestor(struct device *dev, struct device *target)
#define DL_MARKER_FLAGS		(DL_FLAG_INFERRED | \
				 DL_FLAG_CYCLE | \
				 DL_FLAG_MANAGED)
static inline bool device_link_flag_is_sync_state_only(u32 flags)
bool device_link_flag_is_sync_state_only(u32 flags)
{
	return (flags & ~DL_MARKER_FLAGS) == DL_FLAG_SYNC_STATE_ONLY;
}
+4 −2
Original line number Diff line number Diff line
@@ -278,7 +278,8 @@ static void dpm_wait_for_suppliers(struct device *dev, bool async)
	 * walking.
	 */
	dev_for_each_link_to_supplier(link, dev)
		if (READ_ONCE(link->status) != DL_STATE_DORMANT)
		if (READ_ONCE(link->status) != DL_STATE_DORMANT &&
		    !device_link_flag_is_sync_state_only(link->flags))
			dpm_wait(link->supplier, async);

	device_links_read_unlock(idx);
@@ -335,7 +336,8 @@ static void dpm_wait_for_consumers(struct device *dev, bool async)
	 * unregistration).
	 */
	dev_for_each_link_to_consumer(link, dev)
		if (READ_ONCE(link->status) != DL_STATE_DORMANT)
		if (READ_ONCE(link->status) != DL_STATE_DORMANT &&
		    !device_link_flag_is_sync_state_only(link->flags))
			dpm_wait(link->consumer, async);

	device_links_read_unlock(idx);