Received: by 2002:a05:6a10:206:0:0:0:0 with SMTP id 6csp2474740pxj; Mon, 10 May 2021 04:01:05 -0700 (PDT) X-Google-Smtp-Source: ABdhPJz+W3mRWxKm9urisQwRmD1yBhndsoYLvLLdk6cjQ55Kpk6nohjRxuvWHe9hfe3AzegiJIk+ X-Received: by 2002:a92:280b:: with SMTP id l11mr20760306ilf.111.1620644465601; Mon, 10 May 2021 04:01:05 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1620644465; cv=none; d=google.com; s=arc-20160816; b=Rg6C8vdJIf1Wzg/KOHL7M9xo+dBM+Rj14modH2hABw2QXRDtVJZ3Cyf0xus54I9AHw oH6V+SN9AnFnCLz56raesRd8bMIxkLR0+kJyCputZ+jiU/26Ko1b+yWTrlZkyWQ699+s O6QN4TRHaEwqFIIK9vRj91Z6R94VnEV1Dj5T1dvhTCOD5uqfBTHvCYb69uu9U3xdnBAE 8cilDML3wKk/0OTMaLrQFLdzY9iUoLuJI2s2a/TojOHc8X8PBniw9qtzK4qnbfYKXTQ1 ymwi/c3SBVqhk3pdn2WRWeVwbkecngtaPk7se1+aBY4/ZDxinCB1vYhudi6Lcd4egnjJ maTA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :user-agent:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature; bh=6WXSclRD8He0qnTk9bXklr86AxrBAjO6XeywEAX3bOc=; b=fqwqKETHGWV/xHM9b1jZefzVNpUclYWLYzQXac4KEL5tAmKAtgI2IvlQnpbJtZKS4E XL5Qfa2W6v+X5XihUBqDnhjD5KOjJL87SS5ZS2y6jN9WquDn8hs9hdIKXMw5NqxfquAz OeIUc1ANHuXID/in8BJn9ecqCel6v3Rynh/r1x1n2DWNfBJg9EExz7yjgdSTALd/C2bC R77qkQ3x057TjF1DqmOGA9aFhulRVNWMteJfDQybzpkS5cxTEO5atsnCPuTcuG89U37e 6OyDEFZwrukjkYV7jMWUCLx2fMoj3ts+z7MFCXaQUK7vZRln9L+1Q72jr3CUWIcuCCbR 9SXA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linuxfoundation.org header.s=korg header.b=0yIA4m5P; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linuxfoundation.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id s4si17590154ilv.33.2021.05.10.04.00.53; Mon, 10 May 2021 04:01:05 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@linuxfoundation.org header.s=korg header.b=0yIA4m5P; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linuxfoundation.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232660AbhEJKxP (ORCPT + 99 others); Mon, 10 May 2021 06:53:15 -0400 Received: from mail.kernel.org ([198.145.29.99]:52050 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232346AbhEJKm1 (ORCPT ); Mon, 10 May 2021 06:42:27 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 18D1B6197E; Mon, 10 May 2021 10:32:35 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=linuxfoundation.org; s=korg; t=1620642756; bh=5OVvrnC8jH0TfWs42w/y/6vVei0Op2jAom7ehhJxJ3I=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=0yIA4m5PLlLk4fWJR4SyeUW3FUbB3H7awQGxiGB0/rLxx2kBvUOrwBpZmhEju+2yU cjunoG/nKgkcg9r9wRrbXQxLGPznysHGGcA+E7pdFQJ9bwMEqlRdGZ4haTbT5ikYR1 nMfXoDiqpx+f8nH4GnkCeolEDK2Ez5Xmorh/4s6g= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Kiwoong Kim , Ulf Hansson , Linus Walleij Subject: [PATCH 5.10 037/299] mmc: core: Fix hanging on I/O during system suspend for removable cards Date: Mon, 10 May 2021 12:17:14 +0200 Message-Id: <20210510102006.071454006@linuxfoundation.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20210510102004.821838356@linuxfoundation.org> References: <20210510102004.821838356@linuxfoundation.org> User-Agent: quilt/0.66 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Ulf Hansson commit 17a17bf50612e6048a9975450cf1bd30f93815b5 upstream. The mmc core uses a PM notifier to temporarily during system suspend, turn off the card detection mechanism for removal/insertion of (e)MMC/SD/SDIO cards. Additionally, the notifier may be used to remove an SDIO card entirely, if a corresponding SDIO functional driver don't have the system suspend/resume callbacks assigned. This behaviour has been around for a very long time. However, a recent bug report tells us there are problems with this approach. More precisely, when receiving the PM_SUSPEND_PREPARE notification, we may end up hanging on I/O to be completed, thus also preventing the system from getting suspended. In the end what happens, is that the cancel_delayed_work_sync() in mmc_pm_notify() ends up waiting for mmc_rescan() to complete - and since mmc_rescan() wants to claim the host, it needs to wait for the I/O to be completed first. Typically, this problem is triggered in Android, if there is ongoing I/O while the user decides to suspend, resume and then suspend the system again. This due to that after the resume, an mmc_rescan() work gets punted to the workqueue, which job is to verify that the card remains inserted after the system has resumed. To fix this problem, userspace needs to become frozen to suspend the I/O, prior to turning off the card detection mechanism. Therefore, let's drop the PM notifiers for mmc subsystem altogether and rely on the card detection to be turned off/on as a part of the system_freezable_wq, that we are already using. Moreover, to allow and SDIO card to be removed during system suspend, let's manage this from a ->prepare() callback, assigned at the mmc_host_class level. In this way, we can use the parent device (the mmc_host_class device), to remove the card device that is the child, in the device_prepare() phase. Reported-by: Kiwoong Kim Cc: stable@vger.kernel.org # v4.5+ Signed-off-by: Ulf Hansson Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20210310152900.149380-1-ulf.hansson@linaro.org Reviewed-by: Kiwoong Kim Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/core/core.c | 74 ----------------------------------------------- drivers/mmc/core/core.h | 8 ----- drivers/mmc/core/host.c | 40 +++++++++++++++++++++++-- drivers/mmc/core/sdio.c | 28 +++++++++++++---- include/linux/mmc/host.h | 3 - 5 files changed, 59 insertions(+), 94 deletions(-) --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2355,80 +2355,6 @@ void mmc_stop_host(struct mmc_host *host mmc_release_host(host); } -#ifdef CONFIG_PM_SLEEP -/* Do the card removal on suspend if card is assumed removeable - * Do that in pm notifier while userspace isn't yet frozen, so we will be able - to sync the card. -*/ -static int mmc_pm_notify(struct notifier_block *notify_block, - unsigned long mode, void *unused) -{ - struct mmc_host *host = container_of( - notify_block, struct mmc_host, pm_notify); - unsigned long flags; - int err = 0; - - switch (mode) { - case PM_HIBERNATION_PREPARE: - case PM_SUSPEND_PREPARE: - case PM_RESTORE_PREPARE: - spin_lock_irqsave(&host->lock, flags); - host->rescan_disable = 1; - spin_unlock_irqrestore(&host->lock, flags); - cancel_delayed_work_sync(&host->detect); - - if (!host->bus_ops) - break; - - /* Validate prerequisites for suspend */ - if (host->bus_ops->pre_suspend) - err = host->bus_ops->pre_suspend(host); - if (!err) - break; - - if (!mmc_card_is_removable(host)) { - dev_warn(mmc_dev(host), - "pre_suspend failed for non-removable host: " - "%d\n", err); - /* Avoid removing non-removable hosts */ - break; - } - - /* Calling bus_ops->remove() with a claimed host can deadlock */ - host->bus_ops->remove(host); - mmc_claim_host(host); - mmc_detach_bus(host); - mmc_power_off(host); - mmc_release_host(host); - host->pm_flags = 0; - break; - - case PM_POST_SUSPEND: - case PM_POST_HIBERNATION: - case PM_POST_RESTORE: - - spin_lock_irqsave(&host->lock, flags); - host->rescan_disable = 0; - spin_unlock_irqrestore(&host->lock, flags); - _mmc_detect_change(host, 0, false); - - } - - return 0; -} - -void mmc_register_pm_notifier(struct mmc_host *host) -{ - host->pm_notify.notifier_call = mmc_pm_notify; - register_pm_notifier(&host->pm_notify); -} - -void mmc_unregister_pm_notifier(struct mmc_host *host) -{ - unregister_pm_notifier(&host->pm_notify); -} -#endif - static int __init mmc_init(void) { int ret; --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -94,14 +94,6 @@ int mmc_execute_tuning(struct mmc_card * int mmc_hs200_to_hs400(struct mmc_card *card); int mmc_hs400_to_hs200(struct mmc_card *card); -#ifdef CONFIG_PM_SLEEP -void mmc_register_pm_notifier(struct mmc_host *host); -void mmc_unregister_pm_notifier(struct mmc_host *host); -#else -static inline void mmc_register_pm_notifier(struct mmc_host *host) { } -static inline void mmc_unregister_pm_notifier(struct mmc_host *host) { } -#endif - void mmc_wait_for_req_done(struct mmc_host *host, struct mmc_request *mrq); bool mmc_is_req_done(struct mmc_host *host, struct mmc_request *mrq); --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -34,6 +34,42 @@ static DEFINE_IDA(mmc_host_ida); +#ifdef CONFIG_PM_SLEEP +static int mmc_host_class_prepare(struct device *dev) +{ + struct mmc_host *host = cls_dev_to_mmc_host(dev); + + /* + * It's safe to access the bus_ops pointer, as both userspace and the + * workqueue for detecting cards are frozen at this point. + */ + if (!host->bus_ops) + return 0; + + /* Validate conditions for system suspend. */ + if (host->bus_ops->pre_suspend) + return host->bus_ops->pre_suspend(host); + + return 0; +} + +static void mmc_host_class_complete(struct device *dev) +{ + struct mmc_host *host = cls_dev_to_mmc_host(dev); + + _mmc_detect_change(host, 0, false); +} + +static const struct dev_pm_ops mmc_host_class_dev_pm_ops = { + .prepare = mmc_host_class_prepare, + .complete = mmc_host_class_complete, +}; + +#define MMC_HOST_CLASS_DEV_PM_OPS (&mmc_host_class_dev_pm_ops) +#else +#define MMC_HOST_CLASS_DEV_PM_OPS NULL +#endif + static void mmc_host_classdev_release(struct device *dev) { struct mmc_host *host = cls_dev_to_mmc_host(dev); @@ -45,6 +81,7 @@ static void mmc_host_classdev_release(st static struct class mmc_host_class = { .name = "mmc_host", .dev_release = mmc_host_classdev_release, + .pm = MMC_HOST_CLASS_DEV_PM_OPS, }; int mmc_register_host_class(void) @@ -493,8 +530,6 @@ int mmc_add_host(struct mmc_host *host) #endif mmc_start_host(host); - mmc_register_pm_notifier(host); - return 0; } @@ -510,7 +545,6 @@ EXPORT_SYMBOL(mmc_add_host); */ void mmc_remove_host(struct mmc_host *host) { - mmc_unregister_pm_notifier(host); mmc_stop_host(host); #ifdef CONFIG_DEBUG_FS --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -985,21 +985,37 @@ out: */ static int mmc_sdio_pre_suspend(struct mmc_host *host) { - int i, err = 0; + int i; for (i = 0; i < host->card->sdio_funcs; i++) { struct sdio_func *func = host->card->sdio_func[i]; if (func && sdio_func_present(func) && func->dev.driver) { const struct dev_pm_ops *pmops = func->dev.driver->pm; - if (!pmops || !pmops->suspend || !pmops->resume) { + if (!pmops || !pmops->suspend || !pmops->resume) /* force removal of entire card in that case */ - err = -ENOSYS; - break; - } + goto remove; } } - return err; + return 0; + +remove: + if (!mmc_card_is_removable(host)) { + dev_warn(mmc_dev(host), + "missing suspend/resume ops for non-removable SDIO card\n"); + /* Don't remove a non-removable card - we can't re-detect it. */ + return 0; + } + + /* Remove the SDIO card and let it be re-detected later on. */ + mmc_sdio_remove(host); + mmc_claim_host(host); + mmc_detach_bus(host); + mmc_power_off(host); + mmc_release_host(host); + host->pm_flags = 0; + + return 0; } /* --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -285,9 +285,6 @@ struct mmc_host { u32 ocr_avail_sdio; /* SDIO-specific OCR */ u32 ocr_avail_sd; /* SD-specific OCR */ u32 ocr_avail_mmc; /* MMC-specific OCR */ -#ifdef CONFIG_PM_SLEEP - struct notifier_block pm_notify; -#endif struct wakeup_source *ws; /* Enable consume of uevents */ u32 max_current_330; u32 max_current_300;