From patchwork Thu Mar 28 04:45:58 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mike Turquette X-Patchwork-Id: 15715 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id 6554C23E00 for ; Thu, 28 Mar 2013 04:47:08 +0000 (UTC) Received: from mail-ve0-f172.google.com (mail-ve0-f172.google.com [209.85.128.172]) by fiordland.canonical.com (Postfix) with ESMTP id 09F60A185A5 for ; Thu, 28 Mar 2013 04:47:07 +0000 (UTC) Received: by mail-ve0-f172.google.com with SMTP id oz10so2663046veb.3 for ; Wed, 27 Mar 2013 21:47:07 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-received:x-forwarded-to:x-forwarded-for:delivered-to:x-received :received-spf:x-received:from:to:cc:subject:date:message-id:x-mailer :in-reply-to:references:x-gm-message-state; bh=v+jUqsGb6I6Qu34jCo/7OlaiTQl9tBxeUktKUNYCPUg=; b=GvTL/Uussl2OLt20Wp3MJs4aQv0LPp4ZkDgXUksgY2oLlf0kM8LVTF/FMrBtZqplyK yjmYmD5lPSBgU+7K/V/3KSrBpbSaUXhkM7rGmqEYLmI+pMjDmQ3yH84zRL0r+xTGXbvk zgVcgczJJQacb+Xsld/g79Q3GNaWlTefjb3ZmJbOl/lHfSVfvD8p4oQShCr4Fo5u28wm HTbXIeIkKqy0zuaYZ0pNme/aPWISPqUS0W/gX28TxxMBi092JxOgwQ7c51RtXmGAm0dE YrqFk3GIlj5lpkX89WVV6sz8Vlwxbf32Io3sj3wElNQAjWhnUB2gKs6eGbKKmaYZ1k25 fXsg== X-Received: by 10.52.20.239 with SMTP id q15mr22297715vde.73.1364446027538; Wed, 27 Mar 2013 21:47:07 -0700 (PDT) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.59.4.204 with SMTP id cg12csp145712ved; Wed, 27 Mar 2013 21:47:06 -0700 (PDT) X-Received: by 10.66.83.65 with SMTP id o1mr33235647pay.195.1364446026564; Wed, 27 Mar 2013 21:47:06 -0700 (PDT) Received: from mail-da0-x22c.google.com (mail-da0-x22c.google.com [2607:f8b0:400e:c00::22c]) by mx.google.com with ESMTPS id si3si24719208pbc.191.2013.03.27.21.47.06 (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Wed, 27 Mar 2013 21:47:06 -0700 (PDT) Received-SPF: neutral (google.com: 2607:f8b0:400e:c00::22c is neither permitted nor denied by best guess record for domain of mturquette@linaro.org) client-ip=2607:f8b0:400e:c00::22c; Authentication-Results: mx.google.com; spf=neutral (google.com: 2607:f8b0:400e:c00::22c is neither permitted nor denied by best guess record for domain of mturquette@linaro.org) smtp.mail=mturquette@linaro.org Received: by mail-da0-f44.google.com with SMTP id z20so4380945dae.31 for ; Wed, 27 Mar 2013 21:47:06 -0700 (PDT) X-Received: by 10.66.163.132 with SMTP id yi4mr33748198pab.104.1364446026145; Wed, 27 Mar 2013 21:47:06 -0700 (PDT) Received: from quantum.gateway.2wire.net (adsl-69-228-93-79.dsl.pltn13.pacbell.net. [69.228.93.79]) by mx.google.com with ESMTPS id hw16sm26047314pab.19.2013.03.27.21.47.03 (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Wed, 27 Mar 2013 21:47:05 -0700 (PDT) From: Mike Turquette To: linux-kernel@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org, patches@linaro.org, linaro-kernel@lists.linaro.org, rajagopal.venkat@linaro.org, davidb@codeaurora.org, ulf.hansson@linaro.org, laurent.pinchart@ideasonboard.com, tglx@linutronix.de, Mike Turquette Subject: [PATCH 2/2] clk: allow reentrant calls into the clk framework Date: Wed, 27 Mar 2013 21:45:58 -0700 Message-Id: <1364445958-2999-3-git-send-email-mturquette@linaro.org> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1364445958-2999-1-git-send-email-mturquette@linaro.org> References: <1364368183-24420-1-git-send-email-mturquette@linaro.org> <1364445958-2999-1-git-send-email-mturquette@linaro.org> X-Gm-Message-State: ALoCoQkZ8lifGkJ+h6VXj9AESAtw7TUXwpHQlLUcIXanLeYju7Lvksv/7STmtdMER93hXZwq/6gO Reentrancy into the clock framework is necessary for clock operations that result in nested calls to the clk api. A common example is a clock that is prepared via an i2c transaction, such as a clock inside of a discrete audio chip or a power management IC. The i2c subsystem itself will use the clk api resulting in a deadlock: clk_prepare(audio_clk) i2c_transfer(..) clk_prepare(i2c_controller_clk) The ability to reenter the clock framework prevents this deadlock. Other use cases exist such as allowing .set_rate callbacks to call clk_set_parent to achieve the best rate, or to save power in certain configurations. Yet another example is performing pinctrl operations from a clk_ops callback. Calls into the pinctrl subsystem may call clk_{un}prepare on an unrelated clock. Allowing for nested calls to reenter the clock framework enables both of these use cases. Reentrancy is implemented by two global pointers that track the owner currently holding a global lock. One pointer tracks the owner during sleepable, mutex-protected operations and the other one tracks the owner during non-interruptible, spinlock-protected operations. When the clk framework is entered we try to hold the global lock. If it is held we compare the current task id against the current owner; a match implies a nested call and we reenter. If the values do not match then we block on the lock until it is released. Signed-off-by: Mike Turquette Cc: Rajagopal Venkat Cc: David Brown Cc: Ulf Hansson Cc: Laurent Pinchart Cc: Thomas Gleixner Reviewed-by: Thomas Gleixner --- Changes since v4: * remove uneccesary atomic operations * remove casting bugs * place reentrancy logic into locking helper functions * improve debugging with reference counting and WARNs drivers/clk/clk.c | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index bea47d5..fe7c054 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -19,10 +19,17 @@ #include #include #include +#include static DEFINE_SPINLOCK(enable_lock); static DEFINE_MUTEX(prepare_lock); +static struct task_struct *prepare_owner; +static struct task_struct *enable_owner; + +static int prepare_refcnt; +static int enable_refcnt; + static HLIST_HEAD(clk_root_list); static HLIST_HEAD(clk_orphan_list); static LIST_HEAD(clk_notifier_list); @@ -30,21 +37,53 @@ static LIST_HEAD(clk_notifier_list); /*** locking ***/ static void clk_prepare_lock(void) { - mutex_lock(&prepare_lock); + if (!mutex_trylock(&prepare_lock)) { + if (prepare_owner == current) { + prepare_refcnt++; + return; + } + mutex_lock(&prepare_lock); + } + WARN_ON_ONCE(prepare_owner != NULL); + WARN_ON_ONCE(prepare_refcnt != 0); + prepare_owner = current; + prepare_refcnt = 1; } static void clk_prepare_unlock(void) { + WARN_ON_ONCE(prepare_owner != current); + WARN_ON_ONCE(prepare_refcnt == 0); + + if (--prepare_refcnt) + return; + prepare_owner = NULL; mutex_unlock(&prepare_lock); } static void clk_enable_lock(unsigned long *flags) { - spin_lock_irqsave(&enable_lock, *flags); + if (!spin_trylock_irqsave(&enable_lock, *flags)) { + if (enable_owner == current) { + enable_refcnt++; + return; + } + spin_lock_irqsave(&enable_lock, *flags); + } + WARN_ON_ONCE(enable_owner != NULL); + WARN_ON_ONCE(enable_refcnt != 0); + enable_owner = current; + enable_refcnt = 1; } static void clk_enable_unlock(unsigned long *flags) { + WARN_ON_ONCE(enable_owner != current); + WARN_ON_ONCE(enable_refcnt == 0); + + if (--enable_refcnt) + return; + enable_owner = NULL; spin_unlock_irqrestore(&enable_lock, *flags); }