From patchwork Tue May 14 13:21:31 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Lezcano X-Patchwork-Id: 16896 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-vc0-f199.google.com (mail-vc0-f199.google.com [209.85.220.199]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id 819F825D00 for ; Tue, 14 May 2013 13:30:15 +0000 (UTC) Received: by mail-vc0-f199.google.com with SMTP id hz10sf810629vcb.6 for ; Tue, 14 May 2013 06:29:40 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-received:mime-version:x-beenthere:x-received:received-spf :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 :x-gm-message-state:x-original-sender :x-original-authentication-results:precedence:mailing-list:list-id :x-google-group-id:list-post:list-help:list-archive:list-unsubscribe; bh=8ne4B3exRyXXIPA3HXmdo0g3PNpm0IIfll7/Dfmys8A=; b=SAPp6sJsIrq+3UIf0wt1KdZGCI0RD9HFyaYtHtY9axUDYzoPvMPgEectt9XSU+sHp+ k1izXYNacY1Ub84CpQxetrYKiJ+OwvNyjBgXC8cybN5+tqBEFqI2fXEqa1YOICa6uAW6 fZzaK67DCAskiODfqE+fswGl63Wy8IsTXJs0nMblVQvYcdbgfne9aSv8jeI6w2SVPDeK 0a1nx4bzRXFoyIQ/ZefD2TFfvnxR0i77Y38AbhakR1B8EpPqHL4ur69Oot1ZnrCvYCNj EvpK5OlCHFKs4gr5xTxajGCHgedrEXoootLG4uD3fQtr7blmReD9DWeKCZ9WygyjDwea clfw== X-Received: by 10.224.10.6 with SMTP id n6mr21143296qan.4.1368538180277; Tue, 14 May 2013 06:29:40 -0700 (PDT) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.49.62.166 with SMTP id z6ls187387qer.71.gmail; Tue, 14 May 2013 06:29:40 -0700 (PDT) X-Received: by 10.52.93.233 with SMTP id cx9mr18355799vdb.112.1368538180086; Tue, 14 May 2013 06:29:40 -0700 (PDT) Received: from mail-vc0-f175.google.com (mail-vc0-f175.google.com [209.85.220.175]) by mx.google.com with ESMTPS id ty3si8233113vec.27.2013.05.14.06.29.40 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 14 May 2013 06:29:40 -0700 (PDT) Received-SPF: neutral (google.com: 209.85.220.175 is neither permitted nor denied by best guess record for domain of patch+caf_=patchwork-forward=linaro.org@linaro.org) client-ip=209.85.220.175; Received: by mail-vc0-f175.google.com with SMTP id hv10so524746vcb.20 for ; Tue, 14 May 2013 06:29:40 -0700 (PDT) X-Received: by 10.52.36.115 with SMTP id p19mr1196122vdj.8.1368538179884; Tue, 14 May 2013 06:29:39 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patches@linaro.org Received: by 10.220.217.15 with SMTP id hk15csp62775vcb; Tue, 14 May 2013 06:29:39 -0700 (PDT) X-Received: by 10.180.183.76 with SMTP id ek12mr6452301wic.30.1368537696133; Tue, 14 May 2013 06:21:36 -0700 (PDT) Received: from mail-wi0-x232.google.com (mail-wi0-x232.google.com [2a00:1450:400c:c05::232]) by mx.google.com with ESMTPS id dn2si5272354wib.125.2013.05.14.06.21.35 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 14 May 2013 06:21:36 -0700 (PDT) Received-SPF: neutral (google.com: 2a00:1450:400c:c05::232 is neither permitted nor denied by best guess record for domain of daniel.lezcano@linaro.org) client-ip=2a00:1450:400c:c05::232; Received: by mail-wi0-f178.google.com with SMTP id ey16so471894wid.11 for ; Tue, 14 May 2013 06:21:35 -0700 (PDT) X-Received: by 10.180.212.3 with SMTP id ng3mr6538798wic.22.1368537695344; Tue, 14 May 2013 06:21:35 -0700 (PDT) Received: from mai.home (AToulouse-654-1-286-98.w90-5.abo.wanadoo.fr. [90.5.189.98]) by mx.google.com with ESMTPSA id ay7sm14266036wib.9.2013.05.14.06.21.32 for (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 14 May 2013 06:21:34 -0700 (PDT) From: Daniel Lezcano To: rjw@sisk.pl Cc: linux-pm@vger.kernel.org, lorenzo.pieralisi@arm.com, patches@linaro.org, linaro-kernel@lists.linaro.org Subject: [PATCH] cpuidle: simplify multiple driver support Date: Tue, 14 May 2013 15:21:31 +0200 Message-Id: <1368537691-10111-1-git-send-email-daniel.lezcano@linaro.org> X-Mailer: git-send-email 1.7.9.5 X-Gm-Message-State: ALoCoQlP8FSIHMTSedwU9GCjlAfXHLwCDuIwYqgudETatJIjzyC60GHIer+AqiXSBMZPvOGIElq4 X-Original-Sender: daniel.lezcano@linaro.org X-Original-Authentication-Results: mx.google.com; spf=neutral (google.com: 209.85.220.175 is neither permitted nor denied by best guess record for domain of patch+caf_=patchwork-forward=linaro.org@linaro.org) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Precedence: list Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org List-ID: X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , Commit bf4d1b5ddb78f86078ac6ae0415802d5f0c68f92 brought the multiple driver support. The code added a couple of new API to register the driver per cpu. That led to some code complexity to handle the kernel config options when the multiple driver support is enabled or not, which is not really necessary. The code has to be compatible when the multiple driver support is not enabled, and the multiple driver support has to be compatible with the old api. This patch removes this API, which is not yet used by any driver but needed for the HMP cpuidle drivers which will come soon, and replaces its usage by a cpumask pointer in the cpuidle driver structure telling what cpus are handled by the driver. That let the API cpuidle_[un]register_driver to be used for the multipled driver support and also the cpuidle_[un]register functions, added recently in the cpuidle framework. The current code, a bit poor in comments, has been commented and simplified. Signed-off-by: Daniel Lezcano --- drivers/cpuidle/cpuidle.c | 4 +- drivers/cpuidle/driver.c | 325 ++++++++++++++++++++++++++++----------------- include/linux/cpuidle.h | 21 +-- 3 files changed, 214 insertions(+), 136 deletions(-) diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index c3a93fe..fdc432f 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -466,7 +466,7 @@ void cpuidle_unregister(struct cpuidle_driver *drv) int cpu; struct cpuidle_device *device; - for_each_possible_cpu(cpu) { + for_each_cpu(cpu, drv->cpumask) { device = &per_cpu(cpuidle_dev, cpu); cpuidle_unregister_device(device); } @@ -498,7 +498,7 @@ int cpuidle_register(struct cpuidle_driver *drv, return ret; } - for_each_possible_cpu(cpu) { + for_each_cpu(cpu, drv->cpumask) { device = &per_cpu(cpuidle_dev, cpu); device->cpu = cpu; diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c index 8dfaaae..3a447a4 100644 --- a/drivers/cpuidle/driver.c +++ b/drivers/cpuidle/driver.c @@ -18,206 +18,267 @@ DEFINE_SPINLOCK(cpuidle_driver_lock); -static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu); -static struct cpuidle_driver * __cpuidle_get_cpu_driver(int cpu); +#ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS -static void cpuidle_setup_broadcast_timer(void *arg) +static DEFINE_PER_CPU(struct cpuidle_driver *, cpuidle_drivers); + +/** + * __cpuidle_get_cpu_driver: returns the cpuidle driver tied with the specified + * cpu. + * + * @cpu: an integer specifying the cpu number + * + * Returns a pointer to struct cpuidle_driver, NULL if no driver has been + * registered for this driver + */ +static struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) { - int cpu = smp_processor_id(); - clockevents_notify((long)(arg), &cpu); + return per_cpu(cpuidle_drivers, cpu); } -static void __cpuidle_driver_init(struct cpuidle_driver *drv, int cpu) +/** + * __cpuidle_set_driver: assign to the per cpu variable the driver pointer for + * each cpu the driver is assigned to with the cpumask. + * + * @drv: a pointer to a struct cpuidle_driver + * + * Returns 0 on success, < 0 otherwise + */ +static inline int __cpuidle_set_driver(struct cpuidle_driver *drv) { - int i; + int cpu; - drv->refcnt = 0; + for_each_cpu(cpu, drv->cpumask) { - for (i = drv->state_count - 1; i >= 0 ; i--) { + if (__cpuidle_get_cpu_driver(cpu)) + return -EBUSY; - if (!(drv->states[i].flags & CPUIDLE_FLAG_TIMER_STOP)) - continue; - - drv->bctimer = 1; - on_each_cpu_mask(get_cpu_mask(cpu), cpuidle_setup_broadcast_timer, - (void *)CLOCK_EVT_NOTIFY_BROADCAST_ON, 1); - break; + per_cpu(cpuidle_drivers, cpu) = drv; } + + return 0; } -static int __cpuidle_register_driver(struct cpuidle_driver *drv, int cpu) +/** + * __cpuidle_unset_driver: for each cpu the driver is handling, set the per cpu + * variable driver to NULL. + * + * @drv: a pointer to a struct cpuidle_driver + */ +static inline void __cpuidle_unset_driver(struct cpuidle_driver *drv) { - if (!drv || !drv->state_count) - return -EINVAL; - - if (cpuidle_disabled()) - return -ENODEV; - - if (__cpuidle_get_cpu_driver(cpu)) - return -EBUSY; + int cpu; - __cpuidle_driver_init(drv, cpu); + for_each_cpu(cpu, drv->cpumask) { - __cpuidle_set_cpu_driver(drv, cpu); + if (drv != __cpuidle_get_cpu_driver(cpu)) + continue; - return 0; + per_cpu(cpuidle_drivers, cpu) = NULL; + } } -static void __cpuidle_unregister_driver(struct cpuidle_driver *drv, int cpu) -{ - if (drv != __cpuidle_get_cpu_driver(cpu)) - return; +#else - if (!WARN_ON(drv->refcnt > 0)) - __cpuidle_set_cpu_driver(NULL, cpu); +static struct cpuidle_driver *cpuidle_curr_driver; - if (drv->bctimer) { - drv->bctimer = 0; - on_each_cpu_mask(get_cpu_mask(cpu), cpuidle_setup_broadcast_timer, - (void *)CLOCK_EVT_NOTIFY_BROADCAST_OFF, 1); - } +/** + * __cpuidle_get_cpu_driver: returns the global cpuidle driver pointer. + * + * @cpu: an integer specifying the cpu number, this parameter is ignored + * + * Returns a pointer to a struct cpuidle_driver, NULL if no driver was + * previously registered + */ +static inline struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) +{ + return cpuidle_curr_driver; } -#ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS +/** + * __cpuidle_set_driver: assign the cpuidle driver pointer to the global cpuidle + * driver variable. + * + * @drv: a pointer to a struct cpuidle_driver + * + * Returns 0 on success, < 0 otherwise + */ +static inline int __cpuidle_set_driver(struct cpuidle_driver *drv) +{ + if (cpuidle_curr_driver) + return -EBUSY; -static DEFINE_PER_CPU(struct cpuidle_driver *, cpuidle_drivers); + cpuidle_curr_driver = drv; -static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu) -{ - per_cpu(cpuidle_drivers, cpu) = drv; + return 0; } -static struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) +/** + * __cpuidle_unset_driver: reset the global cpuidle driver variable if the + * cpuidle driver pointer match it. + * + * @drv: a pointer to a struct cpuidle_driver + */ +static inline void __cpuidle_unset_driver(struct cpuidle_driver *drv) { - return per_cpu(cpuidle_drivers, cpu); + if (drv == cpuidle_curr_driver) + cpuidle_curr_driver = NULL; } -static void __cpuidle_unregister_all_cpu_driver(struct cpuidle_driver *drv) +#endif + +/** + * cpuidle_setup_broadcast_timer: set the broadcast timer notification for the + * current cpu. This function is called per cpu context invoked by a smp cross + * call. It is not supposed to be called directly. + * + * @arg: a void pointer, actually used to match the smp cross call api but used + * as a long with two values: + * - CLOCK_EVT_NOTIFY_BROADCAST_ON + * - CLOCK_EVT_NOTIFY_BROADCAST_OFF + */ +static void cpuidle_setup_broadcast_timer(void *arg) { - int cpu; - for_each_present_cpu(cpu) - __cpuidle_unregister_driver(drv, cpu); + int cpu = smp_processor_id(); + clockevents_notify((long)(arg), &cpu); } -static int __cpuidle_register_all_cpu_driver(struct cpuidle_driver *drv) +/** + * __cpuidle_driver_init: initialize the driver internal data. + * + * @drv: a valid pointer to a struct cpuidle_driver + * + * Returns 0 on success, < 0 otherwise + */ +static int __cpuidle_driver_init(struct cpuidle_driver *drv) { - int ret = 0; - int i, cpu; + int i; - for_each_present_cpu(cpu) { - ret = __cpuidle_register_driver(drv, cpu); - if (ret) - break; - } + drv->refcnt = 0; - if (ret) - for_each_present_cpu(i) { - if (i == cpu) - break; - __cpuidle_unregister_driver(drv, i); - } + /* + * we default here to all cpu possible because if the kernel + * boots with some cpus offline and then we online one of them + * the cpu notifier won't know which driver to assign + */ + if (!drv->cpumask) + drv->cpumask = (struct cpumask *)cpu_possible_mask; + + /* + * we look for the timer stop flag in the different states, + * so know we have to setup the broadcast timer. The loop is + * in reverse order, because usually the deeper state has this + * flag set + */ + for (i = drv->state_count - 1; i >= 0 ; i--) { + if (!(drv->states[i].flags & CPUIDLE_FLAG_TIMER_STOP)) + continue; - return ret; + drv->bctimer = 1; + break; + } + + return 0; } -int cpuidle_register_cpu_driver(struct cpuidle_driver *drv, int cpu) +/** + * __cpuidle_register_driver: do some sanity checks, initializes the driver, + * assign the driver to the global cpuidle driver variable(s) and setup the + * broadcast timer if the cpuidle driver has some states which shutdown the + * local timer. + * + * @drv: a valid pointer to a struct cpuidle_driver + * + * Returns 0 on success, < 0 otherwise + */ +static int __cpuidle_register_driver(struct cpuidle_driver *drv) { int ret; - spin_lock(&cpuidle_driver_lock); - ret = __cpuidle_register_driver(drv, cpu); - spin_unlock(&cpuidle_driver_lock); + if (!drv || !drv->state_count) + return -EINVAL; - return ret; -} + if (cpuidle_disabled()) + return -ENODEV; -void cpuidle_unregister_cpu_driver(struct cpuidle_driver *drv, int cpu) -{ - spin_lock(&cpuidle_driver_lock); - __cpuidle_unregister_driver(drv, cpu); - spin_unlock(&cpuidle_driver_lock); -} + ret = __cpuidle_driver_init(drv); + if (ret) + return ret; -/** - * cpuidle_register_driver - registers a driver - * @drv: the driver - */ -int cpuidle_register_driver(struct cpuidle_driver *drv) -{ - int ret; + ret = __cpuidle_set_driver(drv); + if (ret) + return ret; - spin_lock(&cpuidle_driver_lock); - ret = __cpuidle_register_all_cpu_driver(drv); - spin_unlock(&cpuidle_driver_lock); + if (drv->bctimer) + on_each_cpu_mask(drv->cpumask, cpuidle_setup_broadcast_timer, + (void *)CLOCK_EVT_NOTIFY_BROADCAST_ON, 1); - return ret; + return 0; } -EXPORT_SYMBOL_GPL(cpuidle_register_driver); /** - * cpuidle_unregister_driver - unregisters a driver - * @drv: the driver + * __cpuidle_unregister_driver: checks the driver is no longer in use, reset the + * global cpuidle driver variable(s) and disable the timer broadcast + * notification mechanism if it was in use. + * + * @drv: a valid pointer to a struct cpuidle_driver + * + * Returns 0 on success, < 0 otherwise */ -void cpuidle_unregister_driver(struct cpuidle_driver *drv) +static void __cpuidle_unregister_driver(struct cpuidle_driver *drv) { - spin_lock(&cpuidle_driver_lock); - __cpuidle_unregister_all_cpu_driver(drv); - spin_unlock(&cpuidle_driver_lock); -} -EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); - -#else - -static struct cpuidle_driver *cpuidle_curr_driver; + if (!WARN_ON(drv->refcnt > 0)) + return; -static inline void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu) -{ - cpuidle_curr_driver = drv; -} + __cpuidle_unset_driver(drv); -static inline struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) -{ - return cpuidle_curr_driver; + if (drv->bctimer) { + drv->bctimer = 0; + on_each_cpu_mask(drv->cpumask, cpuidle_setup_broadcast_timer, + (void *)CLOCK_EVT_NOTIFY_BROADCAST_OFF, 1); + } } /** - * cpuidle_register_driver - registers a driver - * @drv: the driver + * cpuidle_register_driver: registers a driver by taking a lock to prevent + * multiple callers to [un]register a driver at the same time. + * + * @drv: a pointer to a valid struct cpuidle_driver + * + * Returns 0 on success, < 0 otherwise */ int cpuidle_register_driver(struct cpuidle_driver *drv) { - int ret, cpu; + int ret; - cpu = get_cpu(); spin_lock(&cpuidle_driver_lock); - ret = __cpuidle_register_driver(drv, cpu); + ret = __cpuidle_register_driver(drv); spin_unlock(&cpuidle_driver_lock); - put_cpu(); return ret; } EXPORT_SYMBOL_GPL(cpuidle_register_driver); /** - * cpuidle_unregister_driver - unregisters a driver - * @drv: the driver + * cpuidle_unregister_driver: unregisters a driver by taking a lock to prevent + * multiple callers to [un]register a driver at the same time. The specified + * driver must match the driver currently registered. + * + * @drv: a pointer to a valid struct cpuidle_driver */ void cpuidle_unregister_driver(struct cpuidle_driver *drv) { - int cpu; - - cpu = get_cpu(); spin_lock(&cpuidle_driver_lock); - __cpuidle_unregister_driver(drv, cpu); + __cpuidle_unregister_driver(drv); spin_unlock(&cpuidle_driver_lock); - put_cpu(); } EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); -#endif /** - * cpuidle_get_driver - return the current driver + * cpuidle_get_driver: returns the driver tied with the current cpu. + * + * Returns a struct cpuidle_driver pointer, or NULL if no driver is registered */ struct cpuidle_driver *cpuidle_get_driver(void) { @@ -233,7 +294,12 @@ struct cpuidle_driver *cpuidle_get_driver(void) EXPORT_SYMBOL_GPL(cpuidle_get_driver); /** - * cpuidle_get_cpu_driver - return the driver tied with a cpu + * cpuidle_get_cpu_driver: returns the driver registered with a cpu. + * + * @dev: a valid pointer to a struct cpuidle_device + * + * Returns a struct cpuidle_driver pointer, or NULL if no driver is registered + * for the specified cpu */ struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev) { @@ -244,6 +310,13 @@ struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev) } EXPORT_SYMBOL_GPL(cpuidle_get_cpu_driver); +/** + * cpuidle_driver_ref: gets a refcount for the driver. Note this function takes + * a refcount for the driver assigned to the current cpu. + * + * Returns a struct cpuidle_driver pointer, or NULL if no driver is registered + * for the current cpu + */ struct cpuidle_driver *cpuidle_driver_ref(void) { struct cpuidle_driver *drv; @@ -257,6 +330,10 @@ struct cpuidle_driver *cpuidle_driver_ref(void) return drv; } +/** + * cpuidle_driver_unref: puts down the refcount for the driver. Note this + * function decrement the refcount for the driver assigned to the current cpu. + */ void cpuidle_driver_unref(void) { struct cpuidle_driver *drv = cpuidle_get_driver(); diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 8f04062..63d78b1 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -101,16 +101,20 @@ static inline int cpuidle_get_last_residency(struct cpuidle_device *dev) ****************************/ struct cpuidle_driver { - const char *name; - struct module *owner; - int refcnt; + const char *name; + struct module *owner; + int refcnt; /* used by the cpuidle framework to setup the broadcast timer */ - unsigned int bctimer:1; + unsigned int bctimer:1; + /* states array must be ordered in decreasing power consumption */ - struct cpuidle_state states[CPUIDLE_STATE_MAX]; - int state_count; - int safe_state_index; + struct cpuidle_state states[CPUIDLE_STATE_MAX]; + int state_count; + int safe_state_index; + + /* the driver handles the cpus in cpumask */ + struct cpumask *cpumask; }; #ifdef CONFIG_CPU_IDLE @@ -135,9 +139,6 @@ extern void cpuidle_disable_device(struct cpuidle_device *dev); extern int cpuidle_play_dead(void); extern struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev); -extern int cpuidle_register_cpu_driver(struct cpuidle_driver *drv, int cpu); -extern void cpuidle_unregister_cpu_driver(struct cpuidle_driver *drv, int cpu); - #else static inline void disable_cpuidle(void) { } static inline int cpuidle_idle_call(void) { return -ENODEV; }