From patchwork Sat Nov 3 08:35:10 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ming Lei X-Patchwork-Id: 1691491 Return-Path: X-Original-To: patchwork-linux-pm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork1.kernel.org (Postfix) with ESMTP id 505953FCDE for ; Sat, 3 Nov 2012 08:38:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753355Ab2KCIgK (ORCPT ); Sat, 3 Nov 2012 04:36:10 -0400 Received: from mail-da0-f46.google.com ([209.85.210.46]:33728 "EHLO mail-da0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751134Ab2KCIgG (ORCPT ); Sat, 3 Nov 2012 04:36:06 -0400 Received: by mail-da0-f46.google.com with SMTP id n41so1965020dak.19 for ; Sat, 03 Nov 2012 01:36:04 -0700 (PDT) Received: by 10.68.234.36 with SMTP id ub4mr13667993pbc.68.1351931763888; Sat, 03 Nov 2012 01:36:03 -0700 (PDT) Received: from localhost ([58.60.1.16]) by mx.google.com with ESMTPS id se4sm7092994pbb.13.2012.11.03.01.35.54 (version=TLSv1/SSLv3 cipher=OTHER); Sat, 03 Nov 2012 01:36:02 -0700 (PDT) From: Ming Lei To: linux-kernel@vger.kernel.org Cc: Alan Stern , Oliver Neukum , Minchan Kim , Greg Kroah-Hartman , "Rafael J. Wysocki" , Jens Axboe , "David S. Miller" , Andrew Morton , netdev@vger.kernel.org, linux-usb@vger.kernel.org, linux-pm@vger.kernel.org, linux-mm@kvack.org, Ming Lei Subject: [PATCH v4 2/6] PM / Runtime: introduce pm_runtime_set_memalloc_noio() Date: Sat, 3 Nov 2012 16:35:10 +0800 Message-Id: <1351931714-11689-3-git-send-email-ming.lei@canonical.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1351931714-11689-1-git-send-email-ming.lei@canonical.com> References: <1351931714-11689-1-git-send-email-ming.lei@canonical.com> Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org The patch introduces the flag of memalloc_noio in 'struct dev_pm_info' to help PM core to teach mm not allocating memory with GFP_KERNEL flag for avoiding probable deadlock. As explained in the comment, any GFP_KERNEL allocation inside runtime_resume() or runtime_suspend() on any one of device in the path from one block or network device to the root device in the device tree may cause deadlock, the introduced pm_runtime_set_memalloc_noio() sets or clears the flag on device in the path recursively. Cc: Alan Stern Cc: "Rafael J. Wysocki" Signed-off-by: Ming Lei --- v4: - rename memalloc_noio_resume as memalloc_noio - remove pm_runtime_get_memalloc_noio() - add comments on pm_runtime_set_memalloc_noio v3: - introduce pm_runtime_get_memalloc_noio() - hold one global lock on pm_runtime_set_memalloc_noio - hold device power lock when accessing memalloc_noio_resume flag suggested by Alan Stern - implement pm_runtime_set_memalloc_noio without recursion suggested by Alan Stern v2: - introduce pm_runtime_set_memalloc_noio() --- drivers/base/power/runtime.c | 57 ++++++++++++++++++++++++++++++++++++++++++ include/linux/pm.h | 1 + include/linux/pm_runtime.h | 3 +++ 3 files changed, 61 insertions(+) diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 3148b10..d477924 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -124,6 +124,63 @@ unsigned long pm_runtime_autosuspend_expiration(struct device *dev) } EXPORT_SYMBOL_GPL(pm_runtime_autosuspend_expiration); +static int dev_memalloc_noio(struct device *dev, void *data) +{ + return dev->power.memalloc_noio; +} + +/* + * pm_runtime_set_memalloc_noio - Set a device's memalloc_noio flag. + * @dev: Device to handle. + * @enable: True for setting the flag and False for clearing the flag. + * + * Set the flag for all devices in the path from the device to the + * root device in the device tree if @enable is true, otherwise clear + * the flag for devices in the path whose siblings don't set the flag. + * + * The function should only be called by block device, or network + * device driver for solving the deadlock problem during runtime + * resume/suspend: + * if memory allocation with GFP_KERNEL is called inside runtime + * resume/suspend callback of any one of its ancestors(or the + * block device itself), the deadlock may be triggered inside the + * memory allocation since it might not complete until the block + * device becomes active and the involed page I/O finishes. The + * situation is pointed out first by Alan Stern. Network device + * are involved in iSCSI kind of situation. + * + * The lock of dev_hotplug_mutex is held in the function for handling + * hotplug race because pm_runtime_set_memalloc_noio() may be called + * in async probe(). + * + * The function should be called between device_add() and device_del() + * on the affected device(block/network device). + */ +void pm_runtime_set_memalloc_noio(struct device *dev, bool enable) +{ + static DEFINE_MUTEX(dev_hotplug_mutex); + + mutex_lock(&dev_hotplug_mutex); + for(;;) { + /* hold power lock since bitfield is not SMP-safe. */ + spin_lock_irq(&dev->power.lock); + dev->power.memalloc_noio = enable; + spin_unlock_irq(&dev->power.lock); + + dev = dev->parent; + + /* only clear the flag for one device if all + * children of the device don't set the flag. + */ + if (!dev || (!enable && + device_for_each_child(dev, NULL, + dev_memalloc_noio))) + break; + } + mutex_unlock(&dev_hotplug_mutex); +} +EXPORT_SYMBOL_GPL(pm_runtime_set_memalloc_noio); + /** * rpm_check_suspend_allowed - Test whether a device may be suspended. * @dev: Device to test. diff --git a/include/linux/pm.h b/include/linux/pm.h index 03d7bb1..1a8a69d 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -538,6 +538,7 @@ struct dev_pm_info { unsigned int irq_safe:1; unsigned int use_autosuspend:1; unsigned int timer_autosuspends:1; + unsigned int memalloc_noio:1; enum rpm_request request; enum rpm_status runtime_status; int runtime_error; diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h index f271860..775e063 100644 --- a/include/linux/pm_runtime.h +++ b/include/linux/pm_runtime.h @@ -47,6 +47,7 @@ extern void pm_runtime_set_autosuspend_delay(struct device *dev, int delay); extern unsigned long pm_runtime_autosuspend_expiration(struct device *dev); extern void pm_runtime_update_max_time_suspended(struct device *dev, s64 delta_ns); +extern void pm_runtime_set_memalloc_noio(struct device *dev, bool enable); static inline bool pm_children_suspended(struct device *dev) { @@ -149,6 +150,8 @@ static inline void pm_runtime_set_autosuspend_delay(struct device *dev, int delay) {} static inline unsigned long pm_runtime_autosuspend_expiration( struct device *dev) { return 0; } +static inline void pm_runtime_set_memalloc_noio(struct device *dev, + bool enable){} #endif /* !CONFIG_PM_RUNTIME */