From patchwork Thu Mar 28 20:59:02 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mike Turquette X-Patchwork-Id: 2359551 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) by patchwork1.kernel.org (Postfix) with ESMTP id A23753FDDA for ; Thu, 28 Mar 2013 21:03:04 +0000 (UTC) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1ULJw0-0008J6-Ng; Thu, 28 Mar 2013 21:00:32 +0000 Received: from mail-pd0-f180.google.com ([209.85.192.180]) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1ULJvQ-0008Dn-N0 for linux-arm-kernel@lists.infradead.org; Thu, 28 Mar 2013 20:59:58 +0000 Received: by mail-pd0-f180.google.com with SMTP id g10so4272505pdj.39 for ; Thu, 28 Mar 2013 13:59:55 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-received:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references:x-gm-message-state; bh=yPI4tK/Atk0rFMPtTDLLuSrhHIEU7WZ0m9SIuR7aNd4=; b=gU60rH/pSlrmUZnB1xNMmuFcd7D169pdcVDdaCv5FyKUJFV5xXRrWcfVCFSkUUoC4V tS4gn5VF27KA45YD3K2WPSJhSRXF1ELH4JHTNKKw7FhiLVZGs7asXTRXLFnWySrjRZ+T H+aCIubYOmZIGAk6a3A9AsEhkQtx29sSV929jKFHGnrXBmI4qDpjD1XZefiiVQ6sO9f2 5nPHXdVT1QC+WoKqblw+x3pA/4ZLT1lP2NIL3ihuKTc0nOSaQVAfT/jHJF/vtJ4exUdY 3whHJezyJykgznLapcEK7Pkyj40voAhQRbJelWT7XAxlr5M85GrZD0/P4syMRf7aO/73 ErjQ== X-Received: by 10.66.228.74 with SMTP id sg10mr904155pac.48.1364504395105; Thu, 28 Mar 2013 13:59:55 -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 qe3sm163973pbb.0.2013.03.28.13.59.52 (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Thu, 28 Mar 2013 13:59:54 -0700 (PDT) From: Mike Turquette To: linux-kernel@vger.kernel.org Subject: [PATCH 2/2] clk: allow reentrant calls into the clk framework Date: Thu, 28 Mar 2013 13:59:02 -0700 Message-Id: <1364504343-12635-3-git-send-email-mturquette@linaro.org> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1364504343-12635-1-git-send-email-mturquette@linaro.org> References: <1364445958-2999-1-git-send-email-mturquette@linaro.org> <1364504343-12635-1-git-send-email-mturquette@linaro.org> X-Gm-Message-State: ALoCoQlgxFPgYg7FhmAaUONHjcuhsFSRWFuuEds9+REfxK4NdkOYLDRQKAUtmNLxP7qxNAdEv3XJ X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20130328_165956_869246_CD333EC0 X-CRM114-Status: GOOD ( 16.54 ) X-Spam-Score: -1.9 (-) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-1.9 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [209.85.192.180 listed in list.dnswl.org] -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] Cc: ulf.hansson@linaro.org, linaro-kernel@lists.linaro.org, Mike Turquette , patches@linaro.org, laurent.pinchart@ideasonboard.com, rajagopal.venkat@linaro.org, davidb@codeaurora.org, tglx@linutronix.de, linux-arm-kernel@lists.infradead.org X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org 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 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 Tested-by: Laurent Pinchart Reviewed-by: Thomas Gleixner Reviewed-by: Ulf Hansson --- Changes since v5: * fixed up typo in changelog 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 | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 0b5d612..0230c9d 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,23 +37,56 @@ 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 unsigned long clk_enable_lock(void) { unsigned long flags; - spin_lock_irqsave(&enable_lock, flags); + + if (!spin_trylock_irqsave(&enable_lock, flags)) { + if (enable_owner == current) { + enable_refcnt++; + return flags; + } + spin_lock_irqsave(&enable_lock, flags); + } + WARN_ON_ONCE(enable_owner != NULL); + WARN_ON_ONCE(enable_refcnt != 0); + enable_owner = current; + enable_refcnt = 1; return flags; } 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); }