* [RFC PATCH 1/2] thermal: Add a new trip type to use cooling device instance number
From: Amit Daniel Kachhap @ 2011-12-13 15:13 UTC (permalink / raw)
To: linux-pm; +Cc: linaro-dev, patches, linux-kernel, linux-acpi
In-Reply-To: <1323789196-4942-1-git-send-email-amit.kachhap@linaro.org>
This patch adds a new trip type THERMAL_TRIP_STATE_ACTIVE. This
trip behaves same as THERMAL_TRIP_ACTIVE but also passes the cooling
device instance number. This helps the cooling device registered as
different instances to perform appropriate cooling action decision in
the set_cur_state call back function.
Also since the trip temperature's are in ascending order so some logic
is put in place to skip the un-necessary checks.
Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
---
Documentation/thermal/sysfs-api.txt | 4 ++--
drivers/thermal/thermal_sys.c | 27 ++++++++++++++++++++++++++-
include/linux/thermal.h | 1 +
3 files changed, 29 insertions(+), 3 deletions(-)
diff --git a/Documentation/thermal/sysfs-api.txt b/Documentation/thermal/sysfs-api.txt
index b61e46f..5c1d44e 100644
--- a/Documentation/thermal/sysfs-api.txt
+++ b/Documentation/thermal/sysfs-api.txt
@@ -184,8 +184,8 @@ trip_point_[0-*]_temp
trip_point_[0-*]_type
Strings which indicate the type of the trip point.
- E.g. it can be one of critical, hot, passive, active[0-*] for ACPI
- thermal zone.
+ E.g. it can be one of critical, hot, passive, active[0-1],
+ state-active[0-*] for ACPI thermal zone.
RO, Optional
cdev[0-*]
diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c
index dd9a574..72b1ab3 100644
--- a/drivers/thermal/thermal_sys.c
+++ b/drivers/thermal/thermal_sys.c
@@ -192,6 +192,8 @@ trip_point_type_show(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "passive\n");
case THERMAL_TRIP_ACTIVE:
return sprintf(buf, "active\n");
+ case THERMAL_TRIP_STATE_ACTIVE:
+ return sprintf(buf, "state-active\n");
default:
return sprintf(buf, "unknown\n");
}
@@ -1035,7 +1037,7 @@ EXPORT_SYMBOL(thermal_cooling_device_unregister);
void thermal_zone_device_update(struct thermal_zone_device *tz)
{
int count, ret = 0;
- long temp, trip_temp;
+ long temp, trip_temp, max_state, last_trip_change = 0;
enum thermal_trip_type trip_type;
struct thermal_cooling_device_instance *instance;
struct thermal_cooling_device *cdev;
@@ -1086,6 +1088,29 @@ void thermal_zone_device_update(struct thermal_zone_device *tz)
cdev->ops->set_cur_state(cdev, 0);
}
break;
+ case THERMAL_TRIP_STATE_ACTIVE:
+ list_for_each_entry(instance, &tz->cooling_devices,
+ node) {
+ if (instance->trip != count)
+ continue;
+
+ if (temp <= last_trip_change)
+ continue;
+
+ cdev = instance->cdev;
+ cdev->ops->get_max_state(cdev, &max_state);
+
+ if ((temp >= trip_temp) &&
+ ((count + 1) <= max_state))
+ cdev->ops->set_cur_state(cdev,
+ count + 1);
+ else if ((temp < trip_temp) &&
+ (count <= max_state))
+ cdev->ops->set_cur_state(cdev, count);
+
+ last_trip_change = trip_temp;
+ }
+ break;
case THERMAL_TRIP_PASSIVE:
if (temp >= trip_temp || tz->passive)
thermal_zone_device_passive(tz, temp,
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 47b4a27..d7d0a27 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -42,6 +42,7 @@ enum thermal_trip_type {
THERMAL_TRIP_PASSIVE,
THERMAL_TRIP_HOT,
THERMAL_TRIP_CRITICAL,
+ THERMAL_TRIP_STATE_ACTIVE,
};
struct thermal_zone_device_ops {
--
1.7.1
^ permalink raw reply related
* [RFC PATCH 0/2] thermal: Add generic cpu cooling devices according to thermal framework
From: Amit Daniel Kachhap @ 2011-12-13 15:13 UTC (permalink / raw)
To: linux-pm; +Cc: linaro-dev, patches, linux-kernel, linux-acpi
PATCH 1) [thermal: Add a new trip type to use cooling device instance number]
This patch adds a new trip type THERMAL_TRIP_STATE_ACTIVE which passes
cooling device instance number and may be helpful for cpufreq cooling devices
to take the correct cooling action.
PATCH 2) [thermal: Add generic cpu cooling implementation]
This patch adds generic cpu cooling low level implementation through frequency
clipping and cpu hotplug. In future, other cpu related cooling devices may be
added here. An ACPI version of this already exists(drivers/acpi/processor_thermal.c).
But this will be useful for platforms like ARM using the generic thermal interface
along with the generic cpu cooling devices. The cooling device registration API's
return cooling device pointers which can be easily binded with the thermal zone
trip points.
Amit Daniel Kachhap (2):
thermal: Add a new trip type to use cooling device instance number
thermal: Add generic cpu cooling implementation
Documentation/thermal/cpu-cooling-api.txt | 52 +++++
Documentation/thermal/sysfs-api.txt | 4 +-
drivers/thermal/Kconfig | 11 +
drivers/thermal/Makefile | 1 +
drivers/thermal/cpu_cooling.c | 302 +++++++++++++++++++++++++++++
drivers/thermal/thermal_sys.c | 27 +++-
include/linux/cpu_cooling.h | 45 +++++
include/linux/thermal.h | 1 +
8 files changed, 440 insertions(+), 3 deletions(-)
create mode 100644 Documentation/thermal/cpu-cooling-api.txt
create mode 100644 drivers/thermal/cpu_cooling.c
create mode 100644 include/linux/cpu_cooling.h
^ permalink raw reply
* Re: [PATCH UPDATED AGAIN 03/10] threadgroup: extend threadgroup_lock() to cover exit and exec
From: Tejun Heo @ 2011-12-13 2:17 UTC (permalink / raw)
To: Frederic Weisbecker
Cc: akpm, paul, lizf, linux-kernel, oleg, linux-pm, Linus Torvalds,
containers, kamezawa.hiroyu
In-Reply-To: <20111213013334.GC25802@google.com>
Updated patchset published at
git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup.git for-3.3
and merged branch also pushed out to for-next.
Thanks.
--
tejun
^ permalink raw reply
* Re: [PATCH UPDATED AGAIN 03/10] threadgroup: extend threadgroup_lock() to cover exit and exec
From: Tejun Heo @ 2011-12-13 1:33 UTC (permalink / raw)
To: Frederic Weisbecker
Cc: akpm, paul, lizf, linux-kernel, oleg, linux-pm, Linus Torvalds,
containers, kamezawa.hiroyu
In-Reply-To: <20111209234246.GC27173@somewhere>
Hello,
On Sat, Dec 10, 2011 at 12:42:53AM +0100, Frederic Weisbecker wrote:
> > Okay, narrowed exit path protection down to setting of PF_EXITING
> > itself. ->exit() on dangling tasks is a bit weird but I don't think
> > it's too bad. Frederic, are you okay with this version?
>
> Yeah that new scheme that only protects PF_EXITING may look a bit
> strange. But I think we are fine. With rcu list traversal, it should
> be safe even if a group member is concurrently dropped from the list (in that
> case all we check if its PF_EXITING then we give up). And we may
> have a concurrent ->exit() but that should be fine too.
>
> Thanks!
>
> Acked-by: Frederic Weisbecker <fweisbec@gmail.com>
Awesome, adding Acked-by, putting it on top of pm-freezer and pushing
it out to linux-next.
Thank you.
--
tejun
^ permalink raw reply
* Re: [PATCH 0/2] OMAP: PM: switch from omap_pm_ functions to PM QoS
From: Jean Pihet @ 2011-12-12 16:27 UTC (permalink / raw)
To: Kevin Hilman, Linux PM mailing list, linux-omap,
Rafael J. Wysocki, Paul Walmsley, magnus.damm, Todd Poynor
Cc: Jean Pihet
In-Reply-To: <1319034531-22964-1-git-send-email-j-pihet@ti.com>
Hi Kevin, Paul,
ping on this series
Thanks,
Jean
On Wed, Oct 19, 2011 at 4:28 PM, <jean.pihet@newoldbits.com> wrote:
> From: Jean Pihet <j-pihet@ti.com>
>
> . Convert the OMAP I2C driver to the PM QoS API for MPU latency constraints
> . Remove the latency related functions from OMAP PM in favor of
> the generic per-device PM QoS API
>
>
> Apply on top of the OMAP PM QoS patch set [1].
> Based on the pm-qos branch of the linux-pm git tree (3.1.0-rc3), cf. [2].
>
> Tested on OMAP3 Beagleboard (ES2.x) with constraints on MPU, CORE, PER in
> RETention and OFF modes.
>
> [1] http://thread.gmane.org/gmane.linux.ports.arm.omap/65971
> [2] git://github.com/rjwysocki/linux-pm.git
>
>
> Jean Pihet (2):
> OMAP: convert I2C driver to PM QoS for latency constraints
> OMAP: PM: remove the latency related functions from the API
>
> Documentation/arm/OMAP/omap_pm | 55 +++-------------
> arch/arm/plat-omap/i2c.c | 20 ------
> arch/arm/plat-omap/include/plat/omap-pm.h | 99 -----------------------------
> arch/arm/plat-omap/omap-pm-noop.c | 88 -------------------------
> drivers/i2c/busses/i2c-omap.c | 30 +++++-----
> include/linux/i2c-omap.h | 1 -
> 6 files changed, 24 insertions(+), 269 deletions(-)
>
> --
> 1.7.4.1
>
^ permalink raw reply
* Re: [PATCH 4/6] OMAP3: cpuidle: next C-state decision depends on the PM QoS MPU and CORE constraints
From: Jean Pihet @ 2011-12-12 16:26 UTC (permalink / raw)
To: Kevin Hilman; +Cc: Linux PM mailing list, linux-omap, Jean Pihet
In-Reply-To: <877h2qbp56.fsf@ti.com>
Hi Kevin,
On Wed, Nov 23, 2011 at 8:43 PM, Kevin Hilman <khilman@ti.com> wrote:
> Jean Pihet <jean.pihet@newoldbits.com> writes:
>
>> On Thu, Nov 17, 2011 at 10:29 PM, Kevin Hilman <khilman@ti.com> wrote:
>>> jean.pihet@newoldbits.com writes:
>>>
>>>> From: Jean Pihet <j-pihet@ti.com>
>>>>
>>>> The MPU latency figures for cpuidle include the MPU itself and also
>>>> the peripherals needed for the MPU to execute instructions (e.g.
>>>> main memory, caches, IRQ controller, MMU etc). On OMAP3 those
>>>> peripherals belong to the MPU and CORE power domains and so the
>>>> cpuidle C-states are a combination of MPU and CORE states.
>>>>
>>>> This patch implements the relation between the cpuidle and per-
>>>> device PM QoS frameworks in the OMAP3 specific idle callbacks.
>>>>
>>>> The chosen C-state shall satisfy the following conditions:
>>>> . the 'valid' field is enabled,
>>>> . it satisfies the enable_off_mode flag,
>>>
>>> Not directly related to this patch, but is there any reason to keep the
>>> 'enable_off_mode' flag after this series?
>> enable_off_mode could be removed completely after this series unless
>> there is a need to prevent OFF mode for debug reasons.
>
> Great.
>
> For debug reasons, we can just as easily set constraints to prevent off
> mode, so I would like to see it disappear.
I have a WIP patch that removes the enable_off_mode flag and will post
it as soon as this series is in the upstream pipe.
Thanks,
Jean
>
> Kevin
>
^ permalink raw reply
* [PATCH 6/6] OMAP3: powerdomain data: add wake-up latency figures
From: jean.pihet @ 2011-12-12 16:18 UTC (permalink / raw)
To: Kevin Hilman, Linux PM mailing list, linux-omap,
Rafael J. Wysocki, Paul Walmsley, magnus.damm, Todd Poynor
Cc: Jean Pihet, linux-arm
In-Reply-To: <1323706701-6627-1-git-send-email-j-pihet@ti.com>
From: Jean Pihet <j-pihet@ti.com>
Figures are added to the power domains structs for RET and OFF modes.
Note: the latency figures for MPU, PER, CORE, NEON have been obtained
from actual measurements.
The latency figures for the other power domains are preliminary and
shall be added.
Cf. http://www.omappedia.org/wiki/Power_Management_Device_Latencies_Measurement
for a detailed explanation on where are the numbers coming from.
Tested on OMAP3 Beagleboard in RET/OFF using wake-up latency constraints
on MPU, CORE and PER.
Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
arch/arm/mach-omap2/powerdomains3xxx_data.c | 83 +++++++++++++++++++++++++++
1 files changed, 83 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-omap2/powerdomains3xxx_data.c b/arch/arm/mach-omap2/powerdomains3xxx_data.c
index 8ef26da..63a3afd 100644
--- a/arch/arm/mach-omap2/powerdomains3xxx_data.c
+++ b/arch/arm/mach-omap2/powerdomains3xxx_data.c
@@ -31,6 +31,19 @@
/*
* Powerdomains
+ *
+ * The wakeup_lat values are derived from HW and SW measurements on
+ * the actual target. For more details cf.
+ * http://www.omappedia.org/wiki/Power_Management_Device_Latencies_Measurement#Results_for_individual_power_domains
+ *
+ * Note: the latency figures for MPU, PER, CORE, NEON have been obtained
+ * from actual measurements.
+ * The latency figures for the other power domains are preliminary and
+ * shall be added.
+ *
+ * Note: only the SW restore timing values are taken into account.
+ * The HW impact of the sys_clkreq and sys_offmode signals is not taken
+ * into account - TDB
*/
static struct powerdomain iva2_pwrdm = {
@@ -51,6 +64,13 @@ static struct powerdomain iva2_pwrdm = {
[2] = PWRSTS_OFF_ON,
[3] = PWRSTS_ON,
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 1100,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = 350,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
.voltdm = { .name = "mpu_iva" },
};
@@ -67,6 +87,13 @@ static struct powerdomain mpu_3xxx_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_OFF_ON,
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 1830,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = 121,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
.voltdm = { .name = "mpu_iva" },
};
@@ -94,6 +121,13 @@ static struct powerdomain core_3xxx_pre_es3_1_pwrdm = {
[0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 3082,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = 153,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
.voltdm = { .name = "core" },
};
@@ -116,6 +150,13 @@ static struct powerdomain core_3xxx_es3_1_pwrdm = {
[0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 3082,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = 153,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
.voltdm = { .name = "core" },
};
@@ -131,6 +172,13 @@ static struct powerdomain dss_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 70,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = 20,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
.voltdm = { .name = "core" },
};
@@ -152,6 +200,13 @@ static struct powerdomain sgx_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 1000,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
.voltdm = { .name = "core" },
};
@@ -167,6 +222,13 @@ static struct powerdomain cam_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 850,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = 35,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
.voltdm = { .name = "core" },
};
@@ -182,6 +244,13 @@ static struct powerdomain per_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 671,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = 31,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
.voltdm = { .name = "core" },
};
@@ -196,6 +265,13 @@ static struct powerdomain neon_pwrdm = {
.prcm_offs = OMAP3430_NEON_MOD,
.pwrsts = PWRSTS_OFF_RET_ON,
.pwrsts_logic_ret = PWRSTS_RET,
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 0,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = 0,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
.voltdm = { .name = "mpu_iva" },
};
@@ -218,6 +294,13 @@ static struct powerdomain usbhost_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 800,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = 150,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
.voltdm = { .name = "core" },
};
--
1.7.5.4
^ permalink raw reply related
* [PATCH 5/6] OMAP3: update cpuidle latency and threshold figures
From: jean.pihet @ 2011-12-12 16:18 UTC (permalink / raw)
To: Kevin Hilman, Linux PM mailing list, linux-omap,
Rafael J. Wysocki, Paul Walmsley, magnus.damm, Todd Poynor
Cc: Jean Pihet, linux-arm
In-Reply-To: <1323706701-6627-1-git-send-email-j-pihet@ti.com>
From: Jean Pihet <j-pihet@ti.com>
Update the data from the measurements performed at HW and SW levels.
Cf. http://www.omappedia.org/wiki/Power_Management_Device_Latencies_Measurement
for a detailed explanation on where are the numbers coming from.
ToDo:
- Measure the wake-up latencies for all power domains for OMAP3
- Correct some numbers when sys_clkreq and sys_offmode are supported
Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
arch/arm/mach-omap2/cpuidle34xx.c | 52 +++++++++++++++++++++++-------------
1 files changed, 33 insertions(+), 19 deletions(-)
diff --git a/arch/arm/mach-omap2/cpuidle34xx.c b/arch/arm/mach-omap2/cpuidle34xx.c
index c67835f..3caa932 100644
--- a/arch/arm/mach-omap2/cpuidle34xx.c
+++ b/arch/arm/mach-omap2/cpuidle34xx.c
@@ -40,27 +40,41 @@
#ifdef CONFIG_CPU_IDLE
/*
- * The MPU latencies/thresholds for various C states have
- * to be configured from the respective board files.
- * These are some default values (which might not provide
- * the best power savings) used on boards which do not
- * pass these details from the board file.
+ * The MPU latency and threshold values for the C-states are the worst case
+ * values from the HW and SW, as described in details at
+ * http://www.omappedia.org/wiki/Power_Management_Device_Latencies_Measurement#cpuidle_results
+ *
+ * Measurements conditions and remarks:
+ * . the measurements have been performed at OPP50
+ * . the sys_offmode signal is not supported and so not used for the
+ * measurements. Instead the latency and threshold values for C9 are
+ * corrected with the value for Triton 2, which is 11.5ms
+ * . the sys_clkreq signal is not used and so a correction is needed - TBD
+ * . the sys_clkoff signal is supported, this value need to be corrected with
+ * the correct value of SYSCLK on/off timings (1ms for sysclk on, 2.5ms
+ * for sysclk off)
+ * . in order to force the cpuidle algorithm to chose the power efficient
+ * C-states (C1, C3, C5, C7) in preference, the other C-states have a
+ * threshold value equal to the next power efficient C-state
+ *
+ * The latency and threshold values can be overriden by data from the board
+ * files, using omap3_pm_init_cpuidle.
*/
static struct cpuidle_params cpuidle_params_table[] = {
- /* C1 */
- {2 + 2, 5, 1},
- /* C2 */
- {10 + 10, 30, 1},
- /* C3 */
- {50 + 50, 300, 1},
- /* C4 */
- {1500 + 1800, 4000, 1},
- /* C5 */
- {2500 + 7500, 12000, 1},
- /* C6 */
- {3000 + 8500, 15000, 1},
- /* C7 */
- {10000 + 30000, 300000, 1},
+ /* C1 . MPU WFI + Core active */
+ {73 + 78, 152, 1},
+ /* C2 . MPU WFI + Core inactive */
+ {165 + 88, 345, 1},
+ /* C3 . MPU CSWR + Core inactive */
+ {163 + 182, 345, 1},
+ /* C4 . MPU OFF + Core inactive */
+ {2852 + 605, 150000, 1},
+ /* C5 . MPU RET + Core RET */
+ {800 + 366, 2120, 1},
+ /* C6 . MPU OFF + Core RET */
+ {4080 + 801, 215000, 1},
+ /* C7 . MPU OFF + Core OFF */
+ {4300 + 13000, 215000, 1},
};
#define OMAP3_NUM_STATES ARRAY_SIZE(cpuidle_params_table)
--
1.7.5.4
^ permalink raw reply related
* [PATCH 4/6] OMAP3: cpuidle: next C-state decision depends on the PM QoS MPU and CORE constraints
From: jean.pihet @ 2011-12-12 16:18 UTC (permalink / raw)
To: Kevin Hilman, Linux PM mailing list, linux-omap,
Rafael J. Wysocki, Paul Walmsley, magnus.damm, Todd Poynor
Cc: Jean Pihet, linux-arm
In-Reply-To: <1323706701-6627-1-git-send-email-j-pihet@ti.com>
From: Jean Pihet <j-pihet@ti.com>
The MPU latency figures for cpuidle include the MPU itself and also
the peripherals needed for the MPU to execute instructions (e.g.
main memory, caches, IRQ controller, MMU etc). On OMAP3 those
peripherals belong to the MPU and CORE power domains and so the
cpuidle C-states are a combination of MPU and CORE states.
This patch implements the relation between the cpuidle and per-
device PM QoS frameworks in the OMAP3 specific idle callbacks.
The chosen C-state shall satisfy the following conditions:
. the 'valid' field is enabled,
. it satisfies the enable_off_mode flag,
. the next state for MPU and CORE power domains is not lower than the
next state calculated by the per-device PM QoS.
Tested on OMAP3 Beagleboard in RET/OFF using wake-up latency constraints
on MPU, CORE and PER.
Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
arch/arm/mach-omap2/cpuidle34xx.c | 57 ++++++++++++++++++++----------------
arch/arm/mach-omap2/pm.h | 17 ++++++++++-
2 files changed, 47 insertions(+), 27 deletions(-)
diff --git a/arch/arm/mach-omap2/cpuidle34xx.c b/arch/arm/mach-omap2/cpuidle34xx.c
index 1f71ebb..c67835f 100644
--- a/arch/arm/mach-omap2/cpuidle34xx.c
+++ b/arch/arm/mach-omap2/cpuidle34xx.c
@@ -40,7 +40,7 @@
#ifdef CONFIG_CPU_IDLE
/*
- * The latencies/thresholds for various C states have
+ * The MPU latencies/thresholds for various C states have
* to be configured from the respective board files.
* These are some default values (which might not provide
* the best power savings) used on boards which do not
@@ -75,14 +75,14 @@ struct omap3_idle_statedata omap3_idle_data[OMAP3_NUM_STATES];
struct powerdomain *mpu_pd, *core_pd, *per_pd, *cam_pd;
static int _cpuidle_allow_idle(struct powerdomain *pwrdm,
- struct clockdomain *clkdm)
+ struct clockdomain *clkdm)
{
clkdm_allow_idle(clkdm);
return 0;
}
static int _cpuidle_deny_idle(struct powerdomain *pwrdm,
- struct clockdomain *clkdm)
+ struct clockdomain *clkdm)
{
clkdm_deny_idle(clkdm);
return 0;
@@ -96,10 +96,13 @@ static int _cpuidle_deny_idle(struct powerdomain *pwrdm,
*
* Called from the CPUidle framework to program the device to the
* specified target state selected by the governor.
+ *
+ * Note: this function does not check for any pending activity or dependency
+ * between power domains states, so the caller shall check the parameters
+ * correctness.
*/
static int omap3_enter_idle(struct cpuidle_device *dev,
- struct cpuidle_driver *drv,
- int index)
+ struct cpuidle_driver *drv, int index)
{
struct omap3_idle_statedata *cx =
cpuidle_get_statedata(&dev->states_usage[index]);
@@ -174,18 +177,23 @@ return_sleep_time:
* to the caller. Else, this function searches for a lower c-state which is
* still valid (as defined in omap3_power_states[]) and returns its index.
*
- * A state is valid if the 'valid' field is enabled and
- * if it satisfies the enable_off_mode condition.
+ * A state is valid if:
+ * . the 'valid' field is enabled,
+ * . it satisfies the enable_off_mode flag,
+ * . the next state for MPU and CORE power domains is not lower than the
+ * state programmed by the per-device PM QoS.
*/
static int next_valid_state(struct cpuidle_device *dev,
- struct cpuidle_driver *drv,
- int index)
+ struct cpuidle_driver *drv, int index)
{
struct cpuidle_state_usage *curr_usage = &dev->states_usage[index];
struct cpuidle_state *curr = &drv->states[index];
struct omap3_idle_statedata *cx = cpuidle_get_statedata(curr_usage);
u32 mpu_deepest_state = PWRDM_POWER_RET;
u32 core_deepest_state = PWRDM_POWER_RET;
+ u32 mpu_pm_qos_next_state = mpu_pd->wkup_lat_next_state;
+ u32 core_pm_qos_next_state = core_pd->wkup_lat_next_state;
+
int next_index = -1;
if (enable_off_mode) {
@@ -202,7 +210,9 @@ static int next_valid_state(struct cpuidle_device *dev,
/* Check if current state is valid */
if ((cx->valid) &&
(cx->mpu_state >= mpu_deepest_state) &&
- (cx->core_state >= core_deepest_state)) {
+ (cx->core_state >= core_deepest_state) &&
+ (cx->mpu_state >= mpu_pm_qos_next_state) &&
+ (cx->core_state >= core_pm_qos_next_state)) {
return index;
} else {
int idx = OMAP3_NUM_STATES - 1;
@@ -227,7 +237,9 @@ static int next_valid_state(struct cpuidle_device *dev,
cx = cpuidle_get_statedata(&dev->states_usage[idx]);
if ((cx->valid) &&
(cx->mpu_state >= mpu_deepest_state) &&
- (cx->core_state >= core_deepest_state)) {
+ (cx->core_state >= core_deepest_state) &&
+ (cx->mpu_state >= mpu_pm_qos_next_state) &&
+ (cx->core_state >= core_pm_qos_next_state)) {
next_index = idx;
break;
}
@@ -248,12 +260,15 @@ static int next_valid_state(struct cpuidle_device *dev,
* @drv: cpuidle driver
* @index: array index of target state to be programmed
*
- * This function checks for any pending activity and then programs
- * the device to the specified or a safer state.
+ * Called from the CPUidle framework to program the device to the
+ * specified target state selected by the governor.
+ *
+ * This function checks for any pending activity or dependency between
+ * power domains states and then programs the device to the specified
+ * or a safer state.
*/
static int omap3_enter_idle_bm(struct cpuidle_device *dev,
- struct cpuidle_driver *drv,
- int index)
+ struct cpuidle_driver *drv, int index)
{
int new_state_idx;
u32 core_next_state, per_next_state = 0, per_saved_state = 0, cam_state;
@@ -275,19 +290,13 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev,
goto select_state;
}
- /*
- * FIXME: we currently manage device-specific idle states
- * for PER and CORE in combination with CPU-specific
- * idle states. This is wrong, and device-specific
- * idle management needs to be separated out into
- * its own code.
- */
+ new_state_idx = next_valid_state(dev, drv, index);
/*
* Prevent PER off if CORE is not in retention or off as this
* would disable PER wakeups completely.
*/
- cx = cpuidle_get_statedata(&dev->states_usage[index]);
+ cx = cpuidle_get_statedata(&dev->states_usage[new_state_idx]);
core_next_state = cx->core_state;
per_next_state = per_saved_state = pwrdm_read_next_pwrst(per_pd);
if ((per_next_state == PWRDM_POWER_OFF) &&
@@ -298,8 +307,6 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev,
if (per_next_state != per_saved_state)
pwrdm_set_next_pwrst(per_pd, per_next_state);
- new_state_idx = next_valid_state(dev, drv, index);
-
select_state:
ret = omap3_enter_idle(dev, drv, new_state_idx);
diff --git a/arch/arm/mach-omap2/pm.h b/arch/arm/mach-omap2/pm.h
index b737b11..ab32465 100644
--- a/arch/arm/mach-omap2/pm.h
+++ b/arch/arm/mach-omap2/pm.h
@@ -44,9 +44,22 @@ static inline int omap4_opp_init(void)
* omap3_pm_init_cpuidle
*/
struct cpuidle_params {
- u32 exit_latency; /* exit_latency = sleep + wake-up latencies */
+ /*
+ * exit_latency = sleep + wake-up latencies of the MPU,
+ * which include the MPU itself and the peripherals needed
+ * for the MPU to execute instructions (e.g. main memory,
+ * caches, IRQ controller, MMU etc). Some of those peripherals
+ * can belong to other power domains than the MPU subsystem and so
+ * the corresponding latencies must be included in this figure.
+ */
+ u32 exit_latency;
+ /*
+ * target_residency: required amount of time in the C state
+ * to break even on energy cost
+ */
u32 target_residency;
- u8 valid; /* validates the C-state */
+ /* validates the C-state on the given board */
+ u8 valid;
};
#if defined(CONFIG_PM) && defined(CONFIG_CPU_IDLE)
--
1.7.5.4
^ permalink raw reply related
* [PATCH 3/6] OMAP: PM: register to the per-device PM QoS framework
From: jean.pihet @ 2011-12-12 16:18 UTC (permalink / raw)
To: Kevin Hilman, Linux PM mailing list, linux-omap,
Rafael J. Wysocki, Paul Walmsley, magnus.damm, Todd Poynor
Cc: Jean Pihet, linux-arm
In-Reply-To: <1323706701-6627-1-git-send-email-j-pihet@ti.com>
From: Jean Pihet <j-pihet@ti.com>
Implement the devices wake-up latency constraints using the global
device PM QoS notification handler which applies the constraints to the
underlying layer by calling the corresponding function at hwmod level.
Tested on OMAP3 Beagleboard and OMAP4 Pandaboard in RET/OFF using wake-up
latency constraints on MPU, CORE and PER.
Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
arch/arm/mach-omap2/pm.c | 71 +++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 70 insertions(+), 1 deletions(-)
diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c
index 1881fe9..a40d4f3 100644
--- a/arch/arm/mach-omap2/pm.c
+++ b/arch/arm/mach-omap2/pm.c
@@ -11,15 +11,18 @@
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/notifier.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/opp.h>
+#include <linux/pm_qos.h>
#include <linux/export.h>
#include <plat/omap-pm.h>
#include <plat/omap_device.h>
-#include "common.h"
+#include <plat/omap_hwmod.h>
+#include "common.h"
#include "voltage.h"
#include "powerdomain.h"
#include "clockdomain.h"
@@ -215,12 +218,78 @@ static void __init omap4_init_voltages(void)
omap2_set_init_voltage("iva", "dpll_iva_m5x2_ck", "iva");
}
+/* Interface to the per-device PM QoS framework */
+static int omap2_dev_pm_qos_handler(struct notifier_block *nb,
+ unsigned long new_value,
+ void *req)
+{
+ struct omap_device *od;
+ struct omap_hwmod *oh;
+ struct platform_device *pdev;
+ struct dev_pm_qos_request *dev_pm_qos_req = req;
+
+ pr_debug("OMAP PM constraints: req@0x%p, new_value=%lu\n",
+ req, new_value);
+
+ /* Look for the platform device for the constraint target device */
+ pdev = to_platform_device(dev_pm_qos_req->dev);
+
+ /* Try to catch non platform devices */
+ if (pdev->name == NULL) {
+ pr_err("%s: Error: platform device for device %s not valid\n",
+ __func__, dev_name(dev_pm_qos_req->dev));
+ return -EINVAL;
+ }
+
+ /* Find the associated omap_device for dev */
+ od = to_omap_device(pdev);
+ if (od == NULL) {
+ pr_err("%s: Error: no omap_device for device %s\n",
+ __func__, dev_name(dev_pm_qos_req->dev));
+ return -EINVAL;
+ }
+
+ /* Check that the omap_device has an unique hwmod entry */
+ if (od->hwmods_cnt != 1) {
+ pr_err("%s: Error: No unique hwmod for device %s\n",
+ __func__, dev_name(dev_pm_qos_req->dev));
+ return -EINVAL;
+ }
+
+ /* Find the primary omap_hwmod for dev */
+ oh = od->hwmods[0];
+
+ pr_debug("OMAP PM constraints: req@0x%p, dev=0x%p, new_value=%lu\n",
+ req, dev_pm_qos_req->dev, new_value);
+
+ /* Apply the constraint */
+ return omap_hwmod_set_wkup_lat_constraint(oh, dev_pm_qos_req,
+ new_value);
+}
+
+static struct notifier_block omap2_dev_pm_qos_notifier = {
+ .notifier_call = omap2_dev_pm_qos_handler,
+};
+
+static int __init omap2_dev_pm_qos_init(void)
+{
+ int ret;
+
+ ret = dev_pm_qos_add_global_notifier(&omap2_dev_pm_qos_notifier);
+ WARN(ret, KERN_ERR "Cannot add global notifier for dev PM QoS\n");
+
+ return ret;
+}
+
static int __init omap2_common_pm_init(void)
{
if (!of_have_populated_dt())
omap2_init_processor_devices();
omap_pm_if_init();
+ /* Register to the per-device PM QoS framework */
+ omap2_dev_pm_qos_init();
+
return 0;
}
postcore_initcall(omap2_common_pm_init);
--
1.7.5.4
^ permalink raw reply related
* [PATCH 2/6] OMAP2+: omap_hwmod: manage the wake-up latency constraints
From: jean.pihet @ 2011-12-12 16:18 UTC (permalink / raw)
To: Kevin Hilman, Linux PM mailing list, linux-omap,
Rafael J. Wysocki, Paul Walmsley, magnus.damm, Todd Poynor
Cc: Jean Pihet, linux-arm
In-Reply-To: <1323706701-6627-1-git-send-email-j-pihet@ti.com>
From: Jean Pihet <j-pihet@ti.com>
Hwmod is queried from the OMAP_PM layer to manage the power domains
wake-up latency constraints. Hwmod retrieves the correct power domain
and if it exists it calls the corresponding power domain function.
Tested on OMAP3 Beagleboard and OMAP4 Pandaboard in RET/OFF using wake-up
latency constraints on MPU, CORE and PER.
Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
arch/arm/mach-omap2/omap_hwmod.c | 27 +++++++++++++++++++++++++-
arch/arm/plat-omap/include/plat/omap_hwmod.h | 2 +
2 files changed, 28 insertions(+), 1 deletions(-)
diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
index 529142a..c3a4cc4 100644
--- a/arch/arm/mach-omap2/omap_hwmod.c
+++ b/arch/arm/mach-omap2/omap_hwmod.c
@@ -143,6 +143,7 @@
#include "powerdomain.h"
#include <plat/clock.h>
#include <plat/omap_hwmod.h>
+#include <plat/omap_device.h>
#include <plat/prcm.h>
#include "cm2xxx_3xxx.h"
@@ -2616,10 +2617,34 @@ ohsps_unlock:
}
/**
+ * omap_hwmod_set_wkup_constraint- set/release a wake-up latency constraint
+ *
+ * @oh: struct omap_hwmod* to which the target device belongs to.
+ * @cookie: identifier of the constraints list for @oh.
+ * @min_latency: the minimum allowed wake-up latency for @oh.
+ *
+ * Returns 0 upon success, -EINVAL in case of invalid parameters, or
+ * the return value from pwrdm_set_wkup_lat_constraint.
+ */
+int omap_hwmod_set_wkup_lat_constraint(struct omap_hwmod *oh,
+ void *cookie, long min_latency)
+{
+ struct powerdomain *pwrdm = omap_hwmod_get_pwrdm(oh);
+
+ if (!pwrdm) {
+ pr_err("%s: Error: could not find powerdomain "
+ "for %s\n", __func__, oh->name);
+ return -EINVAL;
+ }
+
+ return pwrdm_set_wkup_lat_constraint(pwrdm, cookie, min_latency);
+}
+
+/**
* omap_hwmod_get_context_loss_count - get lost context count
* @oh: struct omap_hwmod *
*
- * Query the powerdomain of of @oh to get the context loss
+ * Query the powerdomain of @oh to get the context loss
* count for this device.
*
* Returns the context loss count of the powerdomain assocated with @oh
diff --git a/arch/arm/plat-omap/include/plat/omap_hwmod.h b/arch/arm/plat-omap/include/plat/omap_hwmod.h
index 8b372ed..222f792 100644
--- a/arch/arm/plat-omap/include/plat/omap_hwmod.h
+++ b/arch/arm/plat-omap/include/plat/omap_hwmod.h
@@ -600,6 +600,8 @@ int omap_hwmod_for_each_by_class(const char *classname,
void *user);
int omap_hwmod_set_postsetup_state(struct omap_hwmod *oh, u8 state);
+int omap_hwmod_set_wkup_lat_constraint(struct omap_hwmod *oh, void *cookie,
+ long min_latency);
int omap_hwmod_get_context_loss_count(struct omap_hwmod *oh);
int omap_hwmod_no_setup_reset(struct omap_hwmod *oh);
--
1.7.5.4
^ permalink raw reply related
* [PATCH 1/6] OMAP2+: powerdomain: control power domains next state
From: jean.pihet @ 2011-12-12 16:18 UTC (permalink / raw)
To: Kevin Hilman, Linux PM mailing list, linux-omap,
Rafael J. Wysocki, Paul Walmsley, magnus.damm, Todd Poynor
Cc: Jean Pihet, linux-arm
In-Reply-To: <1323706701-6627-1-git-send-email-j-pihet@ti.com>
From: Jean Pihet <j-pihet@ti.com>
When a PM QoS device latency constraint is requested or removed the
PM QoS layer notifies the underlying layer with the updated aggregated
constraint value. The constraint is stored in the powerdomain constraints
list and then applied to the corresponding power domain.
The power domains get the next power state programmed directly in the
registers via pwrdm_wakeuplat_update_pwrst.
Tested on OMAP3 Beagleboard and OMAP4 Pandaboard in RET/OFF using
wake-up latency constraints on MPU, CORE and PER.
Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
arch/arm/mach-omap2/powerdomain.c | 245 +++++++++++++++++++++++++++++++++++++
arch/arm/mach-omap2/powerdomain.h | 36 +++++-
2 files changed, 279 insertions(+), 2 deletions(-)
diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c
index 8a18d1b..677a182 100644
--- a/arch/arm/mach-omap2/powerdomain.c
+++ b/arch/arm/mach-omap2/powerdomain.c
@@ -17,8 +17,10 @@
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/list.h>
+#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/string.h>
+#include <linux/pm_qos.h>
#include <trace/events/power.h>
#include "cm2xxx_3xxx.h"
@@ -112,6 +114,12 @@ static int _pwrdm_register(struct powerdomain *pwrdm)
for (i = 0; i < pwrdm->banks; i++)
pwrdm->ret_mem_off_counter[i] = 0;
+ /* Initialize the per-device wake-up constraints framework data */
+ spin_lock_init(&pwrdm->wkup_lat_plist_lock);
+ plist_head_init(&pwrdm->wkup_lat_plist_head);
+ pwrdm->wkup_lat_next_state = PWRDM_POWER_OFF;
+
+ /* Initialize the pwrdm state */
pwrdm_wait_transition(pwrdm);
pwrdm->state = pwrdm_read_pwrst(pwrdm);
pwrdm->state_counter[pwrdm->state] = 1;
@@ -199,6 +207,158 @@ static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
return 0;
}
+/**
+ * _pwrdm_wakeuplat_update_list - Set/update/remove a powerdomain wakeup
+ * latency constraint from the pwrdm's constraint list
+ * @pwrdm: struct powerdomain * which the constraint applies to
+ * @cookie: constraint identifier, used for tracking.
+ * @min_latency: minimum wakeup latency constraint (in microseconds) for
+ * the given pwrdm. The value of PM_QOS_DEV_LAT_DEFAULT_VALUE removes
+ * the constraint.
+ * @user: pointer to the current list entry
+ * @new_user: allocated list entry, used for insertion of new constraints
+ * in the list
+ * @free_new_user: set to non-zero if the newly allocated list entry
+ * is unused and needs to be freed
+ * @free_node: set to non-zero if the current list entry is not in use
+ * anymore and needs to be freed
+ *
+ * Tracks the constraints by @cookie.
+ * Constraint set/update: Adds a new entry to powerdomain's wake-up latency
+ * constraint list.
+ * If the constraint identifier already exists in the list, the old value is
+ * overwritten.
+ * Constraint removal: Removes the identifier's entry from powerdomain's
+ * wakeup latency constraint list.
+ *
+ * Called with the pwrdm wakeup latency spinlock held.
+ *
+ * Returns 0 upon success, -EINVAL if the constraint is not existing.
+ */
+static inline int _pwrdm_update_wakeuplat_list(
+ struct powerdomain *pwrdm,
+ void *cookie,
+ long min_latency,
+ struct pwrdm_wkup_constraints_entry *user,
+ struct pwrdm_wkup_constraints_entry *new_user,
+ int *free_new_user,
+ int *free_node)
+{
+ struct pwrdm_wkup_constraints_entry *tmp_user;
+
+ /* Check if there already is a constraint for cookie */
+ plist_for_each_entry(tmp_user, &pwrdm->wkup_lat_plist_head, node) {
+ if (tmp_user->cookie == cookie) {
+ user = tmp_user;
+ break;
+ }
+ }
+
+ if (min_latency != PM_QOS_DEV_LAT_DEFAULT_VALUE) {
+ /* If nothing to update, job done */
+ if (user && (user->node.prio == min_latency))
+ return 0;
+
+ if (!user) {
+ /* Add new entry to the list */
+ user = new_user;
+ user->cookie = cookie;
+ *free_new_user = 0;
+ } else {
+ /* Update existing entry */
+ plist_del(&user->node, &pwrdm->wkup_lat_plist_head);
+ }
+
+ plist_node_init(&user->node, min_latency);
+ plist_add(&user->node, &pwrdm->wkup_lat_plist_head);
+ } else {
+ if (user) {
+ /* Remove the constraint from the list */
+ plist_del(&user->node, &pwrdm->wkup_lat_plist_head);
+ *free_node = 1;
+ } else {
+ /* Constraint not existing or list empty, do nothing */
+ return -EINVAL;
+ }
+
+ }
+
+ return 0;
+}
+
+/**
+ * _pwrdm_wakeuplat_update_pwrst - Update power domain power state if needed
+ * @pwrdm: struct powerdomain * to which requesting device belongs to.
+ * @min_latency: the allowed wake-up latency for the given power domain. A
+ * value of PM_QOS_DEV_LAT_DEFAULT_VALUE means 'no constraint' on the pwrdm.
+ *
+ * Finds the power domain next power state that fulfills the constraint.
+ * Programs a new target state if it is different from current power state.
+ * The power domains get the next power state programmed directly in the
+ * registers.
+ *
+ * Returns 0 in case of success, -EINVAL in case of invalid parameters,
+ * or the return value from omap_set_pwrdm_state.
+ */
+static int _pwrdm_wakeuplat_update_pwrst(struct powerdomain *pwrdm,
+ long min_latency)
+{
+ int ret = 0, new_state;
+
+ if (!pwrdm) {
+ WARN(1, "powerdomain: %s: invalid parameter(s)", __func__);
+ return -EINVAL;
+ }
+
+ /*
+ * Find the next supported power state with
+ * wakeup latency < minimum constraint
+ */
+ for (new_state = 0x0; new_state < PWRDM_MAX_PWRSTS; new_state++) {
+ if (min_latency == PM_QOS_DEV_LAT_DEFAULT_VALUE)
+ break;
+ if ((pwrdm->wakeup_lat[new_state] != UNSUP_STATE) &&
+ (pwrdm->wakeup_lat[new_state] <= min_latency))
+ break;
+ }
+
+ switch (new_state) {
+ case PWRDM_FUNC_PWRST_OFF:
+ new_state = PWRDM_POWER_OFF;
+ break;
+ case PWRDM_FUNC_PWRST_OSWR:
+ pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_OFF);
+ new_state = PWRDM_POWER_RET;
+ break;
+ case PWRDM_FUNC_PWRST_CSWR:
+ pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_RET);
+ new_state = PWRDM_POWER_RET;
+ break;
+ case PWRDM_FUNC_PWRST_INACTIVE:
+ new_state = PWRDM_POWER_INACTIVE;
+ break;
+ case PWRDM_FUNC_PWRST_ON:
+ new_state = PWRDM_POWER_ON;
+ break;
+ default:
+ pr_warn("powerdomain: requested latency constraint not "
+ "supported %s set to ON state\n", pwrdm->name);
+ new_state = PWRDM_POWER_ON;
+ break;
+ }
+
+ pwrdm->wkup_lat_next_state = new_state;
+ if (pwrdm_read_next_pwrst(pwrdm) != new_state)
+ ret = omap_set_pwrdm_state(pwrdm, new_state);
+
+ pr_debug("powerdomain: %s pwrst: curr=%d, prev=%d next=%d "
+ "min_latency=%ld, set_state=%d\n", pwrdm->name,
+ pwrdm_read_pwrst(pwrdm), pwrdm_read_prev_pwrst(pwrdm),
+ pwrdm_read_next_pwrst(pwrdm), min_latency, new_state);
+
+ return ret;
+}
+
/* Public functions */
/**
@@ -998,6 +1158,91 @@ int pwrdm_post_transition(void)
}
/**
+ * pwrdm_set_wkup_lat_constraint - Set/update/remove a powerdomain wakeup
+ * latency constraint and apply it
+ * @pwrdm: struct powerdomain * which the constraint applies to
+ * @cookie: constraint identifier, used for tracking.
+ * @min_latency: minimum wakeup latency constraint (in microseconds) for
+ * the given pwrdm. The value of PM_QOS_DEV_LAT_DEFAULT_VALUE removes
+ * the constraint.
+ *
+ * Tracks the constraints by @cookie.
+ * Constraint set/update: Adds a new entry to powerdomain's wake-up latency
+ * constraint list.
+ * If the constraint identifier already exists in the list, the old value is
+ * overwritten.
+ * Constraint removal: Removes the identifier's entry from powerdomain's
+ * wakeup latency constraint list.
+ *
+ * Applies the aggregated constraint value for the given pwrdm by calling
+ * _pwrdm_wakeuplat_update_pwrst.
+ *
+ * Returns 0 upon success, -ENOMEM in case of memory shortage, -EINVAL in
+ * case of invalid parameters, or the return value from
+ * _pwrdm_wakeuplat_update_pwrst.
+ *
+ * The caller must check the validity of the parameters.
+ *
+ * Note about the resources allocation and release:
+ * The free_new_user and free_node variables are used to indicate
+ * if the allocated resources can be freed, respectively for the
+ * newly allocated list entry and the current list entry.
+ * Cf. kerneldoc for the _pwrdm_update_wakeuplat_list function for
+ * detailed information about those variables and the
+ * allocation and release of the constraints list entries.
+ */
+int pwrdm_set_wkup_lat_constraint(struct powerdomain *pwrdm, void *cookie,
+ long min_latency)
+{
+ struct pwrdm_wkup_constraints_entry *user = NULL, *new_user = NULL;
+ int ret = 0, free_new_user = 0, free_node = 0;
+ long value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+ unsigned long flags;
+
+ pr_debug("powerdomain: %s: pwrdm %s, cookie=0x%p, min_latency=%ld\n",
+ __func__, pwrdm->name, cookie, min_latency);
+
+ if (min_latency != PM_QOS_DEV_LAT_DEFAULT_VALUE) {
+ new_user = kzalloc(sizeof(struct pwrdm_wkup_constraints_entry),
+ GFP_KERNEL);
+ if (!new_user) {
+ pr_err("%s: FATAL ERROR: kzalloc failed\n", __func__);
+ return -ENOMEM;
+ }
+ free_new_user = 1;
+ }
+
+ spin_lock_irqsave(&pwrdm->wkup_lat_plist_lock, flags);
+
+ /* Manage the constraints list */
+ ret = _pwrdm_update_wakeuplat_list(pwrdm, cookie, min_latency,
+ user, new_user,
+ &free_new_user, &free_node);
+
+ /* Find the aggregated constraint value from the list */
+ if (!ret)
+ if (!plist_head_empty(&pwrdm->wkup_lat_plist_head))
+ value = plist_first(&pwrdm->wkup_lat_plist_head)->prio;
+
+ spin_unlock_irqrestore(&pwrdm->wkup_lat_plist_lock, flags);
+
+ if (free_node)
+ kfree(user);
+
+ if (free_new_user)
+ kfree(new_user);
+
+ /* Apply the constraint to the pwrdm */
+ if (!ret) {
+ pr_debug("powerdomain: %s: pwrdm %s, value=%ld\n",
+ __func__, pwrdm->name, value);
+ ret = _pwrdm_wakeuplat_update_pwrst(pwrdm, value);
+ }
+
+ return ret;
+}
+
+/**
* pwrdm_get_context_loss_count - get powerdomain's context loss count
* @pwrdm: struct powerdomain * to wait for
*
diff --git a/arch/arm/mach-omap2/powerdomain.h b/arch/arm/mach-omap2/powerdomain.h
index 0d72a8a..b4a60cf 100644
--- a/arch/arm/mach-omap2/powerdomain.h
+++ b/arch/arm/mach-omap2/powerdomain.h
@@ -19,7 +19,9 @@
#include <linux/types.h>
#include <linux/list.h>
-
+#include <linux/plist.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
#include <linux/atomic.h>
#include <plat/cpu.h>
@@ -45,6 +47,16 @@
#define PWRSTS_RET_ON (PWRSTS_RET | PWRSTS_ON)
#define PWRSTS_OFF_RET_ON (PWRSTS_OFF_RET | PWRSTS_ON)
+/* Powerdomain functional power states */
+#define PWRDM_FUNC_PWRST_OFF 0x0
+#define PWRDM_FUNC_PWRST_OSWR 0x1
+#define PWRDM_FUNC_PWRST_CSWR 0x2
+#define PWRDM_FUNC_PWRST_INACTIVE 0x3
+#define PWRDM_FUNC_PWRST_ON 0x4
+
+#define PWRDM_MAX_FUNC_PWRSTS 5
+
+#define UNSUP_STATE -1
/* Powerdomain flags */
#define PWRDM_HAS_HDWR_SAR (1 << 0) /* hardware save-and-restore support */
@@ -96,7 +108,13 @@ struct powerdomain;
* @state_counter:
* @timer:
* @state_timer:
- *
+ * @wakeup_lat: wakeup latencies (in us) for possible powerdomain power states
+ * Note about the wakeup latencies ordering: the values must be sorted
+ * in decremental order
+ * @wkup_lat_plist_head: pwrdm wake-up latency constraints list
+ * @wkup_lat_plist_lock: spinlock that protects the constraints lists
+ * domains states
+ * @wkup_lat_next_state: next pwrdm state, calculated from the constraints list
* @prcm_partition possible values are defined in mach-omap2/prcm44xx.h.
*/
struct powerdomain {
@@ -125,6 +143,16 @@ struct powerdomain {
s64 timer;
s64 state_timer[PWRDM_MAX_PWRSTS];
#endif
+ const s32 wakeup_lat[PWRDM_MAX_FUNC_PWRSTS];
+ struct plist_head wkup_lat_plist_head;
+ spinlock_t wkup_lat_plist_lock;
+ int wkup_lat_next_state;
+};
+
+/* Linked list for the wake-up latency constraints */
+struct pwrdm_wkup_constraints_entry {
+ void *cookie;
+ struct plist_node node;
};
/**
@@ -217,6 +245,10 @@ int pwrdm_clkdm_state_switch(struct clockdomain *clkdm);
int pwrdm_pre_transition(void);
int pwrdm_post_transition(void);
int pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm);
+
+int pwrdm_set_wkup_lat_constraint(struct powerdomain *pwrdm, void *cookie,
+ long min_latency);
+
int pwrdm_get_context_loss_count(struct powerdomain *pwrdm);
bool pwrdm_can_ever_lose_context(struct powerdomain *pwrdm);
--
1.7.5.4
^ permalink raw reply related
* [PATCH v5 0/6] PM QoS: implement the OMAP low level constraints management code
From: jean.pihet @ 2011-12-12 16:18 UTC (permalink / raw)
To: Kevin Hilman, Linux PM mailing list, linux-omap,
Rafael J. Wysocki, Paul Walmsley, magnus.damm, Todd Poynor
Cc: Jean Pihet, linux-arm
From: Jean Pihet <j-pihet@ti.com>
. Implement the devices wake-up latency constraints using the global
device PM QoS notification handler which applies the constraints to the
underlying layer
. Implement the low level code which controls the power domains next power
states, through the hwmod and pwrdm layers
. Add cpuidle and power domains wake-up latency figures for OMAP3, cf.
comments in the code and [1] for the details on where the numbers
are magically coming from
. Implement the relation between the cpuidle and per-device PM QoS frameworks
in the OMAP3 specific idle callbacks.
The chosen C-state shall satisfy the following conditions:
. the 'valid' field is enabled,
. it satisfies the enable_off_mode flag,
. the next state for MPU and CORE power domains is not lower than the
state programmed by the per-device PM QoS.
ToDo:
1. validate the constraints framework on OMAP4 HW (done on OMAP3)
2. Re-visit the OMAP power domains states initialization procedure. Currently
the power states that have been changed from the constraints API which were
applied before the initialization of the power domains are lost
3. Further clean-up the OMAP PM layer, use the generic frameworks instead (OPP,
PM QoS...)
Based on the pm-qos branch of the linux-omap git tree (3.2.0-rc4), cf. [2].
Tested on OMAP3 Beagleboard (ES2.x) with constraints on MPU, CORE, PER in
RETention and OFF modes.
[1] http://www.omappedia.org/wiki/Power_Management_Device_Latencies_Measurement
[2] git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap.git
v5:
. rebased on latest linux-omap [2]
. rework after Kevin's comments on the MLs
v4:
. split up the patches which remove the omap_pm_ code from the patch set.
Those patches are to be submitted later, on top of this patch set.
. latency numbers: provide the measurements setup and conditions in the code
comments, added the link to the details on wiki [1].
. improved kerneldoc
. split big functions into smaller ones, in order to improve the readability
v3: reworked the error return path and improved the kerneldoc
v2: reworked the OMAP specific cpuidle code to demote the initial C-state to
a valid C-state which fulfills the per-device constraints
v1: initial version
Jean Pihet (6):
OMAP2+: powerdomain: control power domains next state
OMAP2+: omap_hwmod: manage the wake-up latency constraints
OMAP: PM: register to the per-device PM QoS framework
OMAP3: cpuidle: next C-state decision depends on the PM QoS MPU and
CORE constraints
OMAP3: update cpuidle latency and threshold figures
OMAP3: powerdomain data: add wake-up latency figures
arch/arm/mach-omap2/cpuidle34xx.c | 107 +++++++-----
arch/arm/mach-omap2/omap_hwmod.c | 27 +++-
arch/arm/mach-omap2/pm.c | 71 ++++++++-
arch/arm/mach-omap2/pm.h | 17 ++-
arch/arm/mach-omap2/powerdomain.c | 245 ++++++++++++++++++++++++++
arch/arm/mach-omap2/powerdomain.h | 36 ++++-
arch/arm/mach-omap2/powerdomains3xxx_data.c | 83 +++++++++
arch/arm/plat-omap/include/plat/omap_hwmod.h | 2 +
8 files changed, 539 insertions(+), 49 deletions(-)
--
1.7.5.4
^ permalink raw reply
* Re: [PATCH UPDATED AGAIN 03/10] threadgroup: extend threadgroup_lock() to cover exit and exec
From: Frederic Weisbecker @ 2011-12-09 23:42 UTC (permalink / raw)
To: Tejun Heo
Cc: akpm, paul, lizf, linux-kernel, oleg, linux-pm, Linus Torvalds,
containers, kamezawa.hiroyu
In-Reply-To: <20111208205055.GB12108@google.com>
On Thu, Dec 08, 2011 at 12:50:55PM -0800, Tejun Heo wrote:
> (Note for Linus at the bottom)
>
> threadgroup_lock() protected only protected against new addition to
> the threadgroup, which was inherently somewhat incomplete and
> problematic for its only user cgroup. On-going migration could race
> against exec and exit leading to interesting problems - the symmetry
> between various attach methods, task exiting during method execution,
> ->exit() racing against attach methods, migrating task switching basic
> properties during exec and so on.
>
> This patch extends threadgroup_lock() such that it protects against
> all three threadgroup altering operations - fork, exit and exec. For
> exit, threadgroup_change_begin/end() calls are added to exit_signals
> around assertion of PF_EXITING. For exec, threadgroup_[un]lock() are
> updated to also grab and release cred_guard_mutex.
>
> With this change, threadgroup_lock() guarantees that the target
> threadgroup will remain stable - no new task will be added, no new
> PF_EXITING will be set and exec won't happen.
>
> The next patch will update cgroup so that it can take full advantage
> of this change.
>
> -v2: beefed up comment as suggested by Frederic.
>
> -v3: narrowed scope of protection in exit path as suggested by
> Frederic.
>
> Signed-off-by: Tejun Heo <tj@kernel.org>
> Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
> Acked-by: Li Zefan <lizf@cn.fujitsu.com>
> Cc: Oleg Nesterov <oleg@redhat.com>
> Cc: Andrew Morton <akpm@linux-foundation.org>
> Cc: Paul Menage <paul@paulmenage.org>
> Cc: Frederic Weisbecker <fweisbec@gmail.com>
> Cc: Linus Torvalds <torvalds@linux-foundation.org>
> ---
> Okay, narrowed exit path protection down to setting of PF_EXITING
> itself. ->exit() on dangling tasks is a bit weird but I don't think
> it's too bad. Frederic, are you okay with this version?
Yeah that new scheme that only protects PF_EXITING may look a bit
strange. But I think we are fine. With rcu list traversal, it should
be safe even if a group member is concurrently dropped from the list (in that
case all we check if its PF_EXITING then we give up). And we may
have a concurrent ->exit() but that should be fine too.
Thanks!
Acked-by: Frederic Weisbecker <fweisbec@gmail.com>
>
> Linus, if Frederic is okay with it, I'm gonna rebase the series on top
> of freezer changes in pm tree to avoid conflicts in cgroup_freezer,
> which sits between cgroup and freezer, both of which are going through
> non-trivial changes, push the branch to linux-next and put pending
> cgroup patches on top. Please scream if you're mighty unhappy with it
> or have a better idea.
>
> Thank you.
^ permalink raw reply
* [PATCH UPDATED AGAIN 03/10] threadgroup: extend threadgroup_lock() to cover exit and exec
From: Tejun Heo @ 2011-12-08 20:50 UTC (permalink / raw)
To: Frederic Weisbecker, Linus Torvalds
Cc: akpm, containers, lizf, linux-kernel, oleg, linux-pm, paul,
kamezawa.hiroyu
In-Reply-To: <20111207182214.GA7610@google.com>
(Note for Linus at the bottom)
threadgroup_lock() protected only protected against new addition to
the threadgroup, which was inherently somewhat incomplete and
problematic for its only user cgroup. On-going migration could race
against exec and exit leading to interesting problems - the symmetry
between various attach methods, task exiting during method execution,
->exit() racing against attach methods, migrating task switching basic
properties during exec and so on.
This patch extends threadgroup_lock() such that it protects against
all three threadgroup altering operations - fork, exit and exec. For
exit, threadgroup_change_begin/end() calls are added to exit_signals
around assertion of PF_EXITING. For exec, threadgroup_[un]lock() are
updated to also grab and release cred_guard_mutex.
With this change, threadgroup_lock() guarantees that the target
threadgroup will remain stable - no new task will be added, no new
PF_EXITING will be set and exec won't happen.
The next patch will update cgroup so that it can take full advantage
of this change.
-v2: beefed up comment as suggested by Frederic.
-v3: narrowed scope of protection in exit path as suggested by
Frederic.
Signed-off-by: Tejun Heo <tj@kernel.org>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: Li Zefan <lizf@cn.fujitsu.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Paul Menage <paul@paulmenage.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
---
Okay, narrowed exit path protection down to setting of PF_EXITING
itself. ->exit() on dangling tasks is a bit weird but I don't think
it's too bad. Frederic, are you okay with this version?
Linus, if Frederic is okay with it, I'm gonna rebase the series on top
of freezer changes in pm tree to avoid conflicts in cgroup_freezer,
which sits between cgroup and freezer, both of which are going through
non-trivial changes, push the branch to linux-next and put pending
cgroup patches on top. Please scream if you're mighty unhappy with it
or have a better idea.
Thank you.
include/linux/sched.h | 47 +++++++++++++++++++++++++++++++++++++++++------
kernel/signal.c | 10 ++++++++++
2 files changed, 51 insertions(+), 6 deletions(-)
Index: work/include/linux/sched.h
===================================================================
--- work.orig/include/linux/sched.h
+++ work/include/linux/sched.h
@@ -635,11 +635,13 @@ struct signal_struct {
#endif
#ifdef CONFIG_CGROUPS
/*
- * The group_rwsem prevents threads from forking with
- * CLONE_THREAD while held for writing. Use this for fork-sensitive
- * threadgroup-wide operations. It's taken for reading in fork.c in
- * copy_process().
- * Currently only needed write-side by cgroups.
+ * group_rwsem prevents new tasks from entering the threadgroup and
+ * member tasks from exiting,a more specifically, setting of
+ * PF_EXITING. fork and exit paths are protected with this rwsem
+ * using threadgroup_change_begin/end(). Users which require
+ * threadgroup to remain stable should use threadgroup_[un]lock()
+ * which also takes care of exec path. Currently, cgroup is the
+ * only user.
*/
struct rw_semaphore group_rwsem;
#endif
@@ -2374,7 +2376,6 @@ static inline void unlock_task_sighand(s
spin_unlock_irqrestore(&tsk->sighand->siglock, *flags);
}
-/* See the declaration of group_rwsem in signal_struct. */
#ifdef CONFIG_CGROUPS
static inline void threadgroup_change_begin(struct task_struct *tsk)
{
@@ -2384,13 +2385,47 @@ static inline void threadgroup_change_en
{
up_read(&tsk->signal->group_rwsem);
}
+
+/**
+ * threadgroup_lock - lock threadgroup
+ * @tsk: member task of the threadgroup to lock
+ *
+ * Lock the threadgroup @tsk belongs to. No new task is allowed to enter
+ * and member tasks aren't allowed to exit (as indicated by PF_EXITING) or
+ * perform exec. This is useful for cases where the threadgroup needs to
+ * stay stable across blockable operations.
+ *
+ * fork and exit paths explicitly call threadgroup_change_{begin|end}() for
+ * synchronization. While held, no new task will be added to threadgroup
+ * and no existing live task will have its PF_EXITING set.
+ *
+ * During exec, a task goes and puts its thread group through unusual
+ * changes. After de-threading, exclusive access is assumed to resources
+ * which are usually shared by tasks in the same group - e.g. sighand may
+ * be replaced with a new one. Also, the exec'ing task takes over group
+ * leader role including its pid. Exclude these changes while locked by
+ * grabbing cred_guard_mutex which is used to synchronize exec path.
+ */
static inline void threadgroup_lock(struct task_struct *tsk)
{
+ /*
+ * exec uses exit for de-threading nesting group_rwsem inside
+ * cred_guard_mutex. Grab cred_guard_mutex first.
+ */
+ mutex_lock(&tsk->signal->cred_guard_mutex);
down_write(&tsk->signal->group_rwsem);
}
+
+/**
+ * threadgroup_unlock - unlock threadgroup
+ * @tsk: member task of the threadgroup to unlock
+ *
+ * Reverse threadgroup_lock().
+ */
static inline void threadgroup_unlock(struct task_struct *tsk)
{
up_write(&tsk->signal->group_rwsem);
+ mutex_unlock(&tsk->signal->cred_guard_mutex);
}
#else
static inline void threadgroup_change_begin(struct task_struct *tsk) {}
Index: work/kernel/signal.c
===================================================================
--- work.orig/kernel/signal.c
+++ work/kernel/signal.c
@@ -2359,8 +2359,15 @@ void exit_signals(struct task_struct *ts
int group_stop = 0;
sigset_t unblocked;
+ /*
+ * @tsk is about to have PF_EXITING set - lock out users which
+ * expect stable threadgroup.
+ */
+ threadgroup_change_begin(tsk);
+
if (thread_group_empty(tsk) || signal_group_exit(tsk->signal)) {
tsk->flags |= PF_EXITING;
+ threadgroup_change_end(tsk);
return;
}
@@ -2370,6 +2377,9 @@ void exit_signals(struct task_struct *ts
* see wants_signal(), do_signal_stop().
*/
tsk->flags |= PF_EXITING;
+
+ threadgroup_change_end(tsk);
+
if (!signal_pending(tsk))
goto out;
^ permalink raw reply
* Re: [PATCH UPDATED 03/10] threadgroup: extend threadgroup_lock() to cover exit and exec
From: Tejun Heo @ 2011-12-07 18:22 UTC (permalink / raw)
To: Frederic Weisbecker
Cc: akpm, paul, lizf, linux-kernel, oleg, linux-pm, Linus Torvalds,
containers, kamezawa.hiroyu
In-Reply-To: <20111207153046.GC13252@somewhere.redhat.com>
Hello, Frederic.
On Wed, Dec 07, 2011 at 04:30:54PM +0100, Frederic Weisbecker wrote:
> I don't understand what you mean. On your patches, you only process tasks
> that don't have PF_EXITING, ie: you don't include these in the flex array
> on cgroup_attach_proc(). So that still applies in my proposal.
>
> From the exit path we would have:
>
> exit_signal() {
> lock_threadgroup_change(task);
> task->flags |= PF_EXITING;
> lock_threadgroup(task);
> }
>
> exit all the rest: mm, etc...
>
> Then from cgroup_attach_proc():
>
> lock_threadgroup(task);
> for_each_thread(task) {
> if (!(task->flags & PF_EXITING))
> include in flex array
> }
>
> Am I forgetting something?
The point I was trying to make was that doing the above would make
->exit() called on dangling task of a threadgroup in rare cases.
ie. With the proposed change, after a threadgroup migration, all tasks
in the threadgroup is in the new cgroup. No method will be called on
the old cgroup for any of the member task. With the above change,
process migration would leave out dying tasks and ->exit() can be
called with the old cgroup.
Hmm... that said, it probably doesn't matter all that much either way.
I'll update the patchset and repost.
Thanks.
--
tejun
^ permalink raw reply
* Re: [PATCH UPDATED 03/10] threadgroup: extend threadgroup_lock() to cover exit and exec
From: Frederic Weisbecker @ 2011-12-07 15:30 UTC (permalink / raw)
To: Tejun Heo
Cc: akpm, paul, lizf, linux-kernel, oleg, linux-pm, Linus Torvalds,
containers, kamezawa.hiroyu
In-Reply-To: <20111205184315.GJ627@google.com>
On Mon, Dec 05, 2011 at 10:43:15AM -0800, Tejun Heo wrote:
> Hello, Frederic.
>
> On Fri, Dec 02, 2011 at 05:28:03PM +0100, Frederic Weisbecker wrote:
> > But I don't think it's very useful to protect against irq_exit_thread(),
> > what happens there is purely of internal irq interest.
> >
> > Then right after, PF_EXITING is set before any interesting change.
> > Isn't it possible to simply lock this flag setting? IIRC, as soon
> > as the PF_EXITING flag is set, you ignore the task for attachment.
>
> I think that's technically possible but it does introduce another
> class of tasks - the dying ones. e.g. If a task has PF_EXITING set
> and the containing process is migrating, we'll have to migrate all
> tasks but the dying one and cgroup ->exit callbacks can be called on
> the lonely task after the migration is complete. It's kinda messy and
> if someone makes a wrong assumption there, the bug is gonna be even
> more difficult to reproduce / track down than now. Yes, smaller scope
> locking is nicer but I would like to avoid api weirdities like that.
I don't understand what you mean. On your patches, you only process tasks
that don't have PF_EXITING, ie: you don't include these in the flex array
on cgroup_attach_proc(). So that still applies in my proposal.
>From the exit path we would have:
exit_signal() {
lock_threadgroup_change(task);
task->flags |= PF_EXITING;
lock_threadgroup(task);
}
exit all the rest: mm, etc...
Then from cgroup_attach_proc():
lock_threadgroup(task);
for_each_thread(task) {
if (!(task->flags & PF_EXITING))
include in flex array
}
Am I forgetting something?
^ permalink raw reply
* Re: [PATCH UPDATED 03/10] threadgroup: extend threadgroup_lock() to cover exit and exec
From: Tejun Heo @ 2011-12-05 18:43 UTC (permalink / raw)
To: Frederic Weisbecker
Cc: akpm, paul, lizf, linux-kernel, oleg, linux-pm, Linus Torvalds,
containers, kamezawa.hiroyu
In-Reply-To: <20111202162753.GA19752@somewhere.redhat.com>
Hello, Frederic.
On Fri, Dec 02, 2011 at 05:28:03PM +0100, Frederic Weisbecker wrote:
> But I don't think it's very useful to protect against irq_exit_thread(),
> what happens there is purely of internal irq interest.
>
> Then right after, PF_EXITING is set before any interesting change.
> Isn't it possible to simply lock this flag setting? IIRC, as soon
> as the PF_EXITING flag is set, you ignore the task for attachment.
I think that's technically possible but it does introduce another
class of tasks - the dying ones. e.g. If a task has PF_EXITING set
and the containing process is migrating, we'll have to migrate all
tasks but the dying one and cgroup ->exit callbacks can be called on
the lonely task after the migration is complete. It's kinda messy and
if someone makes a wrong assumption there, the bug is gonna be even
more difficult to reproduce / track down than now. Yes, smaller scope
locking is nicer but I would like to avoid api weirdities like that.
Thanks.
--
tejun
^ permalink raw reply
* Re: [PATCH UPDATED 03/10] threadgroup: extend threadgroup_lock() to cover exit and exec
From: Frederic Weisbecker @ 2011-12-02 16:28 UTC (permalink / raw)
To: Tejun Heo
Cc: akpm, paul, lizf, linux-kernel, oleg, linux-pm, Linus Torvalds,
containers, kamezawa.hiroyu
In-Reply-To: <20111127193001.GC4266@google.com>
On Sun, Nov 27, 2011 at 11:30:01AM -0800, Tejun Heo wrote:
> Hello, Frederic.
>
> On Fri, Nov 25, 2011 at 03:01:39PM +0100, Frederic Weisbecker wrote:
> > > + /*
> > > + * @tsk's threadgroup is going through changes - lock out users
> > > + * which expect stable threadgroup. Do this before actually
> > > + * starting tearing down @tsk so that locked threadgroup has either
> > > + * alive or dead tasks, not something inbetween.
> > > + */
> > > + threadgroup_change_begin(tsk);
> > > +
> >
> > I still wonder why there is a so big coverage of this lock. I mean
> > why is it called right before exit_irq_thread() and released so late.
> > All we want is to lock cgroup_exit() I think, after which tasks can't be
> > migrated.
>
> That way, cgroup plugins never see tasks in the process of
> deconstruction. Its ->mm, ->sighand, ->signal and so on either don't
> exist or continue to exist across all cgroup callbacks. It's simpler
> and safer (especially as bugs in this area would only be visible when
> migration and exit race), and if we're gonna synchronize exit path at
> all, there isn't anything to lose by excluding the whole thing.
Fine.
But I don't think it's very useful to protect against irq_exit_thread(),
what happens there is purely of internal irq interest.
Then right after, PF_EXITING is set before any interesting change.
Isn't it possible to simply lock this flag setting? IIRC, as soon
as the PF_EXITING flag is set, you ignore the task for attachment.
^ permalink raw reply
* Re: [PATCH UPDATED 03/10] threadgroup: extend threadgroup_lock() to cover exit and exec
From: Tejun Heo @ 2011-12-01 19:29 UTC (permalink / raw)
To: Linus Torvalds
Cc: akpm, fweisbec, containers, lizf, linux-kernel, oleg, linux-pm,
paul, kamezawa.hiroyu
In-Reply-To: <20111127212558.GE4266@google.com>
Hello again, Linus.
On Sun, Nov 27, 2011 at 01:25:58PM -0800, Tejun Heo wrote:
> The problem is that cred_guard_mutex uses _interruptible/_killable
> operations and rwsem doesn't have them, so cred_guard_mutex can't be
> easily replaced with write-locking group_rwsem.
>
> If the two locks can't be merged, under the proposed scheme, while not
> exactly pretty, both fork/exit and exec paths go through single
> locking and only the ones which want stable threadgroup need to grab
> both locks, so IMHO it is at least reasonable.
>
> Any better ideas?
I agree that the proposed solution is rather ugly but stable
thread-group is a valid mechanism to have and cgroup can benefit a lot
from it. I'd be happy to revamp the implementation if anyone can come
up with a better way and can add big fat comment stating that. Until
something better comes up, would it be okay to stick with this
implementation?
Thanks.
--
tejun
^ permalink raw reply
* Re: 3.2-rc2+: Reported regressions from 3.0 and 3.1
From: Konrad Rzeszutek Wilk @ 2011-12-01 14:35 UTC (permalink / raw)
To: Borislav Petkov
Cc: Linux SCSI List, Florian Mickler, Network Development,
Linux PM List, x86, Linux Wireless List,
Linux Kernel Mailing List, DRI, Linux ACPI, mingo, hpa, tglx,
Kernel Testers List, Linus Torvalds, Andrew Morton,
Maciej Rutecki
In-Reply-To: <20111201113919.GC31552@aftab>
> >From what I can see, you get the following callchain:
>
> start_kernel
> |-> setup_arch
> |-> x86_init.oem.arch_setup = xen_arch_setup
> ....
> |-> check_bugs
> |-> identify_boot_cpu
> |-> identify_cpu
> |-> select_idle_routine
>
>
> so xen_arch_setup will set pm_idle and select_idle_routine will honour
> it. I'm being told this is run either in the dom0 or the paravirt guest
> and if so, I don't see any issue with this for baremetal. So you can
> have my ACK provided this is tested on baremetal too.
Tested on baremetal and there were no abnormalities. Thanks!
^ permalink raw reply
* Re: 3.2-rc2+: Reported regressions from 3.0 and 3.1
From: Konrad Rzeszutek Wilk @ 2011-11-30 17:59 UTC (permalink / raw)
To: Borislav Petkov
Cc: Linux SCSI List, Florian Mickler, Network Development,
Linux PM List, x86, Linux Wireless List,
Linux Kernel Mailing List, DRI, Linux ACPI, mingo, hpa, tglx,
Kernel Testers List, Linus Torvalds, Andrew Morton,
Maciej Rutecki
In-Reply-To: <20111129183428.GE8200@aftab>
On Tue, Nov 29, 2011 at 07:34:28PM +0100, Borislav Petkov wrote:
> On Tue, Nov 29, 2011 at 01:04:14PM -0500, Konrad Rzeszutek Wilk wrote:
> > This patch:
Borislav,
Thanks for your review comments. How does this patch look? I believe
I touched upon all of the things you mentioned.
>From eb6dbd80078312c428dde69e9313606b7513a2e6 Mon Sep 17 00:00:00 2001
From: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Date: Mon, 21 Nov 2011 18:02:02 -0500
Subject: [PATCH] xen/pm_idle: Make pm_idle be default_idle under Xen.
This patch:
commit d91ee5863b71e8c90eaf6035bff3078a85e2e7b5
Author: Len Brown <len.brown@intel.com>
Date: Fri Apr 1 18:28:35 2011 -0400
cpuidle: replace xen access to x86 pm_idle and default_idle
..scribble on pm_idle and access default_idle,
have it simply disable_cpuidle() so acpi_idle will not load and
architecture default HLT will be used.
idea was to have one call - disable_cpuidle() which would make
pm_idle not be molested by other code. It disallows cpuidle_idle_call
to be set to pm_idle (which is excellent). But in the select_idle_routine()
and idle_setup(), the pm_idle can still be set to either:
amd_e400_idle, mwait_idle or default_idle. This depends on some
CPU flags (MWAIT) and in AMD case on the type of CPU.
In case of mwait_idle we can hit some instances where the hypervisor
(Amazon EC2 specifically) sets the MWAIT and we get:
Brought up 2 CPUs
invalid opcode: 0000 [#1] SMP
CPU 1
Modules linked in:
Pid: 0, comm: swapper Not tainted 3.1.0-0.rc6.git0.3.fc16.x86_64 #1
RIP: e030:[<ffffffff81015d1d>] [<ffffffff81015d1d>] mwait_idle+0x6f/0xb4
RSP: e02b:ffff8801d28ddf10 EFLAGS: 00010082
RAX: ffff8801d28dc010 RBX: ffff8801d28ddfd8 RCX: 0000000000000000
RDX: 0000000000000000 RSI: 0000000000000001 RDI: 0000000000000001
RBP: ffff8801d28ddf10 R08: 0000000000000000 R09: 0000000000000001
R10: 0000000000000001 R11: ffff8801d28ddfd8 R12: ffffffff81b590d0
R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000
FS: 0000000000000000(0000) GS:ffff8801dff81000(0000) knlGS:0000000000000000
CS: e033 DS: 002b ES: 002b CR0: 000000008005003b
CR2: 0000000000000000 CR3: 0000000001a05000 CR4: 0000000000002660
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000000
Process swapper (pid: 0, threadinfo ffff8801d28dc000, task ffff8801d28cae60)
Stack:
ffff8801d28ddf40 ffffffff8100e2ed ffff8801dff8e390 c136dfe72feab515
0000000000000000 0000000000000000 ffff8801d28ddf50 ffffffff8149ee78
0000000000000000 0000000000000000 0000000000000000 0000000000000000
Call Trace:
[<ffffffff8100e2ed>] cpu_idle+0xae/0xe8
[<ffffffff8149ee78>] cpu_bringup_and_idle+0xe/0x10
RIP [<ffffffff81015d1d>] mwait_idle+0x6f/0xb4
RSP <ffff8801d28ddf10>
In case of amd_e400_idle we don't get so spectacular crashes, but
we do end up making an MSR which is trapped in the hypervisor,
and then follow it up with a yield hypercall. Meaning we end up
going to hypervisor twice instead of just once.
The previous behavior before v3.0 was that pm_idle was set
to default_idle irregardless of select_idle_routine/idle_setup.
We want to do that, but only for one specific case: Xen.
This patch does that.
Fixes RH BZ #739499 and Ubuntu #881076
Reported-by: Stefan Bader <stefan.bader@canonical.com>
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
---
arch/x86/include/asm/system.h | 1 +
arch/x86/kernel/process.c | 8 ++++++++
arch/x86/xen/setup.c | 2 +-
3 files changed, 10 insertions(+), 1 deletions(-)
diff --git a/arch/x86/include/asm/system.h b/arch/x86/include/asm/system.h
index c2ff2a1..2d2f01c 100644
--- a/arch/x86/include/asm/system.h
+++ b/arch/x86/include/asm/system.h
@@ -401,6 +401,7 @@ extern unsigned long arch_align_stack(unsigned long sp);
extern void free_init_pages(char *what, unsigned long begin, unsigned long end);
void default_idle(void);
+bool set_pm_idle_to_default(void);
void stop_this_cpu(void *dummy);
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
index 1f7f8c8..31f47ba 100644
--- a/arch/x86/kernel/process.c
+++ b/arch/x86/kernel/process.c
@@ -404,6 +404,14 @@ void default_idle(void)
EXPORT_SYMBOL(default_idle);
#endif
+bool set_pm_idle_to_default(void)
+{
+ bool ret = !!pm_idle;
+
+ pm_idle = default_idle;
+
+ return ret;
+}
void stop_this_cpu(void *dummy)
{
local_irq_disable();
diff --git a/arch/x86/xen/setup.c b/arch/x86/xen/setup.c
index 46d6d21..79dfb57 100644
--- a/arch/x86/xen/setup.c
+++ b/arch/x86/xen/setup.c
@@ -448,6 +448,6 @@ void __init xen_arch_setup(void)
#endif
disable_cpuidle();
boot_option_idle_override = IDLE_HALT;
-
+ WARN_ON(set_pm_idle_to_default());
fiddle_vdso();
}
--
1.7.7.3
^ permalink raw reply related
* [RFC PATCH v3 4/4] cpuidle: (POWER) Handle power_save=off
From: Deepthi Dharwar @ 2011-11-30 12:47 UTC (permalink / raw)
To: benh, len.brown; +Cc: linuxppc-dev, linux-pm, linux-kernel, linux-pm
In-Reply-To: <20111130124608.972.87712.stgit@deepthi-ThinkPad-T420>
This patch makes pseries_idle_driver not to be registered when
power_save=off kernel boot option is specified. The
cpuidle_disable variable used here is similar to
its usage on x86. If cpuidle_disable is set then
sysfs entries for cpuidle framework are not created
and the required drivers are not loaded.
Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
Signed-off-by: Trinabh Gupta <g.trinabh@gmail.com>
Signed-off-by: Arun R Bharadwaj <arun.r.bharadwaj@gmail.com>
---
arch/powerpc/include/asm/processor.h | 1 +
arch/powerpc/platforms/pseries/processor_idle.c | 3 +++
2 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h
index 811b7e7..b585bff 100644
--- a/arch/powerpc/include/asm/processor.h
+++ b/arch/powerpc/include/asm/processor.h
@@ -382,6 +382,7 @@ static inline unsigned long get_clean_sp(struct pt_regs *regs, int is_32)
}
#endif
+extern unsigned long cpuidle_disable;
enum idle_boot_override {IDLE_NO_OVERRIDE = 0, IDLE_POWERSAVE_OFF};
#endif /* __KERNEL__ */
diff --git a/arch/powerpc/platforms/pseries/processor_idle.c b/arch/powerpc/platforms/pseries/processor_idle.c
index 352b78a..4f59af0 100644
--- a/arch/powerpc/platforms/pseries/processor_idle.c
+++ b/arch/powerpc/platforms/pseries/processor_idle.c
@@ -269,6 +269,9 @@ static int pseries_idle_probe(void)
if (!firmware_has_feature(FW_FEATURE_SPLPAR))
return -ENODEV;
+ if (cpuidle_disable != IDLE_NO_OVERRIDE)
+ return -ENODEV;
+
if (max_idle_state == 0) {
printk(KERN_DEBUG "pseries processor idle disabled.\n");
return -EPERM;
^ permalink raw reply related
* [RFC PATCH v3 3/4] cpuidle: (POWER) Enable cpuidle and directly call cpuidle_idle_call() for pSeries
From: Deepthi Dharwar @ 2011-11-30 12:46 UTC (permalink / raw)
To: benh, len.brown; +Cc: linuxppc-dev, linux-pm, linux-kernel, linux-pm
In-Reply-To: <20111130124608.972.87712.stgit@deepthi-ThinkPad-T420>
This patch enables cpuidle for pSeries and pSeries_idle is
directly called from the idle loop. As a result of pSeries_idle, cpuidle
driver registered with cpuidle subsystem comes into action. On
failure of loading of the driver or cpuidle framework default idle
is executed as part of the function. This patch
also removes the routines pseries_shared_idle_sleep and
pseries_dedicated_idle_sleep as they are now implemented as part of
pseries_idle cpuidle driver.
Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
Signed-off-by: Trinabh Gupta <g.trinabh@gmail.com>
Signed-off-by: Arun R Bharadwaj <arun.r.bharadwaj@gmail.com>
---
arch/powerpc/platforms/Kconfig | 6 ++
arch/powerpc/platforms/pseries/setup.c | 101 +++++---------------------------
include/linux/cpuidle.h | 2 -
3 files changed, 23 insertions(+), 86 deletions(-)
diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig
index 3fe6d92..31e1ade 100644
--- a/arch/powerpc/platforms/Kconfig
+++ b/arch/powerpc/platforms/Kconfig
@@ -211,6 +211,12 @@ config PPC_PASEMI_CPUFREQ
endmenu
+menu "CPUIdle driver"
+
+source "drivers/cpuidle/Kconfig"
+
+endmenu
+
config PPC601_SYNC_FIX
bool "Workarounds for PPC601 bugs"
depends on 6xx && (PPC_PREP || PPC_PMAC)
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c
index 9c6716a..f19fc52 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -39,6 +39,7 @@
#include <linux/irq.h>
#include <linux/seq_file.h>
#include <linux/root_dev.h>
+#include <linux/cpuidle.h>
#include <asm/mmu.h>
#include <asm/processor.h>
@@ -74,9 +75,6 @@ EXPORT_SYMBOL(CMO_PageSize);
int fwnmi_active; /* TRUE if an FWNMI handler is present */
-static void pseries_shared_idle_sleep(void);
-static void pseries_dedicated_idle_sleep(void);
-
static struct device_node *pSeries_mpic_node;
static void pSeries_show_cpuinfo(struct seq_file *m)
@@ -352,6 +350,21 @@ static int alloc_dispatch_log_kmem_cache(void)
}
early_initcall(alloc_dispatch_log_kmem_cache);
+static void pSeries_idle(void)
+{
+ /* This would call on the cpuidle framework, and the back-end pseries
+ * driver to go to idle states
+ */
+ if (cpuidle_idle_call()) {
+ /* On error, execute default handler
+ * to go into low thread priority and possibly
+ * low power mode.
+ */
+ HMT_low();
+ HMT_very_low();
+ }
+}
+
static void __init pSeries_setup_arch(void)
{
/* Discover PIC type and setup ppc_md accordingly */
@@ -374,18 +387,9 @@ static void __init pSeries_setup_arch(void)
pSeries_nvram_init();
- /* Choose an idle loop */
if (firmware_has_feature(FW_FEATURE_SPLPAR)) {
vpa_init(boot_cpuid);
- if (get_lppaca()->shared_proc) {
- printk(KERN_DEBUG "Using shared processor idle loop\n");
- ppc_md.power_save = pseries_shared_idle_sleep;
- } else {
- printk(KERN_DEBUG "Using dedicated idle loop\n");
- ppc_md.power_save = pseries_dedicated_idle_sleep;
- }
- } else {
- printk(KERN_DEBUG "Using default idle loop\n");
+ ppc_md.power_save = pSeries_idle;
}
if (firmware_has_feature(FW_FEATURE_LPAR))
@@ -586,77 +590,6 @@ static int __init pSeries_probe(void)
return 1;
}
-static void pseries_dedicated_idle_sleep(void)
-{
- unsigned int cpu = smp_processor_id();
- unsigned long start_snooze;
- unsigned long in_purr, out_purr;
- long snooze = __get_cpu_var(smt_snooze_delay);
-
- /*
- * Indicate to the HV that we are idle. Now would be
- * a good time to find other work to dispatch.
- */
- get_lppaca()->idle = 1;
- get_lppaca()->donate_dedicated_cpu = 1;
- in_purr = mfspr(SPRN_PURR);
-
- /*
- * We come in with interrupts disabled, and need_resched()
- * has been checked recently. If we should poll for a little
- * while, do so.
- */
- if (snooze) {
- start_snooze = get_tb() + snooze * tb_ticks_per_usec;
- local_irq_enable();
- set_thread_flag(TIF_POLLING_NRFLAG);
-
- while ((snooze < 0) || (get_tb() < start_snooze)) {
- if (need_resched() || cpu_is_offline(cpu))
- goto out;
- ppc64_runlatch_off();
- HMT_low();
- HMT_very_low();
- }
-
- HMT_medium();
- clear_thread_flag(TIF_POLLING_NRFLAG);
- smp_mb();
- local_irq_disable();
- if (need_resched() || cpu_is_offline(cpu))
- goto out;
- }
-
- cede_processor();
-
-out:
- HMT_medium();
- out_purr = mfspr(SPRN_PURR);
- get_lppaca()->wait_state_cycles += out_purr - in_purr;
- get_lppaca()->donate_dedicated_cpu = 0;
- get_lppaca()->idle = 0;
-}
-
-static void pseries_shared_idle_sleep(void)
-{
- /*
- * Indicate to the HV that we are idle. Now would be
- * a good time to find other work to dispatch.
- */
- get_lppaca()->idle = 1;
-
- /*
- * Yield the processor to the hypervisor. We return if
- * an external interrupt occurs (which are driven prior
- * to returning here) or if a prod occurs from another
- * processor. When returning here, external interrupts
- * are enabled.
- */
- cede_processor();
-
- get_lppaca()->idle = 0;
-}
-
static int pSeries_pci_probe_mode(struct pci_bus *bus)
{
if (firmware_has_feature(FW_FEATURE_LPAR))
diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
index 7408af8..23f81de 100644
--- a/include/linux/cpuidle.h
+++ b/include/linux/cpuidle.h
@@ -130,7 +130,6 @@ struct cpuidle_driver {
#ifdef CONFIG_CPU_IDLE
extern void disable_cpuidle(void);
extern int cpuidle_idle_call(void);
-
extern int cpuidle_register_driver(struct cpuidle_driver *drv);
struct cpuidle_driver *cpuidle_get_driver(void);
extern void cpuidle_unregister_driver(struct cpuidle_driver *drv);
@@ -145,7 +144,6 @@ extern void cpuidle_disable_device(struct cpuidle_device *dev);
#else
static inline void disable_cpuidle(void) { }
static inline int cpuidle_idle_call(void) { return -ENODEV; }
-
static inline int cpuidle_register_driver(struct cpuidle_driver *drv)
{return -ENODEV; }
static inline struct cpuidle_driver *cpuidle_get_driver(void) {return NULL; }
^ permalink raw reply related
* [RFC PATCH v3 2/4] cpuidle: (POWER) cpuidle driver for pSeries
From: Deepthi Dharwar @ 2011-11-30 12:46 UTC (permalink / raw)
To: benh, len.brown; +Cc: linuxppc-dev, linux-pm, linux-kernel, linux-pm
In-Reply-To: <20111130124608.972.87712.stgit@deepthi-ThinkPad-T420>
This patch implements a back-end cpuidle driver for pSeries
based on pseries_dedicated_idle_loop and pseries_shared_idle_loop
routines. The driver is built only if CONFIG_CPU_IDLE is set. This
cpuidle driver uses global registration of idle states and
not per-cpu.
Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
Signed-off-by: Trinabh Gupta <g.trinabh@gmail.com>
Signed-off-by: Arun R Bharadwaj <arun.r.bharadwaj@gmail.com>
---
arch/powerpc/include/asm/system.h | 8 +
arch/powerpc/kernel/sysfs.c | 2
arch/powerpc/platforms/pseries/Kconfig | 9 +
arch/powerpc/platforms/pseries/Makefile | 1
arch/powerpc/platforms/pseries/processor_idle.c | 326 +++++++++++++++++++++++
arch/powerpc/platforms/pseries/pseries.h | 3
arch/powerpc/platforms/pseries/setup.c | 3
arch/powerpc/platforms/pseries/smp.c | 1
8 files changed, 350 insertions(+), 3 deletions(-)
create mode 100644 arch/powerpc/platforms/pseries/processor_idle.c
diff --git a/arch/powerpc/include/asm/system.h b/arch/powerpc/include/asm/system.h
index ff66680..f56a0a7 100644
--- a/arch/powerpc/include/asm/system.h
+++ b/arch/powerpc/include/asm/system.h
@@ -223,6 +223,14 @@ extern void *zalloc_maybe_bootmem(size_t size, gfp_t mask);
extern int powersave_nap; /* set if nap mode can be used in idle loop */
void cpu_idle_wait(void);
+#ifdef CONFIG_PSERIES_IDLE
+extern void update_smt_snooze_delay(int snooze);
+extern int pseries_notify_cpuidle_add_cpu(int cpu);
+#else
+static inline void update_smt_snooze_delay(int snooze) {}
+static inline int pseries_notify_cpuidle_add_cpu(int cpu) { return 0; }
+#endif
+
/*
* Atomic exchange
*
diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c
index ce035c1..ebe5d78 100644
--- a/arch/powerpc/kernel/sysfs.c
+++ b/arch/powerpc/kernel/sysfs.c
@@ -18,6 +18,7 @@
#include <asm/machdep.h>
#include <asm/smp.h>
#include <asm/pmc.h>
+#include <asm/system.h>
#include "cacheinfo.h"
@@ -51,6 +52,7 @@ static ssize_t store_smt_snooze_delay(struct sys_device *dev,
return -EINVAL;
per_cpu(smt_snooze_delay, cpu->sysdev.id) = snooze;
+ update_smt_snooze_delay(snooze);
return count;
}
diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
index c81f6bb..ae7b6d4 100644
--- a/arch/powerpc/platforms/pseries/Kconfig
+++ b/arch/powerpc/platforms/pseries/Kconfig
@@ -120,3 +120,12 @@ config DTL
which are accessible through a debugfs file.
Say N if you are unsure.
+
+config PSERIES_IDLE
+ tristate "Cpuidle driver for pSeries platforms"
+ depends on CPU_IDLE
+ depends on PPC_PSERIES
+ default y
+ help
+ Select this option to enable processor idle state management
+ through cpuidle subsystem.
diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile
index 3556e40..236db46 100644
--- a/arch/powerpc/platforms/pseries/Makefile
+++ b/arch/powerpc/platforms/pseries/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_PHYP_DUMP) += phyp_dump.o
obj-$(CONFIG_CMM) += cmm.o
obj-$(CONFIG_DTL) += dtl.o
obj-$(CONFIG_IO_EVENT_IRQ) += io_event_irq.o
+obj-$(CONFIG_PSERIES_IDLE) += processor_idle.o
ifeq ($(CONFIG_PPC_PSERIES),y)
obj-$(CONFIG_SUSPEND) += suspend.o
diff --git a/arch/powerpc/platforms/pseries/processor_idle.c b/arch/powerpc/platforms/pseries/processor_idle.c
new file mode 100644
index 0000000..352b78a
--- /dev/null
+++ b/arch/powerpc/platforms/pseries/processor_idle.c
@@ -0,0 +1,326 @@
+/*
+ * processor_idle - idle state cpuidle driver.
+ * Adapted from drivers/idle/intel_idle.c and
+ * drivers/acpi/processor_idle.c
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <linux/cpuidle.h>
+#include <linux/cpu.h>
+
+#include <asm/paca.h>
+#include <asm/reg.h>
+#include <asm/system.h>
+#include <asm/machdep.h>
+#include <asm/firmware.h>
+
+#include "plpar_wrappers.h"
+#include "pseries.h"
+
+struct cpuidle_driver pseries_idle_driver = {
+ .name = "pseries_idle",
+ .owner = THIS_MODULE,
+};
+
+#define MAX_IDLE_STATE_COUNT 2
+
+static int max_idle_state = MAX_IDLE_STATE_COUNT - 1;
+static struct cpuidle_device __percpu *pseries_cpuidle_devices;
+static struct cpuidle_state *cpuidle_state_table;
+
+void update_smt_snooze_delay(int snooze)
+{
+ struct cpuidle_driver *drv = cpuidle_get_driver();
+ if (drv)
+ drv->states[0].target_residency = snooze;
+}
+
+static inline void idle_loop_prolog(unsigned long *in_purr, ktime_t *kt_before)
+{
+
+ *kt_before = ktime_get_real();
+ *in_purr = mfspr(SPRN_PURR);
+ /*
+ * Indicate to the HV that we are idle. Now would be
+ * a good time to find other work to dispatch.
+ */
+ get_lppaca()->idle = 1;
+}
+
+static inline s64 idle_loop_epilog(unsigned long in_purr, ktime_t kt_before)
+{
+ get_lppaca()->wait_state_cycles += mfspr(SPRN_PURR) - in_purr;
+ get_lppaca()->idle = 0;
+
+ return ktime_to_us(ktime_sub(ktime_get_real(), kt_before));
+}
+
+static int snooze_loop(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv,
+ int index)
+{
+ unsigned long in_purr;
+ ktime_t kt_before;
+ unsigned long start_snooze;
+ long snooze = drv->states[0].target_residency;
+
+ idle_loop_prolog(&in_purr, &kt_before);
+
+ if (snooze) {
+ start_snooze = get_tb() + snooze * tb_ticks_per_usec;
+ local_irq_enable();
+ set_thread_flag(TIF_POLLING_NRFLAG);
+
+ while ((snooze < 0) || (get_tb() < start_snooze)) {
+ if (need_resched() || cpu_is_offline(dev->cpu))
+ goto out;
+ ppc64_runlatch_off();
+ HMT_low();
+ HMT_very_low();
+ }
+
+ HMT_medium();
+ clear_thread_flag(TIF_POLLING_NRFLAG);
+ smp_mb();
+ local_irq_disable();
+ }
+
+out:
+ HMT_medium();
+ dev->last_residency =
+ (int)idle_loop_epilog(in_purr, kt_before);
+ return index;
+}
+
+static int dedicated_cede_loop(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv,
+ int index)
+{
+ unsigned long in_purr;
+ ktime_t kt_before;
+
+ idle_loop_prolog(&in_purr, &kt_before);
+ get_lppaca()->donate_dedicated_cpu = 1;
+
+ ppc64_runlatch_off();
+ HMT_medium();
+ cede_processor();
+
+ get_lppaca()->donate_dedicated_cpu = 0;
+ dev->last_residency =
+ (int)idle_loop_epilog(in_purr, kt_before);
+ return index;
+}
+
+static int shared_cede_loop(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv,
+ int index)
+{
+ unsigned long in_purr;
+ ktime_t kt_before;
+
+ idle_loop_prolog(&in_purr, &kt_before);
+
+ /*
+ * Yield the processor to the hypervisor. We return if
+ * an external interrupt occurs (which are driven prior
+ * to returning here) or if a prod occurs from another
+ * processor. When returning here, external interrupts
+ * are enabled.
+ */
+ cede_processor();
+
+ dev->last_residency =
+ (int)idle_loop_epilog(in_purr, kt_before);
+ return index;
+}
+
+/*
+ * States for dedicated partition case.
+ */
+static struct cpuidle_state dedicated_states[MAX_IDLE_STATE_COUNT] = {
+ { /* Snooze */
+ .name = "snooze",
+ .desc = "snooze",
+ .flags = CPUIDLE_FLAG_TIME_VALID,
+ .exit_latency = 0,
+ .target_residency = 0,
+ .enter = &snooze_loop },
+ { /* CEDE */
+ .name = "CEDE",
+ .desc = "CEDE",
+ .flags = CPUIDLE_FLAG_TIME_VALID,
+ .exit_latency = 1,
+ .target_residency = 10,
+ .enter = &dedicated_cede_loop },
+};
+
+/*
+ * States for shared partition case.
+ */
+static struct cpuidle_state shared_states[MAX_IDLE_STATE_COUNT] = {
+ { /* Shared Cede */
+ .name = "Shared Cede",
+ .desc = "Shared Cede",
+ .flags = CPUIDLE_FLAG_TIME_VALID,
+ .exit_latency = 0,
+ .target_residency = 0,
+ .enter = &shared_cede_loop },
+};
+
+int pseries_notify_cpuidle_add_cpu(int cpu)
+{
+ struct cpuidle_device *dev =
+ per_cpu_ptr(pseries_cpuidle_devices, cpu);
+ if (dev && cpuidle_get_driver()) {
+ cpuidle_disable_device(dev);
+ cpuidle_enable_device(dev);
+ }
+ return 0;
+}
+
+/*
+ * pseries_cpuidle_driver_init()
+ */
+static int pseries_cpuidle_driver_init(void)
+{
+ int idle_state;
+ struct cpuidle_driver *drv = &pseries_idle_driver;
+
+ drv->state_count = 0;
+
+ for (idle_state = 0; idle_state < MAX_IDLE_STATE_COUNT; ++idle_state) {
+
+ if (idle_state > max_idle_state)
+ break;
+
+ /* is the state not enabled? */
+ if (cpuidle_state_table[idle_state].enter == NULL)
+ continue;
+
+ drv->states[drv->state_count] = /* structure copy */
+ cpuidle_state_table[idle_state];
+
+ if (cpuidle_state_table == dedicated_states)
+ drv->states[drv->state_count].target_residency =
+ __get_cpu_var(smt_snooze_delay);
+
+ drv->state_count += 1;
+ }
+
+ return 0;
+}
+
+/* pseries_idle_devices_uninit(void)
+ * unregister cpuidle devices and de-allocate memory
+ */
+static void pseries_idle_devices_uninit(void)
+{
+ int i;
+ struct cpuidle_device *dev;
+
+ for_each_possible_cpu(i) {
+ dev = per_cpu_ptr(pseries_cpuidle_devices, i);
+ cpuidle_unregister_device(dev);
+ }
+
+ free_percpu(pseries_cpuidle_devices);
+ return;
+}
+
+/* pseries_idle_devices_init()
+ * allocate, initialize and register cpuidle device
+ */
+static int pseries_idle_devices_init(void)
+{
+ int i;
+ struct cpuidle_driver *drv = &pseries_idle_driver;
+ struct cpuidle_device *dev;
+
+ pseries_cpuidle_devices = alloc_percpu(struct cpuidle_device);
+ if (pseries_cpuidle_devices == NULL)
+ return -ENOMEM;
+
+ for_each_possible_cpu(i) {
+ dev = per_cpu_ptr(pseries_cpuidle_devices, i);
+ dev->state_count = drv->state_count;
+ dev->cpu = i;
+ if (cpuidle_register_device(dev)) {
+ printk(KERN_DEBUG \
+ "cpuidle_register_device %d failed!\n", i);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * pseries_idle_probe()
+ * Choose state table for shared versus dedicated partition
+ */
+static int pseries_idle_probe(void)
+{
+
+ if (!firmware_has_feature(FW_FEATURE_SPLPAR))
+ return -ENODEV;
+
+ if (max_idle_state == 0) {
+ printk(KERN_DEBUG "pseries processor idle disabled.\n");
+ return -EPERM;
+ }
+
+ if (get_lppaca()->shared_proc)
+ cpuidle_state_table = shared_states;
+ else
+ cpuidle_state_table = dedicated_states;
+
+ return 0;
+}
+
+static int __init pseries_processor_idle_init(void)
+{
+ int retval;
+
+ retval = pseries_idle_probe();
+ if (retval)
+ return retval;
+
+ pseries_cpuidle_driver_init();
+ retval = cpuidle_register_driver(&pseries_idle_driver);
+ if (retval) {
+ printk(KERN_DEBUG "Registration of pseries driver failed.\n");
+ return retval;
+ }
+
+ retval = pseries_idle_devices_init();
+ if (retval) {
+ pseries_idle_devices_uninit();
+ cpuidle_unregister_driver(&pseries_idle_driver);
+ return retval;
+ }
+
+ printk(KERN_DEBUG "pseries_idle_driver registered\n");
+
+ return 0;
+}
+
+static void __exit pseries_processor_idle_exit(void)
+{
+
+ pseries_idle_devices_uninit();
+ cpuidle_unregister_driver(&pseries_idle_driver);
+
+ return;
+}
+
+module_init(pseries_processor_idle_init);
+module_exit(pseries_processor_idle_exit);
+
+MODULE_AUTHOR("Deepthi Dharwar <deepthi@linux.vnet.ibm.com>");
+MODULE_DESCRIPTION("Cpuidle driver for POWER");
+MODULE_LICENSE("GPL");
diff --git a/arch/powerpc/platforms/pseries/pseries.h b/arch/powerpc/platforms/pseries/pseries.h
index 24c7162..9a3dda0 100644
--- a/arch/powerpc/platforms/pseries/pseries.h
+++ b/arch/powerpc/platforms/pseries/pseries.h
@@ -57,4 +57,7 @@ extern struct device_node *dlpar_configure_connector(u32);
extern int dlpar_attach_node(struct device_node *);
extern int dlpar_detach_node(struct device_node *);
+/* Snooze Delay, pseries_idle */
+DECLARE_PER_CPU(long, smt_snooze_delay);
+
#endif /* _PSERIES_PSERIES_H */
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c
index c3408ca..9c6716a 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -586,9 +586,6 @@ static int __init pSeries_probe(void)
return 1;
}
-
-DECLARE_PER_CPU(long, smt_snooze_delay);
-
static void pseries_dedicated_idle_sleep(void)
{
unsigned int cpu = smp_processor_id();
diff --git a/arch/powerpc/platforms/pseries/smp.c b/arch/powerpc/platforms/pseries/smp.c
index 26e93fd..bbc3c42 100644
--- a/arch/powerpc/platforms/pseries/smp.c
+++ b/arch/powerpc/platforms/pseries/smp.c
@@ -148,6 +148,7 @@ static void __devinit smp_xics_setup_cpu(int cpu)
set_cpu_current_state(cpu, CPU_STATE_ONLINE);
set_default_offline_state(cpu);
#endif
+ pseries_notify_cpuidle_add_cpu(cpu);
}
static int __devinit smp_pSeries_kick_cpu(int nr)
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox