From patchwork Tue Nov 25 10:34:19 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 41455 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-la0-f71.google.com (mail-la0-f71.google.com [209.85.215.71]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id B3F6C25E18 for ; Tue, 25 Nov 2014 10:35:45 +0000 (UTC) Received: by mail-la0-f71.google.com with SMTP id s18sf216757lam.10 for ; Tue, 25 Nov 2014 02:35:44 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:delivered-to:from:to:cc:subject :date:message-id:in-reply-to:references:in-reply-to:references :sender:precedence:list-id:x-original-sender :x-original-authentication-results:mailing-list:list-post:list-help :list-archive:list-unsubscribe; bh=mW4D17mhBz8F3vO+NftZqR7INVdLwJLGKnsNVVanAac=; b=YWpcCspZTfSYf1APiLTSi5tslahPueUdIaqUBuHcVdruURJLaV2NU0Vdoo47XYShEH kqCtguU6mzP6syqDcrSsrSo5sVVONFwu3j32nFNrDZGs5AlCaUUAFfKIIDtVDcog6Ux8 Iy7FOXdh9SjeFu91/ZPmJBOXufaq4TJCjHzEGIoKoXshPhyUzd6VvK34Bi7OJFkJE2lJ YEFijMME3Fu0RgtdCZZ2dNgqmE3u+pzHIC/K3DJNqST2w+fqyLSlgb70P7gg1quiXg8D G0PXeH6hw77g7HJbJwvMBVunegqPbTyXBGykBbr6qEqSkhpjrMi6f6wjfff9nlbVG4PV s7hw== X-Gm-Message-State: ALoCoQned4dlWu5XTalA2DsYNkjh+aNuzQIn2u++HWmKF6HHfK6B2FRjkG07DCXafpVqM/pOG09E X-Received: by 10.112.204.71 with SMTP id kw7mr305259lbc.13.1416911744695; Tue, 25 Nov 2014 02:35:44 -0800 (PST) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.152.7.177 with SMTP id k17ls485599laa.56.gmail; Tue, 25 Nov 2014 02:35:44 -0800 (PST) X-Received: by 10.112.169.6 with SMTP id aa6mr25793931lbc.29.1416911744294; Tue, 25 Nov 2014 02:35:44 -0800 (PST) Received: from mail-lb0-f179.google.com (mail-lb0-f179.google.com. [209.85.217.179]) by mx.google.com with ESMTPS id du3si915733lbc.32.2014.11.25.02.35.43 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 25 Nov 2014 02:35:43 -0800 (PST) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.217.179 as permitted sender) client-ip=209.85.217.179; Received: by mail-lb0-f179.google.com with SMTP id z11so245986lbi.24 for ; Tue, 25 Nov 2014 02:35:43 -0800 (PST) X-Received: by 10.112.62.166 with SMTP id z6mr24755231lbr.74.1416911743800; Tue, 25 Nov 2014 02:35:43 -0800 (PST) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patch@linaro.org Received: by 10.112.184.201 with SMTP id ew9csp410732lbc; Tue, 25 Nov 2014 02:35:42 -0800 (PST) X-Received: by 10.70.41.137 with SMTP id f9mr41322063pdl.83.1416911741779; Tue, 25 Nov 2014 02:35:41 -0800 (PST) Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id mk8si1367241pdb.23.2014.11.25.02.35.41 for ; Tue, 25 Nov 2014 02:35:41 -0800 (PST) Received-SPF: none (google.com: linux-pm-owner@vger.kernel.org does not designate permitted sender hosts) client-ip=209.132.180.67; Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752429AbaKYKfk (ORCPT + 12 others); Tue, 25 Nov 2014 05:35:40 -0500 Received: from mail-pa0-f47.google.com ([209.85.220.47]:38412 "EHLO mail-pa0-f47.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752317AbaKYKfj (ORCPT ); Tue, 25 Nov 2014 05:35:39 -0500 Received: by mail-pa0-f47.google.com with SMTP id kq14so338069pab.6 for ; Tue, 25 Nov 2014 02:35:39 -0800 (PST) X-Received: by 10.68.165.132 with SMTP id yy4mr35273489pbb.55.1416911739330; Tue, 25 Nov 2014 02:35:39 -0800 (PST) Received: from localhost ([122.167.111.40]) by mx.google.com with ESMTPSA id yl6sm1061718pbc.91.2014.11.25.02.35.37 for (version=TLSv1.2 cipher=RC4-SHA bits=128/128); Tue, 25 Nov 2014 02:35:38 -0800 (PST) From: Viresh Kumar To: Rafael Wysocki Cc: linaro-kernel@lists.linaro.org, linux-pm@vger.kernel.org, stefan.wahren@i2se.com, nm@ti.com, linux-arm-kernel@lists.infradead.org, sudeep.holla@arm.com, Viresh Kumar , Paul McKenney Subject: [PATCH 4/8] opp: Introduce APIs to remove OPPs Date: Tue, 25 Nov 2014 16:04:19 +0530 Message-Id: X-Mailer: git-send-email 2.0.3.693.g996b0fd In-Reply-To: References: In-Reply-To: References: Sender: linux-pm-owner@vger.kernel.org Precedence: list List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: viresh.kumar@linaro.org X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.217.179 as permitted sender) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , OPPs are created statically (from DT) or dynamically. Currently we don't free OPPs that are created statically, when the module unloads. And so if the module is inserted back again, we get warning for duplicate OPPs as the same were already present. Also, there might be a need to remove dynamic OPPs in future and so API for that is also added. This patch adds helper APIs to remove/free existing static and dynamic OPPs. Cc: Paul McKenney Signed-off-by: Viresh Kumar --- @Paul/Rafael: Do we need to use call_srcu() instead of kfree_rcu() in opp_set_availability()? I left it as it looked a bit different. srcu_notifier_call_chain() is getting called after deleting the node. drivers/base/power/opp.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pm_opp.h | 12 +++++- 2 files changed, 113 insertions(+), 1 deletion(-) diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index b249b01..7ae4db5 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -79,6 +79,7 @@ struct dev_pm_opp { * however addition is possible and is secured by dev_opp_list_lock * @dev: device pointer * @srcu_head: notifier head to notify the OPP availability changes. + * @rcu_head: RCU callback head used for deferred freeing * @opp_list: list of opps * * This is an internal data structure maintaining the link to opps attached to @@ -90,6 +91,7 @@ struct device_opp { struct device *dev; struct srcu_notifier_head srcu_head; + struct rcu_head rcu_head; struct list_head opp_list; }; @@ -498,6 +500,76 @@ int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt) } EXPORT_SYMBOL_GPL(dev_pm_opp_add); +static void kfree_opp_rcu(struct rcu_head *head) +{ + struct dev_pm_opp *opp = container_of(head, struct dev_pm_opp, rcu_head); + + kfree(opp); +} + +static void kfree_device_rcu(struct rcu_head *head) +{ + struct device_opp *device_opp = container_of(head, struct device_opp, rcu_head); + + kfree(device_opp); +} + +void __dev_pm_opp_remove(struct device_opp *dev_opp, struct dev_pm_opp *opp) +{ + /* + * Notify the changes in the availability of the operable + * frequency/voltage list. + */ + srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_REMOVE, opp); + list_del_rcu(&opp->node); + call_srcu(&dev_opp->srcu_head.srcu, &opp->rcu_head, kfree_opp_rcu); + + if (list_empty(&dev_opp->opp_list)) { + list_del_rcu(&dev_opp->node); + call_srcu(&dev_opp->srcu_head.srcu, &dev_opp->rcu_head, + kfree_device_rcu); + } +} + +/** + * dev_pm_opp_remove() - Remove an OPP from OPP list + * @dev: device for which we do this operation + * @freq: OPP to remove with matching 'freq' + * + * This function removes an opp from the opp list. + */ +void dev_pm_opp_remove(struct device *dev, unsigned long freq) +{ + struct dev_pm_opp *opp; + struct device_opp *dev_opp; + bool found = false; + + /* Hold our list modification lock here */ + mutex_lock(&dev_opp_list_lock); + + dev_opp = find_device_opp(dev); + if (IS_ERR(dev_opp)) + goto unlock; + + list_for_each_entry(opp, &dev_opp->opp_list, node) { + if (opp->rate == freq) { + found = true; + break; + } + } + + if (!found) { + dev_warn(dev, "%s: Couldn't find OPP with freq: %lu\n", + __func__, freq); + goto unlock; + } + + __dev_pm_opp_remove(dev_opp, opp); +unlock: + mutex_unlock(&dev_opp_list_lock); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_remove); + /** * opp_set_availability() - helper to set the availability of an opp * @dev: device for which we do this operation @@ -687,4 +759,34 @@ int of_init_opp_table(struct device *dev) return 0; } EXPORT_SYMBOL_GPL(of_init_opp_table); + +/** + * of_free_opp_table() - Free OPP table entries created from static DT entries + * @dev: device pointer used to lookup device OPPs. + * + * Free OPPs created using static entries present in DT. + */ +void of_free_opp_table(struct device *dev) +{ + struct device_opp *dev_opp = find_device_opp(dev); + struct dev_pm_opp *opp, *tmp; + + /* Check for existing list for 'dev' */ + dev_opp = find_device_opp(dev); + if (WARN(IS_ERR(dev_opp), "%s: dev_opp: %ld\n", dev_name(dev), + PTR_ERR(dev_opp))) + return; + + /* Hold our list modification lock here */ + mutex_lock(&dev_opp_list_lock); + + /* Free static OPPs */ + list_for_each_entry_safe(opp, tmp, &dev_opp->opp_list, node) { + if (!opp->dynamic) + __dev_pm_opp_remove(dev_opp, opp); + } + + mutex_unlock(&dev_opp_list_lock); +} +EXPORT_SYMBOL_GPL(of_free_opp_table); #endif diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 0330217..cec2d45 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -21,7 +21,7 @@ struct dev_pm_opp; struct device; enum dev_pm_opp_event { - OPP_EVENT_ADD, OPP_EVENT_ENABLE, OPP_EVENT_DISABLE, + OPP_EVENT_ADD, OPP_EVENT_REMOVE, OPP_EVENT_ENABLE, OPP_EVENT_DISABLE, }; #if defined(CONFIG_PM_OPP) @@ -44,6 +44,7 @@ struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev, int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt); +void dev_pm_opp_remove(struct device *dev, unsigned long freq); int dev_pm_opp_enable(struct device *dev, unsigned long freq); @@ -90,6 +91,10 @@ static inline int dev_pm_opp_add(struct device *dev, unsigned long freq, return -EINVAL; } +static inline void dev_pm_opp_remove(struct device *dev, unsigned long freq) +{ +} + static inline int dev_pm_opp_enable(struct device *dev, unsigned long freq) { return 0; @@ -109,11 +114,16 @@ static inline struct srcu_notifier_head *dev_pm_opp_get_notifier( #if defined(CONFIG_PM_OPP) && defined(CONFIG_OF) int of_init_opp_table(struct device *dev); +void of_free_opp_table(struct device *dev); #else static inline int of_init_opp_table(struct device *dev) { return -EINVAL; } + +static inline void of_free_opp_table(struct device *dev) +{ +} #endif #endif /* __LINUX_OPP_H__ */