All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH v6 0/9] The power allocator thermal governor
@ 2014-12-05 19:04 Javi Merino
  2014-12-05 19:04 ` [RFC PATCH v6 1/9] tracing: Add array printing helpers Javi Merino
                   ` (8 more replies)
  0 siblings, 9 replies; 48+ messages in thread
From: Javi Merino @ 2014-12-05 19:04 UTC (permalink / raw)
  To: linux-pm, linux-kernel; +Cc: punit.agrawal, broonie, Javi Merino

Hi linux-pm,

The power allocator governor allocates device power to control
temperature.  This requires transforming performance requests into
requested power, which we do with an extended cooling device API
introduced in patch 5 (thermal: extend the cooling device API to
include power information).  Patch 6 (thermal: cpu_cooling: implement
the power cooling device API) extends the cpu cooling device using a
simple power model.  The division of power between the cooling devices
ensures that power is allocated where it is needed the most, based on
the current workload.

Patches 1-3 adds array printing helpers to ftrace, which we then use
in patch 8.

Changes since v5:
  - Addressed Stephen's review of the trace patches.
  - Removed power actors and extended the cooling device interface
    instead.
  - Let platforms override the power allocator governor parameters in
    their thermal zone parameters

Changes since v4:
  - Add more tracing
  - Document some of the limitations of the power allocator governor
  - Export the power_actor API and move power_actor.h to include/linux

Changes since v3:
  - Use tz->passive to poll faster when the first trip point is hit.
  - Don't make a special directory for power_actors
  - Add a DT property for sustainable-power
  - Simplify the static power interface and pass the current thermal
    zone in every power_actor_ops to remove the controversial
    enum power_actor_types
  - Use locks with the actor_list list
  - Use cpufreq_get() to get the frequency of the cpu instead of
    using the notifiers.
  - Remove the prompt for THERMAL_POWER_ACTOR_CPU when configuring
    the kernel

Changes since v2:
  - Changed the PI controller into a PID controller
  - Added static power to the cpu power model
  - tz parameter max_dissipatable_power renamed to sustainable_power
  - Register the cpufreq cooling device as part of the
    power_cpu_actor registration.

Changes since v1:
  - Fixed finding cpufreq cooling devices in cpufreq_frequency_change()
  - Replaced the cooling device interface with a separate power actor
    API
  - Addressed most of Eduardo's comments
  - Incorporated ftrace support for bitmask to trace cpumasks

Todo:
  - Expose thermal zone parameters in sysfs
  - Expose new governor parameters in device tree
  - Expose cooling device weights in device tree

Cheers,
Javi & Punit

Dave Martin (1):
  tracing: Add array printing helpers

Javi Merino (7):
  tools lib traceevent: Generalize numeric argument
  tools lib traceevent: Add support for __print_u{8,16,32,64}_array()
  thermal: let governors have private data for each thermal zone
  thermal: extend the cooling device API to include power information
  thermal: cpu_cooling: implement the power cooling device API
  thermal: introduce the Power Allocator governor
  thermal: add trace events to the power allocator governor

Punit Agrawal (1):
  of: thermal: Introduce sustainable power for a thermal zone

 .../devicetree/bindings/thermal/thermal.txt        |   4 +
 Documentation/thermal/cpu-cooling-api.txt          | 144 +++++-
 Documentation/thermal/power_allocator.txt          | 223 +++++++++
 drivers/thermal/Kconfig                            |  15 +
 drivers/thermal/Makefile                           |   1 +
 drivers/thermal/cpu_cooling.c                      | 455 +++++++++++++++++-
 drivers/thermal/of-thermal.c                       |   4 +
 drivers/thermal/power_allocator.c                  | 528 +++++++++++++++++++++
 drivers/thermal/thermal_core.c                     | 128 ++++-
 drivers/thermal/thermal_core.h                     |   8 +
 include/linux/cpu_cooling.h                        |  49 +-
 include/linux/ftrace_event.h                       |   9 +
 include/linux/thermal.h                            |  61 ++-
 include/trace/events/thermal_power_allocator.h     | 138 ++++++
 include/trace/ftrace.h                             |  17 +
 kernel/trace/trace_output.c                        |  51 ++
 tools/lib/traceevent/event-parse.c                 |  88 +++-
 tools/lib/traceevent/event-parse.h                 |   8 +-
 18 files changed, 1886 insertions(+), 45 deletions(-)
 create mode 100644 Documentation/thermal/power_allocator.txt
 create mode 100644 drivers/thermal/power_allocator.c
 create mode 100644 include/trace/events/thermal_power_allocator.h

-- 
1.9.1



^ permalink raw reply	[flat|nested] 48+ messages in thread

* [RFC PATCH v6 1/9] tracing: Add array printing helpers
  2014-12-05 19:04 [RFC PATCH v6 0/9] The power allocator thermal governor Javi Merino
@ 2014-12-05 19:04 ` Javi Merino
  2014-12-08 14:39   ` Dave P Martin
  2014-12-08 15:42   ` Steven Rostedt
  2014-12-05 19:04 ` [RFC PATCH v6 2/9] tools lib traceevent: Generalize numeric argument Javi Merino
                   ` (7 subsequent siblings)
  8 siblings, 2 replies; 48+ messages in thread
From: Javi Merino @ 2014-12-05 19:04 UTC (permalink / raw)
  To: linux-pm, linux-kernel
  Cc: punit.agrawal, broonie, Dave Martin, Steven Rostedt, Ingo Molnar

From: Dave Martin <Dave.Martin@arm.com>

If a trace event contains an array, there is currently no standard
way to format this for text output.  Drivers are currently hacking
around this by a) local hacks that use the trace_seq functionailty
directly, or b) just not printing that information.  For fixed size
arrays, formatting of the elements can be open-coded, but this gets
cumbersome for arrays of non-trivial size.

These approaches result in non-standard content of the event format
description delivered to userspace, so userland tools needs to be
taught to understand and parse each array printing method
individually.

This patch implements common __print_<type>_array() helpers that
tracepoint implementations can use instead of reinventing them.  A
simple C-style syntax is used to delimit the array and its elements
{like,this}.

So that the helpers can be used with large static arrays as well as
dynamic arrays, they take a pointer and element count: they can be
used with __get_dynamic_array() for use with dynamic arrays.

Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Ingo Molnar <mingo@redhat.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
---
 include/linux/ftrace_event.h |  9 ++++++++
 include/trace/ftrace.h       | 17 +++++++++++++++
 kernel/trace/trace_output.c  | 51 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 77 insertions(+)

diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h
index 28672e87e910..415afc53fa51 100644
--- a/include/linux/ftrace_event.h
+++ b/include/linux/ftrace_event.h
@@ -44,6 +44,15 @@ const char *ftrace_print_bitmask_seq(struct trace_seq *p, void *bitmask_ptr,
 const char *ftrace_print_hex_seq(struct trace_seq *p,
 				 const unsigned char *buf, int len);
 
+const char *ftrace_print_u8_array_seq(struct trace_seq *p,
+				      const u8 *buf, int count);
+const char *ftrace_print_u16_array_seq(struct trace_seq *p,
+				       const u16 *buf, int count);
+const char *ftrace_print_u32_array_seq(struct trace_seq *p,
+				       const u32 *buf, int count);
+const char *ftrace_print_u64_array_seq(struct trace_seq *p,
+				       const u64 *buf, int count);
+
 struct trace_iterator;
 struct trace_event;
 
diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h
index 26b4f2e13275..15bc5d417aea 100644
--- a/include/trace/ftrace.h
+++ b/include/trace/ftrace.h
@@ -263,6 +263,19 @@
 #undef __print_hex
 #define __print_hex(buf, buf_len) ftrace_print_hex_seq(p, buf, buf_len)
 
+#undef __print_u8_array
+#define __print_u8_array(array, count)			\
+	ftrace_print_u8_array_seq(p, array, count)
+#undef __print_u16_array
+#define __print_u16_array(array, count)			\
+	ftrace_print_u16_array_seq(p, array, count)
+#undef __print_u32_array
+#define __print_u32_array(array, count)			\
+	ftrace_print_u32_array_seq(p, array, count)
+#undef __print_u64_array
+#define __print_u64_array(array, count)			\
+	ftrace_print_u64_array_seq(p, array, count)
+
 #undef DECLARE_EVENT_CLASS
 #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print)	\
 static notrace enum print_line_t					\
@@ -676,6 +689,10 @@ static inline void ftrace_test_probe_##call(void)			\
 #undef __get_dynamic_array_len
 #undef __get_str
 #undef __get_bitmask
+#undef __print_u8_array
+#undef __print_u16_array
+#undef __print_u32_array
+#undef __print_u64_array
 
 #undef TP_printk
 #define TP_printk(fmt, args...) "\"" fmt "\", "  __stringify(args)
diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c
index c6977d5a9b12..4a6ee61f30b3 100644
--- a/kernel/trace/trace_output.c
+++ b/kernel/trace/trace_output.c
@@ -186,6 +186,57 @@ ftrace_print_hex_seq(struct trace_seq *p, const unsigned char *buf, int buf_len)
 }
 EXPORT_SYMBOL(ftrace_print_hex_seq);
 
+static const char *
+ftrace_print_array_seq(struct trace_seq *p, const void *buf, int buf_len,
+		       bool (*iterator)(struct trace_seq *p, const char *prefix,
+					const void **buf, int *buf_len))
+{
+	const char *ret = trace_seq_buffer_ptr(p);
+	const char *prefix = "";
+
+	trace_seq_putc(p, '{');
+
+	while (iterator(p, prefix, &buf, &buf_len))
+		prefix = ",";
+
+	trace_seq_putc(p, '}');
+	trace_seq_putc(p, 0);
+
+	return ret;
+}
+
+#define DEFINE_PRINT_ARRAY(type, printk_type, format)			\
+static bool								\
+ftrace_print_array_iterator_##type(struct trace_seq *p, const char *prefix, \
+				   const void **buf, int *buf_len)	\
+{									\
+	const type *__src = *buf;					\
+									\
+	if (*buf_len < sizeof(*__src))					\
+		return false;						\
+									\
+	trace_seq_printf(p, "%s" format, prefix, (printk_type)*__src++); \
+									\
+	*buf = __src;							\
+	*buf_len -= sizeof(*__src);					\
+									\
+	return true;							\
+}									\
+									\
+const char *ftrace_print_##type##_array_seq(				\
+	struct trace_seq *p, const type *buf, int count)		\
+{									\
+	return ftrace_print_array_seq(p, buf, (count) * sizeof(type),	\
+				      ftrace_print_array_iterator_##type); \
+}									\
+									\
+EXPORT_SYMBOL(ftrace_print_##type##_array_seq)
+
+DEFINE_PRINT_ARRAY(u8, unsigned int, "0x%x");
+DEFINE_PRINT_ARRAY(u16, unsigned int, "0x%x");
+DEFINE_PRINT_ARRAY(u32, unsigned int, "0x%x");
+DEFINE_PRINT_ARRAY(u64, unsigned long long, "0x%llx");
+
 int ftrace_raw_output_prep(struct trace_iterator *iter,
 			   struct trace_event *trace_event)
 {
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [RFC PATCH v6 2/9] tools lib traceevent: Generalize numeric argument
  2014-12-05 19:04 [RFC PATCH v6 0/9] The power allocator thermal governor Javi Merino
  2014-12-05 19:04 ` [RFC PATCH v6 1/9] tracing: Add array printing helpers Javi Merino
@ 2014-12-05 19:04 ` Javi Merino
  2014-12-05 19:04 ` [RFC PATCH v6 3/9] tools lib traceevent: Add support for __print_u{8,16,32,64}_array() Javi Merino
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 48+ messages in thread
From: Javi Merino @ 2014-12-05 19:04 UTC (permalink / raw)
  To: linux-pm, linux-kernel
  Cc: punit.agrawal, broonie, Javi Merino, Steven Rostedt,
	Arnaldo Carvalho de Melo, Jiri Olsa

Numeric arguments can be in different bases, so rename it to num so
that they can be used for formats other than PRINT_HEX

Cc: Steven Rostedt <srostedt@redhat.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Signed-off-by: Javi Merino <javi.merino@arm.com>
---
 tools/lib/traceevent/event-parse.c | 26 +++++++++++++-------------
 tools/lib/traceevent/event-parse.h |  4 ++--
 2 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c
index cf3a44bf1ec3..f12ea53cc83b 100644
--- a/tools/lib/traceevent/event-parse.c
+++ b/tools/lib/traceevent/event-parse.c
@@ -754,8 +754,8 @@ static void free_arg(struct print_arg *arg)
 		free_flag_sym(arg->symbol.symbols);
 		break;
 	case PRINT_HEX:
-		free_arg(arg->hex.field);
-		free_arg(arg->hex.size);
+		free_arg(arg->num.field);
+		free_arg(arg->num.size);
 		break;
 	case PRINT_TYPE:
 		free(arg->typecast.type);
@@ -2503,7 +2503,7 @@ process_hex(struct event_format *event, struct print_arg *arg, char **tok)
 	if (test_type_token(type, token, EVENT_DELIM, ","))
 		goto out_free;
 
-	arg->hex.field = field;
+	arg->num.field = field;
 
 	free_token(token);
 
@@ -2519,7 +2519,7 @@ process_hex(struct event_format *event, struct print_arg *arg, char **tok)
 	if (test_type_token(type, token, EVENT_DELIM, ")"))
 		goto out_free;
 
-	arg->hex.size = field;
+	arg->num.size = field;
 
 	free_token(token);
 	type = read_token_item(tok);
@@ -3740,24 +3740,24 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
 		}
 		break;
 	case PRINT_HEX:
-		if (arg->hex.field->type == PRINT_DYNAMIC_ARRAY) {
+		if (arg->num.field->type == PRINT_DYNAMIC_ARRAY) {
 			unsigned long offset;
 			offset = pevent_read_number(pevent,
-				data + arg->hex.field->dynarray.field->offset,
-				arg->hex.field->dynarray.field->size);
+				data + arg->num.field->dynarray.field->offset,
+				arg->num.field->dynarray.field->size);
 			hex = data + (offset & 0xffff);
 		} else {
-			field = arg->hex.field->field.field;
+			field = arg->num.field->field.field;
 			if (!field) {
-				str = arg->hex.field->field.name;
+				str = arg->num.field->field.name;
 				field = pevent_find_any_field(event, str);
 				if (!field)
 					goto out_warning_field;
-				arg->hex.field->field.field = field;
+				arg->num.field->field.field = field;
 			}
 			hex = data + field->offset;
 		}
-		len = eval_num_arg(data, size, event, arg->hex.size);
+		len = eval_num_arg(data, size, event, arg->num.size);
 		for (i = 0; i < len; i++) {
 			if (i)
 				trace_seq_putc(s, ' ');
@@ -4923,9 +4923,9 @@ static void print_args(struct print_arg *args)
 		break;
 	case PRINT_HEX:
 		printf("__print_hex(");
-		print_args(args->hex.field);
+		print_args(args->num.field);
 		printf(", ");
-		print_args(args->hex.size);
+		print_args(args->num.size);
 		printf(")");
 		break;
 	case PRINT_STRING:
diff --git a/tools/lib/traceevent/event-parse.h b/tools/lib/traceevent/event-parse.h
index 7a3873ff9a4f..2bf72e908a74 100644
--- a/tools/lib/traceevent/event-parse.h
+++ b/tools/lib/traceevent/event-parse.h
@@ -240,7 +240,7 @@ struct print_arg_symbol {
 	struct print_flag_sym	*symbols;
 };
 
-struct print_arg_hex {
+struct print_arg_num {
 	struct print_arg	*field;
 	struct print_arg	*size;
 };
@@ -291,7 +291,7 @@ struct print_arg {
 		struct print_arg_typecast	typecast;
 		struct print_arg_flags		flags;
 		struct print_arg_symbol		symbol;
-		struct print_arg_hex		hex;
+		struct print_arg_num		num;
 		struct print_arg_func		func;
 		struct print_arg_string		string;
 		struct print_arg_bitmask	bitmask;
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [RFC PATCH v6 3/9] tools lib traceevent: Add support for __print_u{8,16,32,64}_array()
  2014-12-05 19:04 [RFC PATCH v6 0/9] The power allocator thermal governor Javi Merino
  2014-12-05 19:04 ` [RFC PATCH v6 1/9] tracing: Add array printing helpers Javi Merino
  2014-12-05 19:04 ` [RFC PATCH v6 2/9] tools lib traceevent: Generalize numeric argument Javi Merino
@ 2014-12-05 19:04 ` Javi Merino
  2014-12-05 19:04 ` [RFC PATCH v6 4/9] thermal: let governors have private data for each thermal zone Javi Merino
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 48+ messages in thread
From: Javi Merino @ 2014-12-05 19:04 UTC (permalink / raw)
  To: linux-pm, linux-kernel
  Cc: punit.agrawal, broonie, Javi Merino, Arnaldo Carvalho de Melo,
	Steven Rostedt, Jiri Olsa

Trace can now generate traces with u8, u16, u32 and u64 dynamic
arrays.  Add support to parse them.

Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Steven Rostedt <srostedt@redhat.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Signed-off-by: Javi Merino <javi.merino@arm.com>
---
 tools/lib/traceevent/event-parse.c | 62 +++++++++++++++++++++++++++++++++++---
 tools/lib/traceevent/event-parse.h |  4 +++
 2 files changed, 61 insertions(+), 5 deletions(-)

diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c
index f12ea53cc83b..f67260bddd65 100644
--- a/tools/lib/traceevent/event-parse.c
+++ b/tools/lib/traceevent/event-parse.c
@@ -753,6 +753,10 @@ static void free_arg(struct print_arg *arg)
 		free_arg(arg->symbol.field);
 		free_flag_sym(arg->symbol.symbols);
 		break;
+	case PRINT_U8:
+	case PRINT_U16:
+	case PRINT_U32:
+	case PRINT_U64:
 	case PRINT_HEX:
 		free_arg(arg->num.field);
 		free_arg(arg->num.size);
@@ -2827,6 +2831,22 @@ process_function(struct event_format *event, struct print_arg *arg,
 		free_token(token);
 		return process_hex(event, arg, tok);
 	}
+	if (strcmp(token, "__print_u8_array") == 0) {
+		free_token(token);
+		return process_num(event, arg, tok, PRINT_U8);
+	}
+	if (strcmp(token, "__print_u16_array") == 0) {
+		free_token(token);
+		return process_num(event, arg, tok, PRINT_U16);
+	}
+	if (strcmp(token, "__print_u32_array") == 0) {
+		free_token(token);
+		return process_num(event, arg, tok, PRINT_U32);
+	}
+	if (strcmp(token, "__print_u64_array") == 0) {
+		free_token(token);
+		return process_num(event, arg, tok, PRINT_U64);
+	}
 	if (strcmp(token, "__get_str") == 0) {
 		free_token(token);
 		return process_str(event, arg, tok);
@@ -3355,6 +3375,10 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg
 		break;
 	case PRINT_FLAGS:
 	case PRINT_SYMBOL:
+	case PRINT_U8:
+	case PRINT_U16:
+	case PRINT_U32:
+	case PRINT_U64:
 	case PRINT_HEX:
 		break;
 	case PRINT_TYPE:
@@ -3660,7 +3684,7 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
 	unsigned long long val, fval;
 	unsigned long addr;
 	char *str;
-	unsigned char *hex;
+	void *num;
 	int print;
 	int i, len;
 
@@ -3739,13 +3763,17 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
 			}
 		}
 		break;
+	case PRINT_U8:
+	case PRINT_U16:
+	case PRINT_U32:
+	case PRINT_U64:
 	case PRINT_HEX:
 		if (arg->num.field->type == PRINT_DYNAMIC_ARRAY) {
 			unsigned long offset;
 			offset = pevent_read_number(pevent,
 				data + arg->num.field->dynarray.field->offset,
 				arg->num.field->dynarray.field->size);
-			hex = data + (offset & 0xffff);
+			num = data + (offset & 0xffff);
 		} else {
 			field = arg->num.field->field.field;
 			if (!field) {
@@ -3755,13 +3783,24 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
 					goto out_warning_field;
 				arg->num.field->field.field = field;
 			}
-			hex = data + field->offset;
+			num = data + field->offset;
 		}
 		len = eval_num_arg(data, size, event, arg->num.size);
 		for (i = 0; i < len; i++) {
 			if (i)
 				trace_seq_putc(s, ' ');
-			trace_seq_printf(s, "%02x", hex[i]);
+			if (arg->type == PRINT_HEX)
+				trace_seq_printf(s, "%02x",
+						((uint8_t *)num)[i]);
+			else if (arg->type == PRINT_U8)
+				trace_seq_printf(s, "%u", ((uint8_t *)num)[i]);
+			else if (arg->type == PRINT_U16)
+				trace_seq_printf(s, "%u", ((uint16_t *)num)[i]);
+			else if (arg->type == PRINT_U32)
+				trace_seq_printf(s, "%u", ((uint32_t *)num)[i]);
+			else    /* PRINT_U64 */
+				trace_seq_printf(s, "%lu",
+						((uint64_t *)num)[i]);
 		}
 		break;
 
@@ -4922,7 +4961,20 @@ static void print_args(struct print_arg *args)
 		printf(")");
 		break;
 	case PRINT_HEX:
-		printf("__print_hex(");
+	case PRINT_U8:
+	case PRINT_U16:
+	case PRINT_U32:
+	case PRINT_U64:
+		if (args->type == PRINT_HEX)
+			printf("__print_hex(");
+		else if (args->type == PRINT_U8)
+			printf("__print_u8_array(");
+		else if (args->type == PRINT_U16)
+			printf("__print_u16_array(");
+		else if (args->type == PRINT_U32)
+			printf("__print_u32_array(");
+		else /* PRINT_U64 */
+			printf("__print_u64_array(");
 		print_args(args->num.field);
 		printf(", ");
 		print_args(args->num.size);
diff --git a/tools/lib/traceevent/event-parse.h b/tools/lib/traceevent/event-parse.h
index 2bf72e908a74..51f1f0f0a3b5 100644
--- a/tools/lib/traceevent/event-parse.h
+++ b/tools/lib/traceevent/event-parse.h
@@ -272,6 +272,10 @@ enum print_arg_type {
 	PRINT_FIELD,
 	PRINT_FLAGS,
 	PRINT_SYMBOL,
+	PRINT_U8,
+	PRINT_U16,
+	PRINT_U32,
+	PRINT_U64,
 	PRINT_HEX,
 	PRINT_TYPE,
 	PRINT_STRING,
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [RFC PATCH v6 4/9] thermal: let governors have private data for each thermal zone
  2014-12-05 19:04 [RFC PATCH v6 0/9] The power allocator thermal governor Javi Merino
                   ` (2 preceding siblings ...)
  2014-12-05 19:04 ` [RFC PATCH v6 3/9] tools lib traceevent: Add support for __print_u{8,16,32,64}_array() Javi Merino
@ 2014-12-05 19:04 ` Javi Merino
  2014-12-08  4:11   ` Zhang Rui
  2014-12-05 19:04 ` [RFC PATCH v6 5/9] thermal: extend the cooling device API to include power information Javi Merino
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 48+ messages in thread
From: Javi Merino @ 2014-12-05 19:04 UTC (permalink / raw)
  To: linux-pm, linux-kernel
  Cc: punit.agrawal, broonie, Javi Merino, Zhang Rui, Eduardo Valentin

A governor may need to store its current state between calls to
throttle().  That state depends on the thermal zone, so store it as
private data in struct thermal_zone_device.

The governors may have two new ops: bind_to_tz() and unbind_from_tz().
When provided, these functions let governors do some initialization
and teardown when they are bound/unbound to a tz and possibly store that
information in the governor_data field of the struct
thermal_zone_device.

Cc: Zhang Rui <rui.zhang@intel.com>
Cc: Eduardo Valentin <edubezval@gmail.com>
Signed-off-by: Javi Merino <javi.merino@arm.com>
---
 drivers/thermal/thermal_core.c | 83 ++++++++++++++++++++++++++++++++++++++----
 include/linux/thermal.h        |  9 +++++
 2 files changed, 84 insertions(+), 8 deletions(-)

diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index 43b90709585f..9021cb72a13a 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -75,6 +75,58 @@ static struct thermal_governor *__find_governor(const char *name)
 	return NULL;
 }
 
+/**
+ * bind_previous_governor() - bind the previous governor of the thermal zone
+ * @tz:		a valid pointer to a struct thermal_zone_device
+ * @failed_gov_name:	the name of the governor that failed to register
+ *
+ * Register the previous governor of the thermal zone after a new
+ * governor has failed to be bound.
+ */
+static void bind_previous_governor(struct thermal_zone_device *tz,
+				const char *failed_gov_name)
+{
+	if (tz->governor && tz->governor->bind_to_tz) {
+		if (tz->governor->bind_to_tz(tz)) {
+			dev_err(&tz->device,
+				"governor %s failed to bind and the previous one (%s) failed to bind again, thermal zone %s has no governor\n",
+				failed_gov_name, tz->governor->name, tz->type);
+			tz->governor = NULL;
+		}
+	}
+}
+
+/**
+ * thermal_set_governor() - Switch to another governor
+ * @tz:		a valid pointer to a struct thermal_zone_device
+ * @new_gov:	pointer to the new governor
+ *
+ * Change the governor of thermal zone @tz.
+ *
+ * Return: 0 on success, an error if the new governor's bind_to_tz() failed.
+ */
+static int thermal_set_governor(struct thermal_zone_device *tz,
+				struct thermal_governor *new_gov)
+{
+	int ret = 0;
+
+	if (tz->governor && tz->governor->unbind_from_tz)
+		tz->governor->unbind_from_tz(tz);
+
+	if (new_gov && new_gov->bind_to_tz) {
+		ret = new_gov->bind_to_tz(tz);
+		if (ret) {
+			bind_previous_governor(tz, new_gov->name);
+
+			return ret;
+		}
+	}
+
+	tz->governor = new_gov;
+
+	return ret;
+}
+
 int thermal_register_governor(struct thermal_governor *governor)
 {
 	int err;
@@ -107,8 +159,15 @@ int thermal_register_governor(struct thermal_governor *governor)
 
 		name = pos->tzp->governor_name;
 
-		if (!strncasecmp(name, governor->name, THERMAL_NAME_LENGTH))
-			pos->governor = governor;
+		if (!strncasecmp(name, governor->name, THERMAL_NAME_LENGTH)) {
+			int ret;
+
+			ret = thermal_set_governor(pos, governor);
+			if (ret)
+				dev_err(&pos->device,
+					"Failed to set governor %s for thermal zone %s: %d\n",
+					governor->name, pos->type, ret);
+		}
 	}
 
 	mutex_unlock(&thermal_list_lock);
@@ -134,7 +193,7 @@ void thermal_unregister_governor(struct thermal_governor *governor)
 	list_for_each_entry(pos, &thermal_tz_list, node) {
 		if (!strncasecmp(pos->governor->name, governor->name,
 						THERMAL_NAME_LENGTH))
-			pos->governor = NULL;
+			thermal_set_governor(pos, NULL);
 	}
 
 	mutex_unlock(&thermal_list_lock);
@@ -762,8 +821,9 @@ policy_store(struct device *dev, struct device_attribute *attr,
 	if (!gov)
 		goto exit;
 
-	tz->governor = gov;
-	ret = count;
+	ret = thermal_set_governor(tz, gov);
+	if (!ret)
+		ret = count;
 
 exit:
 	mutex_unlock(&thermal_governor_lock);
@@ -1459,6 +1519,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
 	int result;
 	int count;
 	int passive = 0;
+	struct thermal_governor *governor;
 
 	if (type && strlen(type) >= THERMAL_NAME_LENGTH)
 		return ERR_PTR(-EINVAL);
@@ -1549,9 +1610,15 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
 	mutex_lock(&thermal_governor_lock);
 
 	if (tz->tzp)
-		tz->governor = __find_governor(tz->tzp->governor_name);
+		governor = __find_governor(tz->tzp->governor_name);
 	else
-		tz->governor = def_governor;
+		governor = def_governor;
+
+	result = thermal_set_governor(tz, governor);
+	if (result) {
+		mutex_unlock(&thermal_governor_lock);
+		goto unregister;
+	}
 
 	mutex_unlock(&thermal_governor_lock);
 
@@ -1640,7 +1707,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
 		device_remove_file(&tz->device, &dev_attr_mode);
 	device_remove_file(&tz->device, &dev_attr_policy);
 	remove_trip_attrs(tz);
-	tz->governor = NULL;
+	thermal_set_governor(tz, NULL);
 
 	thermal_remove_hwmon_sysfs(tz);
 	release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index ef90838b36a0..2c14ab1f5c0d 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -191,6 +191,7 @@ struct thermal_attr {
  * @ops:	operations this &thermal_zone_device supports
  * @tzp:	thermal zone parameters
  * @governor:	pointer to the governor for this thermal zone
+ * @governor_data:	private pointer for governor data
  * @thermal_instances:	list of &struct thermal_instance of this thermal zone
  * @idr:	&struct idr to generate unique id for this zone's cooling
  *		devices
@@ -217,6 +218,7 @@ struct thermal_zone_device {
 	struct thermal_zone_device_ops *ops;
 	const struct thermal_zone_params *tzp;
 	struct thermal_governor *governor;
+	void *governor_data;
 	struct list_head thermal_instances;
 	struct idr idr;
 	struct mutex lock;
@@ -227,12 +229,19 @@ struct thermal_zone_device {
 /**
  * struct thermal_governor - structure that holds thermal governor information
  * @name:	name of the governor
+ * @bind_to_tz: callback called when binding to a thermal zone.  If it
+ *		returns 0, the governor is bound to the thermal zone,
+ *		otherwise it fails.
+ * @unbind_from_tz:	callback called when a governor is unbound from a
+ *			thermal zone.
  * @throttle:	callback called for every trip point even if temperature is
  *		below the trip point temperature
  * @governor_list:	node in thermal_governor_list (in thermal_core.c)
  */
 struct thermal_governor {
 	char name[THERMAL_NAME_LENGTH];
+	int (*bind_to_tz)(struct thermal_zone_device *tz);
+	void (*unbind_from_tz)(struct thermal_zone_device *tz);
 	int (*throttle)(struct thermal_zone_device *tz, int trip);
 	struct list_head	governor_list;
 };
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [RFC PATCH v6 5/9] thermal: extend the cooling device API to include power information
  2014-12-05 19:04 [RFC PATCH v6 0/9] The power allocator thermal governor Javi Merino
                   ` (3 preceding siblings ...)
  2014-12-05 19:04 ` [RFC PATCH v6 4/9] thermal: let governors have private data for each thermal zone Javi Merino
@ 2014-12-05 19:04 ` Javi Merino
  2014-12-23 15:14   ` Eduardo Valentin
  2014-12-05 19:04 ` [RFC PATCH v6 6/9] thermal: cpu_cooling: implement the power cooling device API Javi Merino
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 48+ messages in thread
From: Javi Merino @ 2014-12-05 19:04 UTC (permalink / raw)
  To: linux-pm, linux-kernel
  Cc: punit.agrawal, broonie, Javi Merino, Zhang Rui, Eduardo Valentin

Add three optional callbacks to the cooling device interface to allow
them to express power.  In addition to the callbacks, add helpers to
identify cooling devices that implement the power cooling device API.

Cc: Zhang Rui <rui.zhang@intel.com>
Cc: Eduardo Valentin <edubezval@gmail.com>
Signed-off-by: Javi Merino <javi.merino@arm.com>
---
 Documentation/thermal/power_allocator.txt | 27 ++++++++++++++++++++++
 drivers/thermal/thermal_core.c            | 38 +++++++++++++++++++++++++++++++
 include/linux/thermal.h                   | 12 ++++++++++
 3 files changed, 77 insertions(+)
 create mode 100644 Documentation/thermal/power_allocator.txt

diff --git a/Documentation/thermal/power_allocator.txt b/Documentation/thermal/power_allocator.txt
new file mode 100644
index 000000000000..d3bb79050c27
--- /dev/null
+++ b/Documentation/thermal/power_allocator.txt
@@ -0,0 +1,27 @@
+Cooling device power API
+========================
+
+Cooling devices controlled by this governor must supply the additional
+"power" API in their `cooling_device_ops`.  It consists on three ops:
+
+1. u32 get_actual_power(struct thermal_cooling_device *cdev);
+@cdev: The `struct thermal_cooling_device` pointer
+
+`get_actual_power()` returns the power currently consumed by the
+device in milliwatts.
+
+2. u32 state2power(struct thermal_cooling_device *cdev, unsigned long
+        state);
+@cdev: The `struct thermal_cooling_device` pointer
+@state: A cooling device state
+
+Convert cooling device state @state into power consumption in
+milliwatts.
+
+3. unsigned long power2state(struct thermal_cooling_device *cdev,
+        u32 power);
+@cdev: The `struct thermal_cooling_device` pointer
+@power: power in milliwatts
+
+Calculate a cooling device state that would make the device consume at
+most @power mW.
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index 9021cb72a13a..c490f262ea7f 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -866,6 +866,44 @@ emul_temp_store(struct device *dev, struct device_attribute *attr,
 static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store);
 #endif/*CONFIG_THERMAL_EMULATION*/
 
+/**
+ * power_actor_get_max_power() - get the maximum power that a cdev can consume
+ * @cdev:	pointer to &thermal_cooling_device
+ *
+ * Calculate the maximum power consumption in milliwats that the
+ * cooling device can currently consume.  If @cdev doesn't support the
+ * power_actor API, this function returns 0.
+ */
+u32 power_actor_get_max_power(struct thermal_cooling_device *cdev)
+{
+	if (!cdev_is_power_actor(cdev))
+		return 0;
+
+	return cdev->ops->state2power(cdev, 0);
+}
+
+/**
+ * power_actor_set_power() - limit the maximum power that a cooling device can consume
+ * @cdev:	pointer to &thermal_cooling_device
+ * @power:	the power in milliwatts
+ *
+ * Set the cooling device to consume at most @power milliwatts.
+ *
+ * Returns: 0 on success, -EINVAL if the cooling device does not
+ * implement the power actor API or -E* for other failures.
+ */
+int power_actor_set_power(struct thermal_cooling_device *cdev, u32 power)
+{
+	unsigned long state;
+
+	if (!cdev_is_power_actor(cdev))
+		return -EINVAL;
+
+	state = cdev->ops->power2state(cdev, power);
+
+	return cdev->ops->set_cur_state(cdev, state);
+}
+
 static DEVICE_ATTR(type, 0444, type_show, NULL);
 static DEVICE_ATTR(temp, 0444, temp_show, NULL);
 static DEVICE_ATTR(mode, 0644, mode_show, mode_store);
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 2c14ab1f5c0d..1155457caf52 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -142,6 +142,9 @@ struct thermal_cooling_device_ops {
 	int (*get_max_state) (struct thermal_cooling_device *, unsigned long *);
 	int (*get_cur_state) (struct thermal_cooling_device *, unsigned long *);
 	int (*set_cur_state) (struct thermal_cooling_device *, unsigned long);
+	u32 (*get_actual_power) (struct thermal_cooling_device *);
+	u32 (*state2power) (struct thermal_cooling_device *, unsigned long);
+	unsigned long (*power2state) (struct thermal_cooling_device *, u32);
 };
 
 struct thermal_cooling_device {
@@ -322,6 +325,15 @@ void thermal_zone_of_sensor_unregister(struct device *dev,
 }
 
 #endif
+
+static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev)
+{
+	return cdev->ops->get_actual_power && cdev->ops->state2power &&
+		cdev->ops->power2state;
+}
+
+u32 power_actor_get_max_power(struct thermal_cooling_device *);
+int power_actor_set_power(struct thermal_cooling_device *, u32);
 struct thermal_zone_device *thermal_zone_device_register(const char *, int, int,
 		void *, struct thermal_zone_device_ops *,
 		const struct thermal_zone_params *, int, int);
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [RFC PATCH v6 6/9] thermal: cpu_cooling: implement the power cooling device API
  2014-12-05 19:04 [RFC PATCH v6 0/9] The power allocator thermal governor Javi Merino
                   ` (4 preceding siblings ...)
  2014-12-05 19:04 ` [RFC PATCH v6 5/9] thermal: extend the cooling device API to include power information Javi Merino
@ 2014-12-05 19:04 ` Javi Merino
  2014-12-08  5:49   ` Viresh Kumar
  2015-01-28  5:23   ` Eduardo Valentin
  2014-12-05 19:04 ` [RFC PATCH v6 7/9] thermal: introduce the Power Allocator governor Javi Merino
                   ` (2 subsequent siblings)
  8 siblings, 2 replies; 48+ messages in thread
From: Javi Merino @ 2014-12-05 19:04 UTC (permalink / raw)
  To: linux-pm, linux-kernel
  Cc: punit.agrawal, broonie, Javi Merino, Zhang Rui, Eduardo Valentin

Add a basic power model to the cpu cooling device to implement the
power cooling device API.  The power model uses the current frequency,
current load and OPPs for the power calculations.  The cpus must have
registered their OPPs using the OPP library.

Cc: Zhang Rui <rui.zhang@intel.com>
Cc: Eduardo Valentin <edubezval@gmail.com>
Signed-off-by: Punit Agrawal <punit.agrawal@arm.com>
Signed-off-by: Javi Merino <javi.merino@arm.com>
---
 Documentation/thermal/cpu-cooling-api.txt | 144 +++++++++-
 drivers/thermal/cpu_cooling.c             | 431 +++++++++++++++++++++++++++++-
 include/linux/cpu_cooling.h               |  49 +++-
 3 files changed, 611 insertions(+), 13 deletions(-)

diff --git a/Documentation/thermal/cpu-cooling-api.txt b/Documentation/thermal/cpu-cooling-api.txt
index fca24c931ec8..d438a900e374 100644
--- a/Documentation/thermal/cpu-cooling-api.txt
+++ b/Documentation/thermal/cpu-cooling-api.txt
@@ -25,8 +25,150 @@ the user. The registration APIs returns the cooling device pointer.
 
    clip_cpus: cpumask of cpus where the frequency constraints will happen.
 
-1.1.2 void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
+1.1.2 struct thermal_cooling_device *cpufreq_power_cooling_register(
+    const struct cpumask *clip_cpus, u32 capacitance,
+    get_static_t plat_static_func)
+
+Similar to cpufreq_cooling_register, this function registers a cpufreq
+cooling device.  Using this function, the cooling device will
+implement the power extensions by using a simple cpu power model.  The
+cpus must have registered their OPPs using the OPP library.
+
+The additional parameters are needed for the power model (See 2. Power
+models).  "capacitance" is the dynamic power coefficient (See 2.1
+Dynamic power).  "plat_static_func" is a function to calculate the
+static power consumed by these cpus (See 2.2 Static power).
+
+1.1.3 struct thermal_cooling_device *of_cpufreq_power_cooling_register(
+    struct device_node *np, const struct cpumask *clip_cpus, u32 capacitance,
+    get_static_t plat_static_func)
+
+Similar to cpufreq_power_cooling_register, this function register a
+cpufreq cooling device with power extensions using the device tree
+information supplied by the np parameter.
+
+1.1.4 void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
 
     This interface function unregisters the "thermal-cpufreq-%x" cooling device.
 
     cdev: Cooling device pointer which has to be unregistered.
+
+2. Power models
+
+The power API registration functions provide a simple power model for
+CPUs.  The current power is calculated as dynamic + (optionally)
+static power.  This power model requires that the operating-points of
+the CPUs are registered using the kernel's opp library and the
+`cpufreq_frequency_table` is assigned to the `struct device` of the
+cpu.  If you are using the `cpufreq-cpu0.c` driver then the
+`cpufreq_frequency_table` should already be assigned to the cpu
+device.
+
+The `plat_static_func` parameter of `cpufreq_power_cooling_register()`
+and `of_cpufreq_power_cooling_register()` is optional.  If you don't
+provide it, only dynamic power will be considered.
+
+2.1 Dynamic power
+
+The dynamic power consumption of a processor depends on many factors.
+For a given processor implementation the primary factors are:
+
+- The time the processor spends running, consuming dynamic power, as
+  compared to the time in idle states where dynamic consumption is
+  negligible.  Herein we refer to this as 'utilisation'.
+- The voltage and frequency levels as a result of DVFS.  The DVFS
+  level is a dominant factor governing power consumption.
+- In running time the 'execution' behaviour (instruction types, memory
+  access patterns and so forth) causes, in most cases, a second order
+  variation.  In pathological cases this variation can be significant,
+  but typically it is of a much lesser impact than the factors above.
+
+A high level dynamic power consumption model may then be represented as:
+
+Pdyn = f(run) * Voltage^2 * Frequency * Utilisation
+
+f(run) here represents the described execution behaviour and its
+result has a units of Watts/Hz/Volt^2 (this often expressed in
+mW/MHz/uVolt^2)
+
+The detailed behaviour for f(run) could be modelled on-line.  However,
+in practice, such an on-line model has dependencies on a number of
+implementation specific processor support and characterisation
+factors.  Therefore, in initial implementation that contribution is
+represented as a constant coefficient.  This is a simplification
+consistent with the relative contribution to overall power variation.
+
+In this simplified representation our model becomes:
+
+Pdyn = Kd * Voltage^2 * Frequency * Utilisation
+
+Where Kd (capacitance) represents an indicative running time dynamic
+power coefficient in fundamental units of mW/MHz/uVolt^2
+
+2.2 Static power
+
+Static leakage power consumption depends on a number of factors.  For a
+given circuit implementation the primary factors are:
+
+- Time the circuit spends in each 'power state'
+- Temperature
+- Operating voltage
+- Process grade
+
+The time the circuit spends in each 'power state' for a given
+evaluation period at first order means OFF or ON.  However,
+'retention' states can also be supported that reduce power during
+inactive periods without loss of context.
+
+Note: The visibility of state entries to the OS can vary, according to
+platform specifics, and this can then impact the accuracy of a model
+based on OS state information alone.  It might be possible in some
+cases to extract more accurate information from system resources.
+
+The temperature, operating voltage and process 'grade' (slow to fast)
+of the circuit are all significant factors in static leakage power
+consumption.  All of these have complex relationships to static power.
+
+Circuit implementation specific factors include the chosen silicon
+process as well as the type, number and size of transistors in both
+the logic gates and any RAM elements included.
+
+The static power consumption modelling must take into account the
+power managed regions that are implemented.  Taking the example of an
+ARM processor cluster, the modelling would take into account whether
+each CPU can be powered OFF separately or if only a single power
+region is implemented for the complete cluster.
+
+In one view, there are others, a static power consumption model can
+then start from a set of reference values for each power managed
+region (e.g. CPU, Cluster/L2) in each state (e.g. ON, OFF) at an
+arbitrary process grade, voltage and temperature point.  These values
+are then scaled for all of the following: the time in each state, the
+process grade, the current temperature and the operating voltage.
+However, since both implementation specific and complex relationships
+dominate the estimate, the appropriate interface to the model from the
+cpu cooling device is to provide a function callback that calculates
+the static power in this platform.  When registering the cpu cooling
+device pass a function pointer that follows the `get_static_t`
+prototype:
+
+    u32 plat_get_static(cpumask_t *cpumask, unsigned long voltage);
+
+with `cpumask` a cpumask of the cpus involved in the calculation and
+`voltage` the voltage at which they are operating.
+
+If `plat_static_func` is NULL, static power is considered to be
+negligible for this platform and only dynamic power is considered.
+
+The platform specific callback can then use any combination of tables
+and/or equations to permute the estimated value.  Process grade
+information is not passed to the model since access to such data, from
+on-chip measurement capability or manufacture time data, is platform
+specific.
+
+Note: the significance of static power for CPUs in comparison to
+dynamic power is highly dependent on implementation.  Given the
+potential complexity in implementation, the importance and accuracy of
+its inclusion when using cpu cooling devices should be assessed on a
+case by cases basis.
+
diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
index ad09e51ffae4..335d95dd7e5a 100644
--- a/drivers/thermal/cpu_cooling.c
+++ b/drivers/thermal/cpu_cooling.c
@@ -24,11 +24,25 @@
 #include <linux/thermal.h>
 #include <linux/cpufreq.h>
 #include <linux/err.h>
+#include <linux/pm_opp.h>
 #include <linux/slab.h>
 #include <linux/cpu.h>
 #include <linux/cpu_cooling.h>
 
 /**
+ * struct power_table - frequency to power conversion
+ * @frequency:	frequency in KHz
+ * @power:	power in mW
+ *
+ * This structure is built when the cooling device registers and helps
+ * in translating frequency to power and viceversa.
+ */
+struct power_table {
+	u32 frequency;
+	u32 power;
+};
+
+/**
  * struct cpufreq_cooling_device - data for cooling device with cpufreq
  * @id: unique integer value corresponding to each cpufreq_cooling_device
  *	registered.
@@ -39,6 +53,14 @@
  * @cpufreq_val: integer value representing the absolute value of the clipped
  *	frequency.
  * @allowed_cpus: all the cpus involved for this cpufreq_cooling_device.
+ * @last_load: load measured by the latest call to cpufreq_get_actual_power()
+ * @time_in_idle: previous reading of the absolute time that this cpu was idle
+ * @time_in_idle_timestamp: wall time of the last invocation of
+ *	get_cpu_idle_time_us()
+ * @dyn_power_table: array of struct power_table for frequency to power
+ *	conversion
+ * @dyn_power_table_entries: number of entries in the @dyn_power_table array
+ * @plat_get_static_power: callback to calculate the static power
  *
  * This structure is required for keeping information of each
  * cpufreq_cooling_device registered. In order to prevent corruption of this a
@@ -51,6 +73,12 @@ struct cpufreq_cooling_device {
 	unsigned int cpufreq_val;
 	struct cpumask allowed_cpus;
 	struct list_head node;
+	u32 last_load;
+	u64 time_in_idle[NR_CPUS];
+	u64 time_in_idle_timestamp[NR_CPUS];
+	struct power_table *dyn_power_table;
+	int dyn_power_table_entries;
+	get_static_t plat_get_static_power;
 };
 static DEFINE_IDR(cpufreq_idr);
 static DEFINE_MUTEX(cooling_cpufreq_lock);
@@ -338,6 +366,206 @@ static int cpufreq_thermal_notifier(struct notifier_block *nb,
 	return 0;
 }
 
+/**
+ * build_dyn_power_table() - create a dynamic power to frequency table
+ * @cpufreq_device:	the cpufreq cooling device in which to store the table
+ * @capacitance: dynamic power coefficient for these cpus
+ *
+ * Build a dynamic power to frequency table for this cpu and store it
+ * in @cpufreq_device.  This table will be used in cpu_power_to_freq() and
+ * cpu_freq_to_power() to convert between power and frequency
+ * efficiently.  Power is stored in mW, frequency in KHz.  The
+ * resulting table is in ascending order.
+ *
+ * Return: 0 on success, -E* on error.
+ */
+static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device,
+				u32 capacitance)
+{
+	struct power_table *power_table;
+	struct dev_pm_opp *opp;
+	struct device *dev = NULL;
+	int num_opps, cpu, i, ret = 0;
+	unsigned long freq;
+
+	num_opps = 0;
+
+	rcu_read_lock();
+
+	for_each_cpu(cpu, &cpufreq_device->allowed_cpus) {
+		dev = get_cpu_device(cpu);
+		if (!dev)
+			continue;
+
+		num_opps = dev_pm_opp_get_opp_count(dev);
+		if (num_opps > 0) {
+			break;
+		} else if (num_opps < 0) {
+			ret = num_opps;
+			goto unlock;
+		}
+	}
+
+	if (num_opps == 0) {
+		ret = -EINVAL;
+		goto unlock;
+	}
+
+	power_table = kcalloc(num_opps, sizeof(*power_table), GFP_KERNEL);
+
+	i = 0;
+	for (freq = 0;
+	     opp = dev_pm_opp_find_freq_ceil(dev, &freq), !IS_ERR(opp);
+	     freq++) {
+		u32 freq_mhz, voltage_mv;
+		u64 power;
+
+		freq_mhz = freq / 1000000;
+		voltage_mv = dev_pm_opp_get_voltage(opp) / 1000;
+
+		/*
+		 * Do the multiplication with MHz and millivolt so as
+		 * to not overflow.
+		 */
+		power = (u64)capacitance * freq_mhz * voltage_mv * voltage_mv;
+		do_div(power, 1000000000);
+
+		/* frequency is stored in power_table in KHz */
+		power_table[i].frequency = freq / 1000;
+		power_table[i].power = power;
+
+		i++;
+	}
+
+	if (i == 0) {
+		ret = PTR_ERR(opp);
+		goto unlock;
+	}
+
+	cpufreq_device->dyn_power_table = power_table;
+	cpufreq_device->dyn_power_table_entries = i;
+
+unlock:
+	rcu_read_unlock();
+	return ret;
+}
+
+static u32 cpu_freq_to_power(struct cpufreq_cooling_device *cpufreq_device,
+			u32 freq)
+{
+	int i;
+	struct power_table *pt = cpufreq_device->dyn_power_table;
+
+	for (i = 1; i < cpufreq_device->dyn_power_table_entries; i++)
+		if (freq < pt[i].frequency)
+			break;
+
+	return pt[i - 1].power;
+}
+
+static u32 cpu_power_to_freq(struct cpufreq_cooling_device *cpufreq_device,
+			u32 power)
+{
+	int i;
+	struct power_table *pt = cpufreq_device->dyn_power_table;
+
+	for (i = 1; i < cpufreq_device->dyn_power_table_entries; i++)
+		if (power < pt[i].power)
+			break;
+
+	return pt[i - 1].frequency;
+}
+
+/**
+ * get_load() - get load for a cpu since last updated
+ * @cpufreq_device:	&struct cpufreq_cooling_device for this cpu
+ * @cpu:	cpu number
+ *
+ * Return: The average load of cpu @cpu in percentage since this
+ * function was last called.
+ */
+static u32 get_load(struct cpufreq_cooling_device *cpufreq_device, int cpu)
+{
+	u32 load;
+	u64 now, now_idle, delta_time, delta_idle;
+
+	now_idle = get_cpu_idle_time(cpu, &now, 0);
+	delta_idle = now_idle - cpufreq_device->time_in_idle[cpu];
+	delta_time = now - cpufreq_device->time_in_idle_timestamp[cpu];
+
+	if (delta_time <= delta_idle)
+		load = 0;
+	else
+		load = div64_u64(100 * (delta_time - delta_idle), delta_time);
+
+	cpufreq_device->time_in_idle[cpu] = now_idle;
+	cpufreq_device->time_in_idle_timestamp[cpu] = now;
+
+	return load;
+}
+
+/**
+ * get_static_power() - calculate the static power consumed by the cpus
+ * @cpufreq_device:	struct &cpufreq_cooling_device for this cpu cdev
+ * @freq:	frequency in KHz
+ *
+ * Calculate the static power consumed by the cpus described by
+ * @cpu_actor running at frequency @freq.  This function relies on a
+ * platform specific function that should have been provided when the
+ * actor was registered.  If it wasn't, the static power is assumed to
+ * be negligible.
+ *
+ * Return: The static power consumed by the cpus.  It returns 0 on
+ * error or if there is no plat_get_static_power().
+ */
+static u32 get_static_power(struct cpufreq_cooling_device *cpufreq_device,
+			unsigned long freq)
+{
+	struct device *cpu_dev;
+	struct dev_pm_opp *opp;
+	unsigned long voltage;
+	struct cpumask *cpumask = &cpufreq_device->allowed_cpus;
+	unsigned long freq_hz = freq * 1000;
+
+	if (!cpufreq_device->plat_get_static_power)
+		return 0;
+
+	cpu_dev = get_cpu_device(cpumask_any(cpumask));
+
+	rcu_read_lock();
+
+	opp = dev_pm_opp_find_freq_exact(cpu_dev, freq_hz, true);
+	voltage = dev_pm_opp_get_voltage(opp);
+
+	rcu_read_unlock();
+
+	if (voltage == 0) {
+		dev_warn_ratelimited(cpu_dev,
+				"Failed to get voltage for frequency %lu: %ld\n",
+				freq_hz, IS_ERR(opp) ? PTR_ERR(opp) : 0);
+		return 0;
+	}
+
+	return cpufreq_device->plat_get_static_power(cpumask, voltage);
+}
+
+/**
+ * get_dynamic_power() - calculate the dynamic power
+ * @cpufreq_device:	&cpufreq_cooling_device for this cdev
+ * @freq:	current frequency
+ *
+ * Return: the dynamic power consumed by the cpus described by
+ * @cpufreq_device.
+ */
+static u32 get_dynamic_power(struct cpufreq_cooling_device *cpufreq_device,
+			unsigned long freq)
+{
+	u32 raw_cpu_power;
+
+	raw_cpu_power = cpu_freq_to_power(cpufreq_device, freq);
+	return (raw_cpu_power * cpufreq_device->last_load) / 100;
+}
+
 /* cpufreq cooling device callback functions are defined below */
 
 /**
@@ -407,8 +635,106 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
 	return cpufreq_apply_cooling(cpufreq_device, state);
 }
 
+/**
+ * cpufreq_get_actual_power() - get the current power
+ * @cdev:	&thermal_cooling_device pointer
+ *
+ * Return the current power consumption of the cpus in milliwatts.
+ */
+static u32 cpufreq_get_actual_power(struct thermal_cooling_device *cdev)
+{
+	unsigned long freq;
+	int cpu;
+	u32 static_power, dynamic_power, total_load = 0;
+	struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
+
+	freq = cpufreq_quick_get(cpumask_any(&cpufreq_device->allowed_cpus));
+
+	for_each_cpu(cpu, &cpufreq_device->allowed_cpus) {
+		u32 load;
+
+		if (cpu_online(cpu))
+			load = get_load(cpufreq_device, cpu);
+		else
+			load = 0;
+
+		total_load += load;
+	}
+
+	cpufreq_device->last_load = total_load;
+
+	static_power = get_static_power(cpufreq_device, freq);
+	dynamic_power = get_dynamic_power(cpufreq_device, freq);
+
+	return static_power + dynamic_power;
+}
+
+/**
+ * cpufreq_state2power() - convert a cpu cdev state to power consumed
+ * @cdev:	&thermal_cooling_device pointer
+ * @state:	cooling device state to be converted
+ *
+ * Convert cooling device state @state into power consumption in milliwatts.
+ */
+static u32 cpufreq_state2power(struct thermal_cooling_device *cdev,
+			unsigned long state)
+{
+	unsigned int freq, num_cpus;
+	cpumask_t cpumask;
+	u32 static_power, dynamic_power;
+	struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
+
+	cpumask_and(&cpumask, &cpufreq_device->allowed_cpus, cpu_online_mask);
+	num_cpus = cpumask_weight(&cpumask);
+
+	freq = get_cpu_frequency(cpumask_any(&cpumask), state);
+	if (!freq)
+		return 0;
+
+	static_power = get_static_power(cpufreq_device, freq);
+	dynamic_power = cpu_freq_to_power(cpufreq_device, freq) * num_cpus;
+
+	return static_power + dynamic_power;
+}
+
+/**
+ * cpufreq_power2state() - convert power to a cooling device state
+ * @cdev:	&thermal_cooling_device pointer
+ * @power:	power in milliwatts to be converted
+ *
+ * Calculate a cooling device state for the cpus described by @cdev
+ * that would allow them to consume at most @power mW.
+ */
+static unsigned long cpufreq_power2state(struct thermal_cooling_device *cdev,
+					u32 power)
+{
+	unsigned int cpu, cur_freq, target_freq;
+	s32 dyn_power;
+	u32 last_load, normalised_power;
+	unsigned long cdev_state;
+	struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
+
+	cpu = cpumask_any_and(&cpufreq_device->allowed_cpus, cpu_online_mask);
+
+	cur_freq = cpufreq_quick_get(cpu);
+	dyn_power = power - get_static_power(cpufreq_device, cur_freq);
+	dyn_power = dyn_power > 0 ? dyn_power : 0;
+	last_load = cpufreq_device->last_load ?: 1;
+	normalised_power = (dyn_power * 100) / last_load;
+	target_freq = cpu_power_to_freq(cpufreq_device, normalised_power);
+
+	cdev_state = cpufreq_cooling_get_level(cpu, target_freq);
+	if (cdev_state == THERMAL_CSTATE_INVALID) {
+		pr_err_ratelimited("Failed to convert %dKHz for cpu %d into a cdev state\n",
+				target_freq, cpu);
+		return 0;
+	}
+
+	return cdev_state;
+}
+
 /* Bind cpufreq callbacks to thermal cooling device ops */
-static struct thermal_cooling_device_ops const cpufreq_cooling_ops = {
+static struct thermal_cooling_device_ops cpufreq_cooling_ops = {
 	.get_max_state = cpufreq_get_max_state,
 	.get_cur_state = cpufreq_get_cur_state,
 	.set_cur_state = cpufreq_set_cur_state,
@@ -434,7 +760,8 @@ static struct notifier_block thermal_cpufreq_notifier_block = {
  */
 static struct thermal_cooling_device *
 __cpufreq_cooling_register(struct device_node *np,
-			   const struct cpumask *clip_cpus)
+			const struct cpumask *clip_cpus, u32 capacitance,
+			get_static_t plat_static_func)
 {
 	struct thermal_cooling_device *cool_dev;
 	struct cpufreq_cooling_device *cpufreq_dev = NULL;
@@ -464,10 +791,23 @@ __cpufreq_cooling_register(struct device_node *np,
 
 	cpumask_copy(&cpufreq_dev->allowed_cpus, clip_cpus);
 
+	if (capacitance) {
+		cpufreq_cooling_ops.get_actual_power = cpufreq_get_actual_power;
+		cpufreq_cooling_ops.state2power = cpufreq_state2power;
+		cpufreq_cooling_ops.power2state = cpufreq_power2state;
+		cpufreq_dev->plat_get_static_power = plat_static_func;
+
+		ret = build_dyn_power_table(cpufreq_dev, capacitance);
+		if (ret) {
+			cool_dev = ERR_PTR(ret);
+			goto free;
+		}
+	}
+
 	ret = get_idr(&cpufreq_idr, &cpufreq_dev->id);
 	if (ret) {
-		kfree(cpufreq_dev);
-		return ERR_PTR(-EINVAL);
+		cool_dev = ERR_PTR(-EINVAL);
+		goto free;
 	}
 
 	snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d",
@@ -475,11 +815,8 @@ __cpufreq_cooling_register(struct device_node *np,
 
 	cool_dev = thermal_of_cooling_device_register(np, dev_name, cpufreq_dev,
 						      &cpufreq_cooling_ops);
-	if (IS_ERR(cool_dev)) {
-		release_idr(&cpufreq_idr, cpufreq_dev->id);
-		kfree(cpufreq_dev);
-		return cool_dev;
-	}
+	if (IS_ERR(cool_dev))
+		goto release_idr;
 	cpufreq_dev->cool_dev = cool_dev;
 	cpufreq_dev->cpufreq_state = 0;
 	mutex_lock(&cooling_cpufreq_lock);
@@ -494,6 +831,12 @@ __cpufreq_cooling_register(struct device_node *np,
 	mutex_unlock(&cooling_cpufreq_lock);
 
 	return cool_dev;
+
+release_idr:
+	release_idr(&cpufreq_idr, cpufreq_dev->id);
+free:
+	kfree(cpufreq_dev);
+	return cool_dev;
 }
 
 /**
@@ -510,7 +853,7 @@ __cpufreq_cooling_register(struct device_node *np,
 struct thermal_cooling_device *
 cpufreq_cooling_register(const struct cpumask *clip_cpus)
 {
-	return __cpufreq_cooling_register(NULL, clip_cpus);
+	return __cpufreq_cooling_register(NULL, clip_cpus, 0, NULL);
 }
 EXPORT_SYMBOL_GPL(cpufreq_cooling_register);
 
@@ -534,11 +877,77 @@ of_cpufreq_cooling_register(struct device_node *np,
 	if (!np)
 		return ERR_PTR(-EINVAL);
 
-	return __cpufreq_cooling_register(np, clip_cpus);
+	return __cpufreq_cooling_register(np, clip_cpus, 0, NULL);
 }
 EXPORT_SYMBOL_GPL(of_cpufreq_cooling_register);
 
 /**
+ * cpufreq_power_cooling_register() - create cpufreq cooling device with power extensions
+ * @clip_cpus:	cpumask of cpus where the frequency constraints will happen
+ * @capacitance:	dynamic power coefficient for these cpus
+ * @plat_static_func:	function to calculate the static power consumed by these
+ *			cpus (optional)
+ *
+ * This interface function registers the cpufreq cooling device with
+ * the name "thermal-cpufreq-%x".  This api can support multiple
+ * instances of cpufreq cooling devices.  Using this function, the
+ * cooling device will implement the power extensions by using a
+ * simple cpu power model.  The cpus must have registered their OPPs
+ * using the OPP library.
+ *
+ * An optional @plat_static_func may be provided to calculate the
+ * static power consumed by these cpus.  If the platform's static
+ * power consumption is unknown or negligible, make it NULL.
+ *
+ * Return: a valid struct thermal_cooling_device pointer on success,
+ * on failure, it returns a corresponding ERR_PTR().
+ */
+struct thermal_cooling_device *
+cpufreq_power_cooling_register(const struct cpumask *clip_cpus, u32 capacitance,
+			get_static_t plat_static_func)
+{
+	return __cpufreq_cooling_register(NULL, clip_cpus, capacitance,
+				plat_static_func);
+}
+EXPORT_SYMBOL(cpufreq_power_cooling_register);
+
+/**
+ * of_cpufreq_power_cooling_register() - create cpufreq cooling device with power extensions
+ * @np:	a valid struct device_node to the cooling device device tree node
+ * @clip_cpus:	cpumask of cpus where the frequency constraints will happen
+ * @capacitance:	dynamic power coefficient for these cpus
+ * @plat_static_func:	function to calculate the static power consumed by these
+ *			cpus (optional)
+ *
+ * This interface function registers the cpufreq cooling device with
+ * the name "thermal-cpufreq-%x".  This api can support multiple
+ * instances of cpufreq cooling devices.  Using this API, the cpufreq
+ * cooling device will be linked to the device tree node provided.
+ * Using this function, the cooling device will implement the power
+ * extensions by using a simple cpu power model.  The cpus must have
+ * registered their OPPs using the OPP library.
+ *
+ * An optional @plat_static_func may be provided to calculate the
+ * static power consumed by these cpus.  If the platform's static
+ * power consumption is unknown or negligible, make it NULL.
+ *
+ * Return: a valid struct thermal_cooling_device pointer on success,
+ * on failure, it returns a corresponding ERR_PTR().
+ */
+struct thermal_cooling_device *
+of_cpufreq_power_cooling_register(struct device_node *np,
+			const struct cpumask *clip_cpus, u32 capacitance,
+			get_static_t plat_static_func)
+{
+	if (!np)
+		return ERR_PTR(-EINVAL);
+
+	return __cpufreq_cooling_register(np, clip_cpus, capacitance,
+				plat_static_func);
+}
+EXPORT_SYMBOL(of_cpufreq_power_cooling_register);
+
+/**
  * cpufreq_cooling_unregister - function to remove cpufreq cooling device.
  * @cdev: thermal cooling device pointer.
  *
diff --git a/include/linux/cpu_cooling.h b/include/linux/cpu_cooling.h
index c303d383def1..5c4f4567acf0 100644
--- a/include/linux/cpu_cooling.h
+++ b/include/linux/cpu_cooling.h
@@ -28,6 +28,8 @@
 #include <linux/thermal.h>
 #include <linux/cpumask.h>
 
+typedef u32 (*get_static_t)(cpumask_t *cpumask, unsigned long voltage);
+
 #ifdef CONFIG_CPU_THERMAL
 /**
  * cpufreq_cooling_register - function to create cpufreq cooling device.
@@ -37,14 +39,38 @@ struct thermal_cooling_device *
 cpufreq_cooling_register(const struct cpumask *clip_cpus);
 
 /**
+ * cpufreq_power_cooling_register() - create cpufreq cooling device with power extensions
+ * @clip_cpus: cpumask of cpus where the frequency constraints will happen
+ * @capacitance:	dynamic power coefficient for these cpus
+ * @plat_static_func:	function to calculate the static power consumed by these
+ *			cpus (optional)
+ */
+struct thermal_cooling_device *
+cpufreq_power_cooling_register(const struct cpumask *clip_cpus,
+			u32 capacitance, get_static_t plat_static_func);
+
+#ifdef CONFIG_THERMAL_OF
+/**
  * of_cpufreq_cooling_register - create cpufreq cooling device based on DT.
  * @np: a valid struct device_node to the cooling device device tree node.
  * @clip_cpus: cpumask of cpus where the frequency constraints will happen
  */
-#ifdef CONFIG_THERMAL_OF
 struct thermal_cooling_device *
 of_cpufreq_cooling_register(struct device_node *np,
 			    const struct cpumask *clip_cpus);
+
+/**
+ * of_cpufreq_power_cooling_register() - create cpufreq cooling device with power extensions
+ * @np:	a valid struct device_node to the cooling device device tree node
+ * @clip_cpus:	cpumask of cpus where the frequency constraints will happen
+ * @capacitance:	dynamic power coefficient for these cpus
+ * @plat_static_func:	function to calculate the static power consumed by these
+ *			cpus (optional)
+ */
+struct thermal_cooling_device *
+of_cpufreq_power_cooling_register(struct device_node *np,
+				const struct cpumask *clip_cpus,
+				u32 capacitance, get_static_t plat_static_func);
 #else
 static inline struct thermal_cooling_device *
 of_cpufreq_cooling_register(struct device_node *np,
@@ -52,6 +78,14 @@ of_cpufreq_cooling_register(struct device_node *np,
 {
 	return NULL;
 }
+
+struct thermal_cooling_device *
+of_cpufreq_power_cooling_register(struct device_node *np,
+				const struct cpumask *clip_cpus,
+				u32 capacitance, get_static_t plat_static_func)
+{
+	return NULL;
+}
 #endif
 
 /**
@@ -68,11 +102,24 @@ cpufreq_cooling_register(const struct cpumask *clip_cpus)
 	return NULL;
 }
 static inline struct thermal_cooling_device *
+cpufreq_power_cooling_register(const struct cpumask *clip_cpus,
+			u32 capacitance, get_static_t plat_static_func)
+{
+	return NULL;
+}
+static inline struct thermal_cooling_device *
 of_cpufreq_cooling_register(struct device_node *np,
 			    const struct cpumask *clip_cpus)
 {
 	return NULL;
 }
+static inline struct thermal_cooling_device *
+of_cpufreq_power_cooling_register(struct device_node *np,
+				const struct cpumask *clip_cpus,
+				u32 capacitance, get_static_t plat_static_func)
+{
+	return NULL;
+}
 static inline
 void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
 {
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [RFC PATCH v6 7/9] thermal: introduce the Power Allocator governor
  2014-12-05 19:04 [RFC PATCH v6 0/9] The power allocator thermal governor Javi Merino
                   ` (5 preceding siblings ...)
  2014-12-05 19:04 ` [RFC PATCH v6 6/9] thermal: cpu_cooling: implement the power cooling device API Javi Merino
@ 2014-12-05 19:04 ` Javi Merino
  2015-01-02 15:46   ` Eduardo Valentin
  2015-01-02 15:51   ` Eduardo Valentin
  2014-12-05 19:04 ` [RFC PATCH v6 8/9] thermal: add trace events to the power allocator governor Javi Merino
  2014-12-05 19:04 ` [RFC PATCH v6 9/9] of: thermal: Introduce sustainable power for a thermal zone Javi Merino
  8 siblings, 2 replies; 48+ messages in thread
From: Javi Merino @ 2014-12-05 19:04 UTC (permalink / raw)
  To: linux-pm, linux-kernel
  Cc: punit.agrawal, broonie, Javi Merino, Zhang Rui, Eduardo Valentin

The power allocator governor is a thermal governor that controls system
and device power allocation to control temperature.  Conceptually, the
implementation divides the sustainable power of a thermal zone among
all the heat sources in that zone.

This governor relies on "power actors", entities that represent heat
sources.  They can report current and maximum power consumption and
can set a given maximum power consumption, usually via a cooling
device.

The governor uses a Proportional Integral Derivative (PID) controller
driven by the temperature of the thermal zone.  The output of the
controller is a power budget that is then allocated to each power
actor that can have bearing on the temperature we are trying to
control.  It decides how much power to give each cooling device based
on the performance they are requesting.  The PID controller ensures
that the total power budget does not exceed the control temperature.

Cc: Zhang Rui <rui.zhang@intel.com>
Cc: Eduardo Valentin <edubezval@gmail.com>
Signed-off-by: Punit Agrawal <punit.agrawal@arm.com>
Signed-off-by: Javi Merino <javi.merino@arm.com>
---
 Documentation/thermal/power_allocator.txt | 196 ++++++++++++
 drivers/thermal/Kconfig                   |  15 +
 drivers/thermal/Makefile                  |   1 +
 drivers/thermal/power_allocator.c         | 511 ++++++++++++++++++++++++++++++
 drivers/thermal/thermal_core.c            |   7 +-
 drivers/thermal/thermal_core.h            |   8 +
 include/linux/thermal.h                   |  40 ++-
 7 files changed, 774 insertions(+), 4 deletions(-)
 create mode 100644 drivers/thermal/power_allocator.c

diff --git a/Documentation/thermal/power_allocator.txt b/Documentation/thermal/power_allocator.txt
index d3bb79050c27..23b684afdc75 100644
--- a/Documentation/thermal/power_allocator.txt
+++ b/Documentation/thermal/power_allocator.txt
@@ -1,3 +1,172 @@
+Power allocator governor tunables
+=================================
+
+Trip points
+-----------
+
+The governor requires the following two passive trip points:
+
+1.  "switch on" trip point: temperature above which the governor
+    control loop starts operating.
+2.  "desired temperature" trip point: it should be higher than the
+    "switch on" trip point. It is the target temperature the governor
+    is controlling for.
+
+PID Controller
+--------------
+
+The power allocator governor implements a
+Proportional-Integral-Derivative controller (PID controller) with
+temperature as the control input and power as the controlled output:
+
+    P_max = k_p * e + k_i * err_integral + k_d * diff_err + sustainable_power
+
+where
+    e = desired_temperature - current_temperature
+    err_integral is the sum of previous errors
+    diff_err = e - previous_error
+
+It is similar to the one depicted below:
+
+                                      k_d
+                                       |
+current_temp                           |
+     |                                 v
+     |                +----------+   +---+
+     |         +----->| diff_err |-->| X |------+
+     |         |      +----------+   +---+      |
+     |         |                                |      tdp        actor
+     |         |                      k_i       |       |    get_actual_power()
+     |         |                       |        |       |        |     |
+     |         |                       |        |       |        |     | ...
+     v         |                       v        v       v        v     v
+   +---+       |      +-------+      +---+    +---+   +---+   +----------+
+   | S |-------+----->| sum e |----->| X |--->| S |-->| S |-->|power     |
+   +---+       |      +-------+      +---+    +---+   +---+   |allocation|
+     ^         |                                ^             +----------+
+     |         |                                |                |     |
+     |         |        +---+                   |                |     |
+     |         +------->| X |-------------------+                v     v
+     |                  +---+                               granted performance
+desired_temperature       ^
+                          |
+                          |
+                      k_po/k_pu
+
+Sustainable power
+-----------------
+
+An estimate of the sustainable dissipatable power (in mW) should be
+provided while registering the thermal zone.  This estimates the
+sustained power that can be dissipated at the desired control
+temperature.  This is the maximum sustained power for allocation at
+the desired maximum temperature.  The actual sustained power can vary
+for a number of reasons.  The closed loop controller will take care of
+variations such as environmental conditions, and some factors related
+to the speed-grade of the silicon.  `sustainable_power` is therefore
+simply an estimate, and may be tuned to affect the aggressiveness of
+the thermal ramp.  For reference, this is 2000mW - 4500mW depending on
+screen size (4" phone - 10" tablet).
+
+If you are using device tree, do add it as a property of the
+thermal-zone.  For example:
+
+	thermal-zones {
+		soc_thermal {
+			polling-delay = <1000>;
+			polling-delay-passive = <100>;
+			sustainable-power = <2500>;
+			...
+
+If you use platform code to register your thermal zone instead, pass a
+`thermal_zone_params` that has a `sustainable_power`.  If you weren't
+passing any `thermal_zone_params`, then something like this will do:
+
+	static const struct thermal_zone_params tz_params = {
+		.sustainable_power = 3500,
+	};
+
+and then pass `tz_params` as the 5th parameter to
+`thermal_zone_device_register()`
+
+k_po and k_pu
+-------------
+
+The implementation of the PID controller in the power allocator
+thermal governor allows the configuration of two proportional term
+constants: `k_po` and `k_pu`.  `k_po` is the proportional term
+constant during temperature overshoot periods (current temperature is
+above "desired temperature" trip point).  Conversely, `k_pu` is the
+proportional term constant during temperature undershoot periods
+(current temperature below "desired temperature" trip point).
+
+These controls are intended as the primary mechanism for configuring
+the permitted thermal "ramp" of the system.  For instance, a lower
+`k_pu` value will provide a slower ramp, at the cost of capping
+available capacity at a low temperature.  On the other hand, a high
+value of `k_pu` will result in the governor granting very high power
+whilst temperature is low, and may lead to temperature overshooting.
+
+The default value for `k_pu` is:
+
+    2 * sustainable_power / (desired_temperature - switch_on_temp)
+
+This means that at `switch_on_temp` the output of the controller's
+proportional term will be 2 * `sustainable_power`.  The default value
+for `k_po` is:
+
+    sustainable_power / (desired_temperature - switch_on_temp)
+
+Focusing on the proportional and feed forward values of the PID
+controller equation we have:
+
+    P_max = k_p * e + sustainable_power
+
+The proportional term is proportional to the difference between the
+desired temperature and the current one.  When the current temperature
+is the desired one, then the proportional component is zero and
+`P_max` = `sustainable_power`.  That is, the system should operate in
+thermal equilibrium under constant load.  `sustainable_power` is only
+an estimate, which is the reason for closed-loop control such as this.
+
+Expanding `k_pu` we get:
+    P_max = 2 * sustainable_power * (T_set - T) / (T_set - T_on) +
+        sustainable_power
+
+where
+    T_set is the desired temperature
+    T is the current temperature
+    T_on is the switch on temperature
+
+When the current temperature is the switch_on temperature, the above
+formula becomes:
+
+    P_max = 2 * sustainable_power * (T_set - T_on) / (T_set - T_on) +
+        sustainable_power = 2 * sustainable_power + sustainable_power = 
+        3 * sustainable_power
+
+Therefore, the proportional term alone linearly decreases power from
+3 * `sustainable_power` to `sustainable_power` as the temperature
+rises from the switch on temperature to the desired temperature.
+
+k_i and integral_cutoff
+-----------------------
+
+`k_i` configures the PID loop's integral term constant.  This term
+allows the PID controller to compensate for long term drift and for
+the quantized nature of the output control: cooling devices can't set
+the exact power that the governor requests.  When the temperature
+error is below `integral_cutoff`, errors are accumulated in the
+integral term.  This term is then multiplied by `k_i` and the result
+added to the output of the controller.  Typically `k_i` is set low (1
+or 2) and `integral_cutoff` is 0.
+
+k_d
+---
+
+`k_d` configures the PID loop's derivative term constant.  It's
+recommended to leave it as the default: 0.
+
 Cooling device power API
 ========================
 
@@ -25,3 +194,30 @@ milliwatts.
 
 Calculate a cooling device state that would make the device consume at
 most @power mW.
+
+Cooling device weights
+----------------------
+
+Weights are a mechanism to bias the allocation between cooling
+devices.  They express the relative power efficiency of different
+cooling devices.  Higher weight can be used to express higher power
+efficiency.  Weighting is relative such that if each cooling device
+has a weight of one they are considered equal.  This is particularly
+useful in heterogeneous systems where two cooling devices may perform
+the same kind of compute, but with different efficiency.  For example,
+a system with two different types of processors.
+
+Weights shall be passed as part of the thermal zone's
+`thermal_bind_parameters`.
+
+Limitations of the power allocator governor
+===========================================
+
+The power allocator governor's PID controller works best if there is a
+periodic tick.  If you have a driver that calls
+`thermal_zone_device_update()` (or anything that ends up calling the
+governor's `throttle()` function) repetitively, the governor response
+won't be very good.  Note that this is not particular to this
+governor, step-wise will also misbehave if you call its throttle()
+faster than the normal thermal framework tick (due to interrupts for
+example) as it will overreact.
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index f554d25b4399..4496fa5e4a33 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -71,6 +71,14 @@ config THERMAL_DEFAULT_GOV_USER_SPACE
 	  Select this if you want to let the user space manage the
 	  platform thermals.
 
+config THERMAL_DEFAULT_GOV_POWER_ALLOCATOR
+	bool "power_allocator"
+	select THERMAL_GOV_POWER_ALLOCATOR
+	help
+	  Select this if you want to control temperature based on
+	  system and device power allocation. This governor relies on
+	  power actors to operate.
+
 endchoice
 
 config THERMAL_GOV_FAIR_SHARE
@@ -99,6 +107,13 @@ config THERMAL_GOV_USER_SPACE
 	help
 	  Enable this to let the user space manage the platform thermals.
 
+config THERMAL_GOV_POWER_ALLOCATOR
+	bool "Power allocator thermal governor"
+	select THERMAL_POWER_ACTOR
+	help
+	  Enable this to manage platform thermals by dynamically
+	  allocating and limiting power to devices.
+
 config CPU_THERMAL
 	bool "generic cpu cooling support"
 	depends on CPU_FREQ
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 39c4fe87da2f..c33904848c45 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -14,6 +14,7 @@ thermal_sys-$(CONFIG_THERMAL_GOV_FAIR_SHARE)	+= fair_share.o
 thermal_sys-$(CONFIG_THERMAL_GOV_BANG_BANG)	+= gov_bang_bang.o
 thermal_sys-$(CONFIG_THERMAL_GOV_STEP_WISE)	+= step_wise.o
 thermal_sys-$(CONFIG_THERMAL_GOV_USER_SPACE)	+= user_space.o
+thermal_sys-$(CONFIG_THERMAL_GOV_POWER_ALLOCATOR)	+= power_allocator.o
 
 # cpufreq cooling
 thermal_sys-$(CONFIG_CPU_THERMAL)	+= cpu_cooling.o
diff --git a/drivers/thermal/power_allocator.c b/drivers/thermal/power_allocator.c
new file mode 100644
index 000000000000..09e98991efbb
--- /dev/null
+++ b/drivers/thermal/power_allocator.c
@@ -0,0 +1,511 @@
+/*
+ * A power allocator to manage temperature
+ *
+ * Copyright (C) 2014 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "Power allocator: " fmt
+
+#include <linux/rculist.h>
+#include <linux/slab.h>
+#include <linux/thermal.h>
+
+#include "thermal_core.h"
+
+#define FRAC_BITS 10
+#define int_to_frac(x) ((x) << FRAC_BITS)
+#define frac_to_int(x) ((x) >> FRAC_BITS)
+
+/**
+ * mul_frac() - multiply two fixed-point numbers
+ * @x:	first multiplicand
+ * @y:	second multiplicand
+ *
+ * Return: the result of multiplying two fixed-point numbers.  The
+ * result is also a fixed-point number.
+ */
+static inline s64 mul_frac(s64 x, s64 y)
+{
+	return (x * y) >> FRAC_BITS;
+}
+
+enum power_allocator_trip_levels {
+	TRIP_SWITCH_ON = 0,	/* Switch on PID controller */
+	TRIP_MAX_DESIRED_TEMPERATURE, /* Temperature we are controlling for */
+
+	THERMAL_TRIP_NUM,
+};
+
+/**
+ * struct power_allocator_params - parameters for the power allocator governor
+ * @k_po:	Proportional parameter of the PID controller when overshooting
+ *		(i.e., when temperature is below the target)
+ * @k_pu:	Proportional parameter of the PID controller when undershooting
+ * @k_i:	Integral parameter of the PID controller
+ * @k_d:	Derivative parameter of the PID controller
+ * @integral_cutoff:	threshold below which the error is no longer accumulated
+			in the PID controller
+ * @err_integral:	accumulated error in the PID controller.
+ * @prev_err:	error in the previous iteration of the PID controller.
+ *		Used to calculate the derivative term.
+ */
+struct power_allocator_params {
+	s32 k_po;
+	s32 k_pu;
+	s32 k_i;
+	s32 k_d;
+	s32 integral_cutoff;
+	s64 err_integral;
+	s32 prev_err;
+};
+
+/**
+ * get_actor_weight() - get the weight for the power actor
+ * @tz:		thermal zone we are operating in
+ * @actor:	the power actor
+ *
+ * Returns: The weight inside the thermal binding parameters of the
+ * thermal zone.  If it could not be found, a default weight of 1 is
+ * assumed.  Weights are expressed as a FRAC_BITS (currently 10-bit)
+ * fixed point integer.
+ */
+static int get_actor_weight(struct thermal_zone_device *tz,
+			struct thermal_cooling_device *cdev)
+{
+	int i;
+
+	for (i = 0; i < tz->tzp->num_tbps; i++)
+		if (tz->tzp->tbp[i].cdev == cdev)
+			return tz->tzp->tbp[i].weight;
+
+	return int_to_frac(1);
+}
+
+/**
+ * pid_controller() - PID controller
+ * @tz:	thermal zone we are operating in
+ * @current_temp:	the current temperature in millicelsius
+ * @control_temp:	the target temperature in millicelsius
+ * @max_allocatable_power:	maximum allocatable power for this thermal zone
+ *
+ * This PID controller increases the available power budget so that the
+ * temperature of the thermal zone gets as close as possible to
+ * @control_temp and limits the power if it exceeds it.  k_po is the
+ * proportional term when we are overshooting, k_pu is the
+ * proportional term when we are undershooting.  integral_cutoff is a
+ * threshold below which we stop accumulating the error.  The
+ * accumulated error is only valid if the requested power will make
+ * the system warmer.  If the system is mostly idle, there's no point
+ * in accumulating positive error.
+ *
+ * Return: The power budget for the next period.
+ */
+static u32 pid_controller(struct thermal_zone_device *tz,
+			unsigned long current_temp, unsigned long control_temp,
+			u32 max_allocatable_power)
+{
+	s64 p, i, d, power_range;
+	s32 err, max_power_frac;
+	struct power_allocator_params *params = tz->governor_data;
+
+	max_power_frac = int_to_frac(max_allocatable_power);
+
+	err = ((s32)control_temp - (s32)current_temp);
+	err = int_to_frac(err);
+
+	/* Calculate the proportional term */
+	p = mul_frac(err < 0 ? params->k_po : params->k_pu, err);
+
+	/*
+	 * Calculate the integral term
+	 *
+	 * if the error is less than cut off allow integration (but
+	 * the integral is limited to max power)
+	 */
+	i = mul_frac(params->k_i, params->err_integral);
+
+	if (err < int_to_frac(params->integral_cutoff)) {
+		s64 i_next = i + mul_frac(params->k_i, err);
+
+		if (abs64(i_next) < max_power_frac) {
+			i = i_next;
+			params->err_integral += err;
+		}
+	}
+
+	/*
+	 * Calculate the derivative term
+	 *
+	 * We do err - prev_err, so with a positive k_d, a decreasing
+	 * error (i.e. driving closer to the line) results in less
+	 * power being applied, slowing down the controller)
+	 */
+	d = mul_frac(params->k_d, err - params->prev_err);
+	params->prev_err = err;
+
+	power_range = p + i + d;
+
+	/* feed-forward the known sustainable dissipatable power */
+	power_range = tz->tzp->sustainable_power + frac_to_int(power_range);
+
+	return clamp(power_range, (s64)0, (s64)max_allocatable_power);
+}
+
+/**
+ * divvy_up_power() - divvy the allocated power between the actors
+ * @req_power:	each actor's requested power
+ * @max_power:	each actor's maximum available power
+ * @num_actors:	size of the @req_power, @max_power and @granted_power's array
+ * @total_req_power: sum of @req_power
+ * @power_range:	total allocated power
+ * @granted_power:	output array: each actor's granted power
+ *
+ * This function divides the total allocated power (@power_range)
+ * fairly between the actors.  It first tries to give each actor a
+ * share of the @power_range according to how much power it requested
+ * compared to the rest of the actors.  For example, if only one actor
+ * requests power, then it receives all the @power_range.  If
+ * three actors each requests 1mW, each receives a third of the
+ * @power_range.
+ *
+ * If any actor received more than their maximum power, then that
+ * surplus is re-divvied among the actors based on how far they are
+ * from their respective maximums.
+ *
+ * Granted power for each actor is written to @granted_power, which
+ * should've been allocated by the calling function.
+ */
+static void divvy_up_power(u32 *req_power, u32 *max_power, int num_actors,
+			u32 total_req_power, u32 power_range,
+			u32 *granted_power)
+{
+	u32 extra_power, capped_extra_power, extra_actor_power[num_actors];
+	int i;
+
+	if (!total_req_power) {
+		/*
+		 * Nobody requested anything, so just give everybody
+		 * the maximum power
+		 */
+		for (i = 0; i < num_actors; i++)
+			granted_power[i] = max_power[i];
+
+		return;
+	}
+
+	capped_extra_power = 0;
+	extra_power = 0;
+	for (i = 0; i < num_actors; i++) {
+		u64 req_range = req_power[i] * power_range;
+
+		granted_power[i] = div_u64(req_range, total_req_power);
+
+		if (granted_power[i] > max_power[i]) {
+			extra_power += granted_power[i] - max_power[i];
+			granted_power[i] = max_power[i];
+		}
+
+		extra_actor_power[i] = max_power[i] - granted_power[i];
+		capped_extra_power += extra_actor_power[i];
+	}
+
+	if (!extra_power)
+		return;
+
+	/*
+	 * Re-divvy the reclaimed extra among actors based on
+	 * how far they are from the max
+	 */
+	extra_power = min(extra_power, capped_extra_power);
+	if (capped_extra_power > 0)
+		for (i = 0; i < num_actors; i++)
+			granted_power[i] += (extra_actor_power[i] *
+					extra_power) / capped_extra_power;
+}
+
+static int allocate_power(struct thermal_zone_device *tz,
+			unsigned long current_temp, unsigned long control_temp)
+{
+	struct thermal_instance *instance;
+	u32 *req_power, *max_power, *granted_power;
+	u32 total_req_power, max_allocatable_power;
+	u32 power_range;
+	int i, num_actors, ret = 0;
+
+	mutex_lock(&tz->lock);
+
+	num_actors = 0;
+	list_for_each_entry(instance, &tz->thermal_instances, tz_node)
+		if ((instance->trip == TRIP_MAX_DESIRED_TEMPERATURE) &&
+			cdev_is_power_actor(instance->cdev))
+			num_actors++;
+
+	req_power = devm_kcalloc(&tz->device, num_actors, sizeof(*req_power),
+				GFP_KERNEL);
+	if (!req_power) {
+		ret = -ENOMEM;
+		goto unlock;
+	}
+
+	max_power = devm_kcalloc(&tz->device, num_actors, sizeof(*max_power),
+				GFP_KERNEL);
+	if (!max_power) {
+		ret = -ENOMEM;
+		goto free_req_power;
+	}
+
+	granted_power = devm_kcalloc(&tz->device, num_actors,
+				sizeof(*granted_power), GFP_KERNEL);
+	if (!granted_power) {
+		ret = -ENOMEM;
+		goto free_max_power;
+	}
+
+	i = 0;
+	total_req_power = 0;
+	max_allocatable_power = 0;
+
+	list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
+		int weight;
+		struct thermal_cooling_device *cdev = instance->cdev;
+
+		if (instance->trip != TRIP_MAX_DESIRED_TEMPERATURE)
+			continue;
+
+		if (!cdev_is_power_actor(cdev))
+			continue;
+
+		req_power[i] = cdev->ops->get_actual_power(cdev);
+		weight = get_actor_weight(tz, cdev);
+		req_power[i] = frac_to_int(weight * req_power[i]);
+		total_req_power += req_power[i];
+
+		max_power[i] = power_actor_get_max_power(cdev);
+		max_allocatable_power += max_power[i];
+
+		i++;
+	}
+
+	power_range = pid_controller(tz, current_temp, control_temp,
+				max_allocatable_power);
+
+	divvy_up_power(req_power, max_power, num_actors, total_req_power,
+		power_range, granted_power);
+
+	i = 0;
+	list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
+		if (instance->trip != TRIP_MAX_DESIRED_TEMPERATURE)
+			continue;
+
+		if (!cdev_is_power_actor(instance->cdev))
+			continue;
+
+		power_actor_set_power(instance->cdev, granted_power[i]);
+
+		i++;
+	}
+
+	devm_kfree(&tz->device, granted_power);
+free_max_power:
+	devm_kfree(&tz->device, max_power);
+free_req_power:
+	devm_kfree(&tz->device, req_power);
+unlock:
+	mutex_unlock(&tz->lock);
+
+	return ret;
+}
+
+static int check_trips(struct thermal_zone_device *tz)
+{
+	int ret;
+	enum thermal_trip_type type;
+
+	if (tz->trips < THERMAL_TRIP_NUM)
+		return -EINVAL;
+
+	ret = tz->ops->get_trip_type(tz, TRIP_SWITCH_ON, &type);
+	if (ret)
+		return ret;
+
+	if (type != THERMAL_TRIP_PASSIVE)
+		return -EINVAL;
+
+	ret = tz->ops->get_trip_type(tz, TRIP_MAX_DESIRED_TEMPERATURE, &type);
+	if (ret)
+		return ret;
+
+	if (type != THERMAL_TRIP_PASSIVE)
+		return -EINVAL;
+
+	return ret;
+}
+
+static void reset_pid_controller(struct power_allocator_params *params)
+{
+	params->err_integral = 0;
+	params->prev_err = 0;
+}
+
+static void allow_maximum_power(struct thermal_zone_device *tz)
+{
+	struct thermal_instance *instance;
+
+	list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
+		u32 max_power;
+
+		if ((instance->trip != TRIP_MAX_DESIRED_TEMPERATURE) ||
+			(!cdev_is_power_actor(instance->cdev)))
+			continue;
+
+		max_power = power_actor_get_max_power(instance->cdev);
+		power_actor_set_power(instance->cdev, max_power);
+	}
+}
+
+/**
+ * power_allocator_bind() - bind the power_allocator governor to a thermal zone
+ * @tz:	thermal zone to bind it to
+ *
+ * Check that the thermal zone is valid for this governor, that is, it
+ * has two thermal trips.  If so, initialize the PID controller
+ * parameters and bind it to the thermal zone.
+ *
+ * Return: 0 on success, -EINVAL if the trips were invalid or -ENOMEM
+ * if we ran out of memory.
+ */
+static int power_allocator_bind(struct thermal_zone_device *tz)
+{
+	int ret;
+	struct power_allocator_params *params;
+	unsigned long switch_on_temp, control_temp;
+	u32 temperature_threshold;
+
+	ret = check_trips(tz);
+	if (ret) {
+		dev_err(&tz->device,
+			"thermal zone %s has the wrong number of trips for this governor\n",
+			tz->type);
+		return ret;
+	}
+
+	if (!tz->tzp || !tz->tzp->sustainable_power) {
+		dev_err(&tz->device,
+			"power_allocator: missing sustainable_power\n");
+		return -EINVAL;
+	}
+
+	params = devm_kzalloc(&tz->device, sizeof(*params), GFP_KERNEL);
+	if (!params)
+		return -ENOMEM;
+
+	ret = tz->ops->get_trip_temp(tz, TRIP_SWITCH_ON, &switch_on_temp);
+	if (ret)
+		goto free;
+
+	ret = tz->ops->get_trip_temp(tz, TRIP_MAX_DESIRED_TEMPERATURE,
+				&control_temp);
+	if (ret)
+		goto free;
+
+	temperature_threshold = control_temp - switch_on_temp;
+
+	params->k_po = tz->tzp->k_po ?:
+		int_to_frac(tz->tzp->sustainable_power) / temperature_threshold;
+	params->k_pu = tz->tzp->k_pu ?:
+		int_to_frac(2 * tz->tzp->sustainable_power) /
+		temperature_threshold;
+	params->k_i = tz->tzp->k_i ?: int_to_frac(10) / 1000;
+	params->k_d = tz->tzp->k_d ?: int_to_frac(0);
+	params->integral_cutoff = tz->tzp->integral_cutoff ?: 0;
+
+	reset_pid_controller(params);
+
+	tz->governor_data = params;
+
+	return 0;
+
+free:
+	devm_kfree(&tz->device, params);
+	return ret;
+}
+
+static void power_allocator_unbind(struct thermal_zone_device *tz)
+{
+	dev_dbg(&tz->device, "Unbinding from thermal zone %d\n", tz->id);
+	devm_kfree(&tz->device, tz->governor_data);
+	tz->governor_data = NULL;
+}
+
+static int power_allocator_throttle(struct thermal_zone_device *tz, int trip)
+{
+	int ret;
+	unsigned long switch_on_temp, control_temp, current_temp;
+	struct power_allocator_params *params = tz->governor_data;
+
+	/*
+	 * We get called for every trip point but we only need to do
+	 * our calculations once
+	 */
+	if (trip != TRIP_MAX_DESIRED_TEMPERATURE)
+		return 0;
+
+	ret = thermal_zone_get_temp(tz, &current_temp);
+	if (ret) {
+		dev_warn(&tz->device, "Failed to get temperature: %d\n", ret);
+		return ret;
+	}
+
+	ret = tz->ops->get_trip_temp(tz, TRIP_SWITCH_ON, &switch_on_temp);
+	if (ret) {
+		dev_warn(&tz->device,
+			"Failed to get switch on temperature: %d\n", ret);
+		return ret;
+	}
+
+	if (current_temp < switch_on_temp) {
+		tz->passive = 0;
+		reset_pid_controller(params);
+		allow_maximum_power(tz);
+		return 0;
+	}
+
+	tz->passive = 1;
+
+	ret = tz->ops->get_trip_temp(tz, TRIP_MAX_DESIRED_TEMPERATURE,
+				&control_temp);
+	if (ret) {
+		dev_warn(&tz->device,
+			"Failed to get the maximum desired temperature: %d\n",
+			ret);
+		return ret;
+	}
+
+	return allocate_power(tz, current_temp, control_temp);
+}
+
+static struct thermal_governor thermal_gov_power_allocator = {
+	.name		= "power_allocator",
+	.bind_to_tz	= power_allocator_bind,
+	.unbind_from_tz	= power_allocator_unbind,
+	.throttle	= power_allocator_throttle,
+};
+
+int thermal_gov_power_allocator_register(void)
+{
+	return thermal_register_governor(&thermal_gov_power_allocator);
+}
+
+void thermal_gov_power_allocator_unregister(void)
+{
+	thermal_unregister_governor(&thermal_gov_power_allocator);
+}
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index c490f262ea7f..4921e084c20b 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -1905,7 +1905,11 @@ static int __init thermal_register_governors(void)
 	if (result)
 		return result;
 
-	return thermal_gov_user_space_register();
+	result = thermal_gov_user_space_register();
+	if (result)
+		return result;
+
+	return thermal_gov_power_allocator_register();
 }
 
 static void thermal_unregister_governors(void)
@@ -1914,6 +1918,7 @@ static void thermal_unregister_governors(void)
 	thermal_gov_fair_share_unregister();
 	thermal_gov_bang_bang_unregister();
 	thermal_gov_user_space_unregister();
+	thermal_gov_power_allocator_unregister();
 }
 
 static int __init thermal_init(void)
diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h
index d15d243de27a..b907be823527 100644
--- a/drivers/thermal/thermal_core.h
+++ b/drivers/thermal/thermal_core.h
@@ -85,6 +85,14 @@ static inline int thermal_gov_user_space_register(void) { return 0; }
 static inline void thermal_gov_user_space_unregister(void) {}
 #endif /* CONFIG_THERMAL_GOV_USER_SPACE */
 
+#ifdef CONFIG_THERMAL_GOV_POWER_ALLOCATOR
+int thermal_gov_power_allocator_register(void);
+void thermal_gov_power_allocator_unregister(void);
+#else
+static inline int thermal_gov_power_allocator_register(void) { return 0; }
+static inline void thermal_gov_power_allocator_unregister(void) {}
+#endif /* CONFIG_THERMAL_GOV_POWER_ALLOCATOR */
+
 /* device tree support */
 #ifdef CONFIG_THERMAL_OF
 int of_parse_thermal_zones(void);
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 1155457caf52..b23e019b1761 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -61,6 +61,8 @@
 #define DEFAULT_THERMAL_GOVERNOR       "fair_share"
 #elif defined(CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE)
 #define DEFAULT_THERMAL_GOVERNOR       "user_space"
+#elif defined(CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR)
+#define DEFAULT_THERMAL_GOVERNOR       "power_allocator"
 #endif
 
 struct thermal_zone_device;
@@ -255,9 +257,14 @@ struct thermal_bind_params {
 
 	/*
 	 * This is a measure of 'how effectively these devices can
-	 * cool 'this' thermal zone. The shall be determined by platform
-	 * characterization. This is on a 'percentage' scale.
-	 * See Documentation/thermal/sysfs-api.txt for more information.
+	 * cool 'this' thermal zone. The shall be determined by
+	 * platform characterization. For the fair-share governor,
+	 * this is on a 'percentage' scale.  See
+	 * Documentation/thermal/sysfs-api.txt for more
+	 * information. For the power_allocator governor, they are
+	 * relative to each other, see
+	 * Documentation/thermal/power_allocator.txt for more
+	 * information.
 	 */
 	int weight;
 
@@ -294,6 +301,33 @@ struct thermal_zone_params {
 
 	int num_tbps;	/* Number of tbp entries */
 	struct thermal_bind_params *tbp;
+
+	/*
+	 * Sustainable power (heat) that this thermal zone can dissipate in
+	 * mW
+	 */
+	u32 sustainable_power;
+
+	/*
+	 * Proportional parameter of the PID controller when
+	 * overshooting (i.e., when temperature is below the target)
+	 */
+	s32 k_po;
+
+	/*
+	 * Proportional parameter of the PID controller when
+	 * undershooting
+	 */
+	s32 k_pu;
+
+	/* Integral parameter of the PID controller */
+	s32 k_i;
+
+	/* Derivative parameter of the PID controller */
+	s32 k_d;
+
+	/* threshold below which the error is no longer accumulated */
+	s32 integral_cutoff;
 };
 
 struct thermal_genl_event {
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [RFC PATCH v6 8/9] thermal: add trace events to the power allocator governor
  2014-12-05 19:04 [RFC PATCH v6 0/9] The power allocator thermal governor Javi Merino
                   ` (6 preceding siblings ...)
  2014-12-05 19:04 ` [RFC PATCH v6 7/9] thermal: introduce the Power Allocator governor Javi Merino
@ 2014-12-05 19:04 ` Javi Merino
  2014-12-05 19:04 ` [RFC PATCH v6 9/9] of: thermal: Introduce sustainable power for a thermal zone Javi Merino
  8 siblings, 0 replies; 48+ messages in thread
From: Javi Merino @ 2014-12-05 19:04 UTC (permalink / raw)
  To: linux-pm, linux-kernel
  Cc: punit.agrawal, broonie, Javi Merino, Zhang Rui, Eduardo Valentin,
	Steven Rostedt, Frederic Weisbecker, Ingo Molnar

Add trace events for the power allocator governor and the power actor
interface of the cpu cooling device.

Cc: Zhang Rui <rui.zhang@intel.com>
Cc: Eduardo Valentin <edubezval@gmail.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@redhat.com>
Signed-off-by: Javi Merino <javi.merino@arm.com>
---
 drivers/thermal/cpu_cooling.c                  |  26 ++++-
 drivers/thermal/power_allocator.c              |  21 +++-
 include/trace/events/thermal_power_allocator.h | 138 +++++++++++++++++++++++++
 3 files changed, 182 insertions(+), 3 deletions(-)
 create mode 100644 include/trace/events/thermal_power_allocator.h

diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
index 335d95dd7e5a..f4d453429742 100644
--- a/drivers/thermal/cpu_cooling.c
+++ b/drivers/thermal/cpu_cooling.c
@@ -29,6 +29,8 @@
 #include <linux/cpu.h>
 #include <linux/cpu_cooling.h>
 
+#include <trace/events/thermal_power_allocator.h>
+
 /**
  * struct power_table - frequency to power conversion
  * @frequency:	frequency in KHz
@@ -644,12 +646,20 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
 static u32 cpufreq_get_actual_power(struct thermal_cooling_device *cdev)
 {
 	unsigned long freq;
-	int cpu;
+	int i = 0, cpu;
 	u32 static_power, dynamic_power, total_load = 0;
 	struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
+	u32 *load_cpu = NULL;
 
 	freq = cpufreq_quick_get(cpumask_any(&cpufreq_device->allowed_cpus));
 
+	if (trace_thermal_power_cpu_get_power_enabled()) {
+		u32 ncpus = cpumask_weight(&cpufreq_device->allowed_cpus);
+
+		load_cpu = devm_kcalloc(&cdev->device, ncpus, sizeof(*load_cpu),
+					GFP_KERNEL);
+	}
+
 	for_each_cpu(cpu, &cpufreq_device->allowed_cpus) {
 		u32 load;
 
@@ -659,6 +669,10 @@ static u32 cpufreq_get_actual_power(struct thermal_cooling_device *cdev)
 			load = 0;
 
 		total_load += load;
+		if (trace_thermal_power_cpu_limit_enabled() && load_cpu)
+			load_cpu[i] = load;
+
+		i++;
 	}
 
 	cpufreq_device->last_load = total_load;
@@ -666,6 +680,14 @@ static u32 cpufreq_get_actual_power(struct thermal_cooling_device *cdev)
 	static_power = get_static_power(cpufreq_device, freq);
 	dynamic_power = get_dynamic_power(cpufreq_device, freq);
 
+	if (trace_thermal_power_cpu_limit_enabled() && load_cpu) {
+		trace_thermal_power_cpu_get_power(
+			&cpufreq_device->allowed_cpus,
+			freq, load_cpu, i, dynamic_power, static_power);
+
+		devm_kfree(&cdev->device, load_cpu);
+	}
+
 	return static_power + dynamic_power;
 }
 
@@ -730,6 +752,8 @@ static unsigned long cpufreq_power2state(struct thermal_cooling_device *cdev,
 		return 0;
 	}
 
+	trace_thermal_power_cpu_limit(&cpufreq_device->allowed_cpus,
+					target_freq, cdev_state, power);
 	return cdev_state;
 }
 
diff --git a/drivers/thermal/power_allocator.c b/drivers/thermal/power_allocator.c
index 09e98991efbb..fa725a36872e 100644
--- a/drivers/thermal/power_allocator.c
+++ b/drivers/thermal/power_allocator.c
@@ -19,6 +19,9 @@
 #include <linux/slab.h>
 #include <linux/thermal.h>
 
+#define CREATE_TRACE_POINTS
+#include <trace/events/thermal_power_allocator.h>
+
 #include "thermal_core.h"
 
 #define FRAC_BITS 10
@@ -157,7 +160,14 @@ static u32 pid_controller(struct thermal_zone_device *tz,
 	/* feed-forward the known sustainable dissipatable power */
 	power_range = tz->tzp->sustainable_power + frac_to_int(power_range);
 
-	return clamp(power_range, (s64)0, (s64)max_allocatable_power);
+	power_range = clamp(power_range, (s64)0, (s64)max_allocatable_power);
+
+	trace_thermal_power_allocator_pid(frac_to_int(err),
+					frac_to_int(params->err_integral),
+					frac_to_int(p), frac_to_int(i),
+					frac_to_int(d), power_range);
+
+	return power_range;
 }
 
 /**
@@ -238,7 +248,7 @@ static int allocate_power(struct thermal_zone_device *tz,
 	struct thermal_instance *instance;
 	u32 *req_power, *max_power, *granted_power;
 	u32 total_req_power, max_allocatable_power;
-	u32 power_range;
+	u32 total_granted_power, power_range;
 	int i, num_actors, ret = 0;
 
 	mutex_lock(&tz->lock);
@@ -301,6 +311,7 @@ static int allocate_power(struct thermal_zone_device *tz,
 	divvy_up_power(req_power, max_power, num_actors, total_req_power,
 		power_range, granted_power);
 
+	total_granted_power = 0;
 	i = 0;
 	list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
 		if (instance->trip != TRIP_MAX_DESIRED_TEMPERATURE)
@@ -310,10 +321,16 @@ static int allocate_power(struct thermal_zone_device *tz,
 			continue;
 
 		power_actor_set_power(instance->cdev, granted_power[i]);
+		total_granted_power += granted_power[i];
 
 		i++;
 	}
 
+	trace_thermal_power_allocator(req_power, total_req_power, granted_power,
+				total_granted_power, num_actors, power_range,
+				max_allocatable_power, current_temp,
+				(s32)control_temp - (s32)current_temp);
+
 	devm_kfree(&tz->device, granted_power);
 free_max_power:
 	devm_kfree(&tz->device, max_power);
diff --git a/include/trace/events/thermal_power_allocator.h b/include/trace/events/thermal_power_allocator.h
new file mode 100644
index 000000000000..6760ec31de29
--- /dev/null
+++ b/include/trace/events/thermal_power_allocator.h
@@ -0,0 +1,138 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM thermal_power_allocator
+
+#if !defined(_TRACE_THERMAL_POWER_ALLOCATOR_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_THERMAL_POWER_ALLOCATOR_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(thermal_power_allocator,
+	TP_PROTO(u32 *req_power, u32 total_req_power, u32 *granted_power,
+		u32 total_granted_power, size_t num_actors, u32 power_range,
+		u32 max_allocatable_power, unsigned long current_temp,
+		s32 delta_temp),
+	TP_ARGS(req_power, total_req_power, granted_power, total_granted_power,
+		num_actors, power_range, max_allocatable_power, current_temp,
+		delta_temp),
+	TP_STRUCT__entry(
+		__dynamic_array(u32,   req_power, num_actors    )
+		__field(u32,           total_req_power          )
+		__dynamic_array(u32,   granted_power, num_actors)
+		__field(u32,           total_granted_power      )
+		__field(size_t,        num_actors               )
+		__field(u32,           power_range              )
+		__field(u32,           max_allocatable_power    )
+		__field(unsigned long, current_temp             )
+		__field(s32,           delta_temp               )
+	),
+	TP_fast_assign(
+		memcpy(__get_dynamic_array(req_power), req_power,
+			num_actors * sizeof(*req_power));
+		__entry->total_req_power = total_req_power;
+		memcpy(__get_dynamic_array(granted_power), granted_power,
+			num_actors * sizeof(*granted_power));
+		__entry->total_granted_power = total_granted_power;
+		__entry->num_actors = num_actors;
+		__entry->power_range = power_range;
+		__entry->max_allocatable_power = max_allocatable_power;
+		__entry->current_temp = current_temp;
+		__entry->delta_temp = delta_temp;
+	),
+
+	TP_printk("req_power={%s} total_req_power=%u granted_power={%s} total_granted_power=%u power_range=%u max_allocatable_power=%u current_temperature=%lu delta_temperature=%d",
+		__print_u32_array(__get_dynamic_array(req_power),
+				__entry->num_actors),
+		__entry->total_req_power,
+		__print_u32_array(__get_dynamic_array(granted_power),
+				__entry->num_actors),
+		__entry->total_granted_power, __entry->power_range,
+		__entry->max_allocatable_power, __entry->current_temp,
+		__entry->delta_temp)
+);
+
+TRACE_EVENT(thermal_power_allocator_pid,
+	TP_PROTO(s32 err, s32 err_integral, s64 p, s64 i, s64 d, s32 output),
+	TP_ARGS(err, err_integral, p, i, d, output),
+	TP_STRUCT__entry(
+		__field(s32, err         )
+		__field(s32, err_integral)
+		__field(s64, p           )
+		__field(s64, i           )
+		__field(s64, d           )
+		__field(s32, output      )
+	),
+	TP_fast_assign(
+		__entry->err = err;
+		__entry->err_integral = err_integral;
+		__entry->p = p;
+		__entry->i = i;
+		__entry->d = d;
+		__entry->output = output;
+	),
+
+	TP_printk("err=%d err_integral=%d p=%lld i=%lld d=%lld output=%d",
+		__entry->err, __entry->err_integral,
+		__entry->p, __entry->i, __entry->d, __entry->output)
+);
+
+TRACE_EVENT(thermal_power_cpu_get_power,
+	TP_PROTO(const struct cpumask *cpus, unsigned long freq, u32 *load,
+		size_t load_len, u32 dynamic_power, u32 static_power),
+
+	TP_ARGS(cpus, freq, load, load_len, dynamic_power, static_power),
+
+	TP_STRUCT__entry(
+		__bitmask(cpumask, num_possible_cpus())
+		__field(unsigned long, freq          )
+		__dynamic_array(u32,   load, load_len)
+		__field(size_t,        load_len      )
+		__field(u32,           dynamic_power )
+		__field(u32,           static_power  )
+	),
+
+	TP_fast_assign(
+		__assign_bitmask(cpumask, cpumask_bits(cpus),
+				num_possible_cpus());
+		__entry->freq = freq;
+		memcpy(__get_dynamic_array(load), load,
+			load_len * sizeof(*load));
+		__entry->load_len = load_len;
+		__entry->dynamic_power = dynamic_power;
+		__entry->static_power = static_power;
+	),
+
+	TP_printk("cpus=%s freq=%lu load={%s} dynamic_power=%d static_power=%d",
+		__get_bitmask(cpumask), __entry->freq,
+		__print_u32_array(__get_dynamic_array(load), __entry->load_len),
+		__entry->dynamic_power, __entry->static_power)
+);
+
+TRACE_EVENT(thermal_power_cpu_limit,
+	TP_PROTO(const struct cpumask *cpus, unsigned int freq,
+		unsigned long cdev_state, u32 power),
+
+	TP_ARGS(cpus, freq, cdev_state, power),
+
+	TP_STRUCT__entry(
+		__bitmask(cpumask, num_possible_cpus())
+		__field(unsigned int,  freq      )
+		__field(unsigned long, cdev_state)
+		__field(u32,           power     )
+	),
+
+	TP_fast_assign(
+		__assign_bitmask(cpumask, cpumask_bits(cpus),
+				num_possible_cpus());
+		__entry->freq = freq;
+		__entry->cdev_state = cdev_state;
+		__entry->power = power;
+	),
+
+	TP_printk("cpus=%s freq=%u cdev_state=%lu power=%u",
+		__get_bitmask(cpumask), __entry->freq, __entry->cdev_state,
+		__entry->power)
+);
+#endif /* _TRACE_THERMAL_POWER_ALLOCATOR_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [RFC PATCH v6 9/9] of: thermal: Introduce sustainable power for a thermal zone
  2014-12-05 19:04 [RFC PATCH v6 0/9] The power allocator thermal governor Javi Merino
                   ` (7 preceding siblings ...)
  2014-12-05 19:04 ` [RFC PATCH v6 8/9] thermal: add trace events to the power allocator governor Javi Merino
@ 2014-12-05 19:04 ` Javi Merino
  2015-01-02 15:53   ` Eduardo Valentin
  8 siblings, 1 reply; 48+ messages in thread
From: Javi Merino @ 2014-12-05 19:04 UTC (permalink / raw)
  To: linux-pm, linux-kernel
  Cc: punit.agrawal, broonie, Zhang Rui, Eduardo Valentin

From: Punit Agrawal <punit.agrawal@arm.com>

Introduce an optional property called, sustainable-power, which
represents the power (in mW) which the thermal zone can safely
dissipate.

If provided the property is parsed and associated with the thermal
zone via the thermal zone parameters.

Cc: Zhang Rui <rui.zhang@intel.com>
Cc: Eduardo Valentin <edubezval@gmail.com>
Signed-off-by: Punit Agrawal <punit.agrawal@arm.com>
---
 Documentation/devicetree/bindings/thermal/thermal.txt | 4 ++++
 drivers/thermal/of-thermal.c                          | 4 ++++
 2 files changed, 8 insertions(+)

diff --git a/Documentation/devicetree/bindings/thermal/thermal.txt b/Documentation/devicetree/bindings/thermal/thermal.txt
index f5db6b72a36f..c6eb9a8d2aed 100644
--- a/Documentation/devicetree/bindings/thermal/thermal.txt
+++ b/Documentation/devicetree/bindings/thermal/thermal.txt
@@ -167,6 +167,10 @@ Optional property:
 			by means of sensor ID. Additional coefficients are
 			interpreted as constant offset.
 
+- sustainable-power:	An estimate of the sustainable power (in mW) that the
+  Type: unsigned	thermal zone can dissipate.
+  Size: one cell
+
 Note: The delay properties are bound to the maximum dT/dt (temperature
 derivative over time) in two situations for a thermal zone:
 (i)  - when passive cooling is activated (polling-delay-passive); and
diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c
index 62143ba31001..e032b9bf4085 100644
--- a/drivers/thermal/of-thermal.c
+++ b/drivers/thermal/of-thermal.c
@@ -794,6 +794,7 @@ int __init of_parse_thermal_zones(void)
 	for_each_child_of_node(np, child) {
 		struct thermal_zone_device *zone;
 		struct thermal_zone_params *tzp;
+		u32 prop;
 
 		/* Check whether child is enabled or not */
 		if (!of_device_is_available(child))
@@ -820,6 +821,9 @@ int __init of_parse_thermal_zones(void)
 		/* No hwmon because there might be hwmon drivers registering */
 		tzp->no_hwmon = true;
 
+		if (!of_property_read_u32(child, "sustainable-power", &prop))
+			tzp->sustainable_power = prop;
+
 		zone = thermal_zone_device_register(child->name, tz->ntrips,
 						    0, tz,
 						    ops, tzp,
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 4/9] thermal: let governors have private data for each thermal zone
  2014-12-05 19:04 ` [RFC PATCH v6 4/9] thermal: let governors have private data for each thermal zone Javi Merino
@ 2014-12-08  4:11   ` Zhang Rui
  2015-01-23 14:33     ` Javi Merino
  0 siblings, 1 reply; 48+ messages in thread
From: Zhang Rui @ 2014-12-08  4:11 UTC (permalink / raw)
  To: Javi Merino
  Cc: linux-pm, linux-kernel, punit.agrawal, broonie, Eduardo Valentin

On Fri, 2014-12-05 at 19:04 +0000, Javi Merino wrote:
> A governor may need to store its current state between calls to
> throttle().  That state depends on the thermal zone, so store it as
> private data in struct thermal_zone_device.
> 
> The governors may have two new ops: bind_to_tz() and unbind_from_tz().
> When provided, these functions let governors do some initialization
> and teardown when they are bound/unbound to a tz and possibly store that
> information in the governor_data field of the struct
> thermal_zone_device.
> 
> Cc: Zhang Rui <rui.zhang@intel.com>
> Cc: Eduardo Valentin <edubezval@gmail.com>
> Signed-off-by: Javi Merino <javi.merino@arm.com>

applied.

thanks,
rui
> ---
>  drivers/thermal/thermal_core.c | 83 ++++++++++++++++++++++++++++++++++++++----
>  include/linux/thermal.h        |  9 +++++
>  2 files changed, 84 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
> index 43b90709585f..9021cb72a13a 100644
> --- a/drivers/thermal/thermal_core.c
> +++ b/drivers/thermal/thermal_core.c
> @@ -75,6 +75,58 @@ static struct thermal_governor *__find_governor(const char *name)
>  	return NULL;
>  }
>  
> +/**
> + * bind_previous_governor() - bind the previous governor of the thermal zone
> + * @tz:		a valid pointer to a struct thermal_zone_device
> + * @failed_gov_name:	the name of the governor that failed to register
> + *
> + * Register the previous governor of the thermal zone after a new
> + * governor has failed to be bound.
> + */
> +static void bind_previous_governor(struct thermal_zone_device *tz,
> +				const char *failed_gov_name)
> +{
> +	if (tz->governor && tz->governor->bind_to_tz) {
> +		if (tz->governor->bind_to_tz(tz)) {
> +			dev_err(&tz->device,
> +				"governor %s failed to bind and the previous one (%s) failed to bind again, thermal zone %s has no governor\n",
> +				failed_gov_name, tz->governor->name, tz->type);
> +			tz->governor = NULL;
> +		}
> +	}
> +}
> +
> +/**
> + * thermal_set_governor() - Switch to another governor
> + * @tz:		a valid pointer to a struct thermal_zone_device
> + * @new_gov:	pointer to the new governor
> + *
> + * Change the governor of thermal zone @tz.
> + *
> + * Return: 0 on success, an error if the new governor's bind_to_tz() failed.
> + */
> +static int thermal_set_governor(struct thermal_zone_device *tz,
> +				struct thermal_governor *new_gov)
> +{
> +	int ret = 0;
> +
> +	if (tz->governor && tz->governor->unbind_from_tz)
> +		tz->governor->unbind_from_tz(tz);
> +
> +	if (new_gov && new_gov->bind_to_tz) {
> +		ret = new_gov->bind_to_tz(tz);
> +		if (ret) {
> +			bind_previous_governor(tz, new_gov->name);
> +
> +			return ret;
> +		}
> +	}
> +
> +	tz->governor = new_gov;
> +
> +	return ret;
> +}
> +
>  int thermal_register_governor(struct thermal_governor *governor)
>  {
>  	int err;
> @@ -107,8 +159,15 @@ int thermal_register_governor(struct thermal_governor *governor)
>  
>  		name = pos->tzp->governor_name;
>  
> -		if (!strncasecmp(name, governor->name, THERMAL_NAME_LENGTH))
> -			pos->governor = governor;
> +		if (!strncasecmp(name, governor->name, THERMAL_NAME_LENGTH)) {
> +			int ret;
> +
> +			ret = thermal_set_governor(pos, governor);
> +			if (ret)
> +				dev_err(&pos->device,
> +					"Failed to set governor %s for thermal zone %s: %d\n",
> +					governor->name, pos->type, ret);
> +		}
>  	}
>  
>  	mutex_unlock(&thermal_list_lock);
> @@ -134,7 +193,7 @@ void thermal_unregister_governor(struct thermal_governor *governor)
>  	list_for_each_entry(pos, &thermal_tz_list, node) {
>  		if (!strncasecmp(pos->governor->name, governor->name,
>  						THERMAL_NAME_LENGTH))
> -			pos->governor = NULL;
> +			thermal_set_governor(pos, NULL);
>  	}
>  
>  	mutex_unlock(&thermal_list_lock);
> @@ -762,8 +821,9 @@ policy_store(struct device *dev, struct device_attribute *attr,
>  	if (!gov)
>  		goto exit;
>  
> -	tz->governor = gov;
> -	ret = count;
> +	ret = thermal_set_governor(tz, gov);
> +	if (!ret)
> +		ret = count;
>  
>  exit:
>  	mutex_unlock(&thermal_governor_lock);
> @@ -1459,6 +1519,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
>  	int result;
>  	int count;
>  	int passive = 0;
> +	struct thermal_governor *governor;
>  
>  	if (type && strlen(type) >= THERMAL_NAME_LENGTH)
>  		return ERR_PTR(-EINVAL);
> @@ -1549,9 +1610,15 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
>  	mutex_lock(&thermal_governor_lock);
>  
>  	if (tz->tzp)
> -		tz->governor = __find_governor(tz->tzp->governor_name);
> +		governor = __find_governor(tz->tzp->governor_name);
>  	else
> -		tz->governor = def_governor;
> +		governor = def_governor;
> +
> +	result = thermal_set_governor(tz, governor);
> +	if (result) {
> +		mutex_unlock(&thermal_governor_lock);
> +		goto unregister;
> +	}
>  
>  	mutex_unlock(&thermal_governor_lock);
>  
> @@ -1640,7 +1707,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
>  		device_remove_file(&tz->device, &dev_attr_mode);
>  	device_remove_file(&tz->device, &dev_attr_policy);
>  	remove_trip_attrs(tz);
> -	tz->governor = NULL;
> +	thermal_set_governor(tz, NULL);
>  
>  	thermal_remove_hwmon_sysfs(tz);
>  	release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
> diff --git a/include/linux/thermal.h b/include/linux/thermal.h
> index ef90838b36a0..2c14ab1f5c0d 100644
> --- a/include/linux/thermal.h
> +++ b/include/linux/thermal.h
> @@ -191,6 +191,7 @@ struct thermal_attr {
>   * @ops:	operations this &thermal_zone_device supports
>   * @tzp:	thermal zone parameters
>   * @governor:	pointer to the governor for this thermal zone
> + * @governor_data:	private pointer for governor data
>   * @thermal_instances:	list of &struct thermal_instance of this thermal zone
>   * @idr:	&struct idr to generate unique id for this zone's cooling
>   *		devices
> @@ -217,6 +218,7 @@ struct thermal_zone_device {
>  	struct thermal_zone_device_ops *ops;
>  	const struct thermal_zone_params *tzp;
>  	struct thermal_governor *governor;
> +	void *governor_data;
>  	struct list_head thermal_instances;
>  	struct idr idr;
>  	struct mutex lock;
> @@ -227,12 +229,19 @@ struct thermal_zone_device {
>  /**
>   * struct thermal_governor - structure that holds thermal governor information
>   * @name:	name of the governor
> + * @bind_to_tz: callback called when binding to a thermal zone.  If it
> + *		returns 0, the governor is bound to the thermal zone,
> + *		otherwise it fails.
> + * @unbind_from_tz:	callback called when a governor is unbound from a
> + *			thermal zone.
>   * @throttle:	callback called for every trip point even if temperature is
>   *		below the trip point temperature
>   * @governor_list:	node in thermal_governor_list (in thermal_core.c)
>   */
>  struct thermal_governor {
>  	char name[THERMAL_NAME_LENGTH];
> +	int (*bind_to_tz)(struct thermal_zone_device *tz);
> +	void (*unbind_from_tz)(struct thermal_zone_device *tz);
>  	int (*throttle)(struct thermal_zone_device *tz, int trip);
>  	struct list_head	governor_list;
>  };



^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 6/9] thermal: cpu_cooling: implement the power cooling device API
  2014-12-05 19:04 ` [RFC PATCH v6 6/9] thermal: cpu_cooling: implement the power cooling device API Javi Merino
@ 2014-12-08  5:49   ` Viresh Kumar
  2014-12-08 12:50     ` Javi Merino
  2015-01-28  5:23   ` Eduardo Valentin
  1 sibling, 1 reply; 48+ messages in thread
From: Viresh Kumar @ 2014-12-08  5:49 UTC (permalink / raw)
  To: Javi Merino
  Cc: Linux PM list, linux-kernel, punit.agrawal, Mark Brown,
	Zhang Rui, Eduardo Valentin

Hi Javi,

Looks like ARM's exchange server screwed up your patch?

This is how I see it with gmail's show-original option:

+=09cpufreq_device->dyn_power_table =3D power_table;
+=09cpufreq_device->dyn_power_table_entries =3D i;
+

I have seen this a lot, while I was in ARM. Had to adopt some work-arounds to
get over it. :)

On Sat, Dec 6, 2014 at 12:34 AM, Javi Merino <javi.merino@arm.com> wrote:

> diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c

> +static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device,
> +                               u32 capacitance)
> +{
> +       struct power_table *power_table;
> +       struct dev_pm_opp *opp;
> +       struct device *dev = NULL;
> +       int num_opps, cpu, i, ret = 0;

Why not initialize num_opps and i to 0 here?

> +       unsigned long freq;
> +
> +       num_opps = 0;
> +
> +       rcu_read_lock();
> +
> +       for_each_cpu(cpu, &cpufreq_device->allowed_cpus) {

All these CPUs must be sharing the OPPs as they must be supplied
from a single clock line. But probably you need to iterate over all
because you don't know which ones share OPP. Right ? Probably
the work I am doing around getting new OPP bindings might solve
this..

> +               dev = get_cpu_device(cpu);
> +               if (!dev)

Is this allowed? I understand you can continue, but this is not
possible. Right ? So, print a error here?

> +                       continue;
> +
> +               num_opps = dev_pm_opp_get_opp_count(dev);
> +               if (num_opps > 0) {
> +                       break;
> +               } else if (num_opps < 0) {
> +                       ret = num_opps;
> +                       goto unlock;
> +               }
> +       }
> +
> +       if (num_opps == 0) {
> +               ret = -EINVAL;
> +               goto unlock;
> +       }
> +
> +       power_table = kcalloc(num_opps, sizeof(*power_table), GFP_KERNEL);
> +
> +       i = 0;

Either initialize i at the beginning or in the initialization part of
for loop below.

> +       for (freq = 0;
> +            opp = dev_pm_opp_find_freq_ceil(dev, &freq), !IS_ERR(opp);
> +            freq++) {
> +               u32 freq_mhz, voltage_mv;
> +               u64 power;
> +
> +               freq_mhz = freq / 1000000;
> +               voltage_mv = dev_pm_opp_get_voltage(opp) / 1000;
> +
> +               /*
> +                * Do the multiplication with MHz and millivolt so as
> +                * to not overflow.
> +                */
> +               power = (u64)capacitance * freq_mhz * voltage_mv * voltage_mv;
> +               do_div(power, 1000000000);
> +
> +               /* frequency is stored in power_table in KHz */
> +               power_table[i].frequency = freq / 1000;
> +               power_table[i].power = power;
> +
> +               i++;

Why here and not with freq++?

> +       }
> +
> +       if (i == 0) {
> +               ret = PTR_ERR(opp);
> +               goto unlock;
> +       }
> +
> +       cpufreq_device->dyn_power_table = power_table;
> +       cpufreq_device->dyn_power_table_entries = i;
> +
> +unlock:
> +       rcu_read_unlock();
> +       return ret;
> +}
> +
> +static u32 cpu_freq_to_power(struct cpufreq_cooling_device *cpufreq_device,
> +                       u32 freq)

Because the patch is screwed up a bit, I really can't see if the 'u'
or u32 is directly
below the 's' of struct cpufreq_cooling_device. Running checkpatch with --strict
will take care of that probably. Sorry if you have already taken care of that..

> +{
> +       int i;
> +       struct power_table *pt = cpufreq_device->dyn_power_table;
> +
> +       for (i = 1; i < cpufreq_device->dyn_power_table_entries; i++)
> +               if (freq < pt[i].frequency)
> +                       break;
> +
> +       return pt[i - 1].power;
> +}

> +static u32 get_static_power(struct cpufreq_cooling_device *cpufreq_device,
> +                       unsigned long freq)
> +{
> +       struct device *cpu_dev;
> +       struct dev_pm_opp *opp;
> +       unsigned long voltage;
> +       struct cpumask *cpumask = &cpufreq_device->allowed_cpus;
> +       unsigned long freq_hz = freq * 1000;
> +
> +       if (!cpufreq_device->plat_get_static_power)
> +               return 0;
> +
> +       cpu_dev = get_cpu_device(cpumask_any(cpumask));

Similar to the way you have used for-each-cpu earlier, the cpu
returned from above maynot have opps attached to it. Right ?

Probably you can keep a copy of the cpu_dev we have opps attached
with somewhere and reuse it.

> +
> +       rcu_read_lock();
> +
> +       opp = dev_pm_opp_find_freq_exact(cpu_dev, freq_hz, true);

So, this might fail if I am not wrong.

> +       voltage = dev_pm_opp_get_voltage(opp);
> +
> +       rcu_read_unlock();
> +
> +       if (voltage == 0) {
> +               dev_warn_ratelimited(cpu_dev,
> +                               "Failed to get voltage for frequency %lu: %ld\n",
> +                               freq_hz, IS_ERR(opp) ? PTR_ERR(opp) : 0);
> +               return 0;
> +       }
> +
> +       return cpufreq_device->plat_get_static_power(cpumask, voltage);
> +}

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 6/9] thermal: cpu_cooling: implement the power cooling device API
  2014-12-08  5:49   ` Viresh Kumar
@ 2014-12-08 12:50     ` Javi Merino
  2014-12-08 13:31       ` Viresh Kumar
  0 siblings, 1 reply; 48+ messages in thread
From: Javi Merino @ 2014-12-08 12:50 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: Linux PM list, linux-kernel, Punit Agrawal, Mark Brown,
	Zhang Rui, Eduardo Valentin

On Mon, Dec 08, 2014 at 05:49:00AM +0000, Viresh Kumar wrote:
> Hi Javi,

Hi Viresh,

> Looks like ARM's exchange server screwed up your patch?
> 
> This is how I see it with gmail's show-original option:
> 
> +=09cpufreq_device->dyn_power_table =3D power_table;
> +=09cpufreq_device->dyn_power_table_entries =3D i;
> +
> 
> I have seen this a lot, while I was in ARM. Had to adopt some work-arounds to
> get over it. :)

Sigh.  Care to share them (privately I guess)?
 
> On Sat, Dec 6, 2014 at 12:34 AM, Javi Merino <javi.merino@arm.com> wrote:
> 
> > diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
> 
> > +static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device,
> > +                               u32 capacitance)
> > +{
> > +       struct power_table *power_table;
> > +       struct dev_pm_opp *opp;
> > +       struct device *dev = NULL;
> > +       int num_opps, cpu, i, ret = 0;
> 
> Why not initialize num_opps and i to 0 here?

ok

> > +       unsigned long freq;
> > +
> > +       num_opps = 0;
> > +
> > +       rcu_read_lock();
> > +
> > +       for_each_cpu(cpu, &cpufreq_device->allowed_cpus) {
> 
> All these CPUs must be sharing the OPPs as they must be supplied
> from a single clock line. But probably you need to iterate over all
> because you don't know which ones share OPP. Right ? Probably
> the work I am doing around getting new OPP bindings might solve
> this..

Is this loop pointless?  I seem to recall that it was needed but I
forgot the details.  If you think it is, I can remove it.

> > +               dev = get_cpu_device(cpu);
> > +               if (!dev)
> 
> Is this allowed? I understand you can continue, but this is not
> possible. Right ? So, print a error here?

Ok, now it prints an error.

> > +                       continue;
> > +
> > +               num_opps = dev_pm_opp_get_opp_count(dev);
> > +               if (num_opps > 0) {
> > +                       break;
> > +               } else if (num_opps < 0) {
> > +                       ret = num_opps;
> > +                       goto unlock;
> > +               }
> > +       }
> > +
> > +       if (num_opps == 0) {
> > +               ret = -EINVAL;
> > +               goto unlock;
> > +       }
> > +
> > +       power_table = kcalloc(num_opps, sizeof(*power_table), GFP_KERNEL);
> > +
> > +       i = 0;
> 
> Either initialize i at the beginning or in the initialization part of
> for loop below.

As part of the for loop.
 
> > +       for (freq = 0;
> > +            opp = dev_pm_opp_find_freq_ceil(dev, &freq), !IS_ERR(opp);
> > +            freq++) {
> > +               u32 freq_mhz, voltage_mv;
> > +               u64 power;
> > +
> > +               freq_mhz = freq / 1000000;
> > +               voltage_mv = dev_pm_opp_get_voltage(opp) / 1000;
> > +
> > +               /*
> > +                * Do the multiplication with MHz and millivolt so as
> > +                * to not overflow.
> > +                */
> > +               power = (u64)capacitance * freq_mhz * voltage_mv * voltage_mv;
> > +               do_div(power, 1000000000);
> > +
> > +               /* frequency is stored in power_table in KHz */
> > +               power_table[i].frequency = freq / 1000;
> > +               power_table[i].power = power;
> > +
> > +               i++;
> 
> Why here and not with freq++?

As part of the for loop as well.
 
> > +       }
> > +
> > +       if (i == 0) {
> > +               ret = PTR_ERR(opp);
> > +               goto unlock;
> > +       }
> > +
> > +       cpufreq_device->dyn_power_table = power_table;
> > +       cpufreq_device->dyn_power_table_entries = i;
> > +
> > +unlock:
> > +       rcu_read_unlock();
> > +       return ret;
> > +}
> > +
> > +static u32 cpu_freq_to_power(struct cpufreq_cooling_device *cpufreq_device,
> > +                       u32 freq)
> 
> Because the patch is screwed up a bit, I really can't see if the 'u'
> or u32 is directly
> below the 's' of struct cpufreq_cooling_device. Running checkpatch with --strict
> will take care of that probably. Sorry if you have already taken care of that..

It wasn't.  I'll run checkpatch with --strict on next submission.

> > +{
> > +       int i;
> > +       struct power_table *pt = cpufreq_device->dyn_power_table;
> > +
> > +       for (i = 1; i < cpufreq_device->dyn_power_table_entries; i++)
> > +               if (freq < pt[i].frequency)
> > +                       break;
> > +
> > +       return pt[i - 1].power;
> > +}
> 
> > +static u32 get_static_power(struct cpufreq_cooling_device *cpufreq_device,
> > +                       unsigned long freq)
> > +{
> > +       struct device *cpu_dev;
> > +       struct dev_pm_opp *opp;
> > +       unsigned long voltage;
> > +       struct cpumask *cpumask = &cpufreq_device->allowed_cpus;
> > +       unsigned long freq_hz = freq * 1000;
> > +
> > +       if (!cpufreq_device->plat_get_static_power)
> > +               return 0;
> > +
> > +       cpu_dev = get_cpu_device(cpumask_any(cpumask));
> 
> Similar to the way you have used for-each-cpu earlier, the cpu
> returned from above maynot have opps attached to it. Right ?
> 
> Probably you can keep a copy of the cpu_dev we have opps attached
> with somewhere and reuse it.

Sounds like a good idea, done.

> > +
> > +       rcu_read_lock();
> > +
> > +       opp = dev_pm_opp_find_freq_exact(cpu_dev, freq_hz, true);
> 
> So, this might fail if I am not wrong.
> 
> > +       voltage = dev_pm_opp_get_voltage(opp);
> > +
> > +       rcu_read_unlock();
> > +
> > +       if (voltage == 0) {
> > +               dev_warn_ratelimited(cpu_dev,
> > +                               "Failed to get voltage for frequency %lu: %ld\n",
> > +                               freq_hz, IS_ERR(opp) ? PTR_ERR(opp) : 0);
> > +               return 0;
> > +       }
> > +
> > +       return cpufreq_device->plat_get_static_power(cpumask, voltage);
> > +}
> 

Cheers,
Javi


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 6/9] thermal: cpu_cooling: implement the power cooling device API
  2014-12-08 12:50     ` Javi Merino
@ 2014-12-08 13:31       ` Viresh Kumar
  2014-12-08 14:22         ` Javi Merino
  0 siblings, 1 reply; 48+ messages in thread
From: Viresh Kumar @ 2014-12-08 13:31 UTC (permalink / raw)
  To: Javi Merino
  Cc: Linux PM list, linux-kernel, Punit Agrawal, Mark Brown,
	Zhang Rui, Eduardo Valentin

On 8 December 2014 at 18:20, Javi Merino <javi.merino@arm.com> wrote:
> Sigh.  Care to share them (privately I guess)?

Sure, you will get that in a separate (private) mail..

> Is this loop pointless?  I seem to recall that it was needed but I
> forgot the details.  If you think it is, I can remove it.

Yes it is pointless. The CPUs you are iterating on, share clock lines
and so they will have same set of OPPs. Just do this for the cpu
we are registering the cooling device.

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 6/9] thermal: cpu_cooling: implement the power cooling device API
  2014-12-08 13:31       ` Viresh Kumar
@ 2014-12-08 14:22         ` Javi Merino
  2014-12-09  1:59           ` Viresh Kumar
  0 siblings, 1 reply; 48+ messages in thread
From: Javi Merino @ 2014-12-08 14:22 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: Linux PM list, linux-kernel, Punit Agrawal, Mark Brown,
	Zhang Rui, Eduardo Valentin

On Mon, Dec 08, 2014 at 01:31:35PM +0000, Viresh Kumar wrote:
> On 8 December 2014 at 18:20, Javi Merino <javi.merino@arm.com> wrote:
> > Is this loop pointless?  I seem to recall that it was needed but I
> > forgot the details.  If you think it is, I can remove it.
> 
> Yes it is pointless. The CPUs you are iterating on, share clock lines
> and so they will have same set of OPPs. Just do this for the cpu
> we are registering the cooling device.

Ok, changed it into:

	cpu = cpumask_any(&cpufreq_device->allowed_cpus);
	dev = get_cpu_device(cpu);
	if (!dev) {
		dev_warn(&cpufreq_device->cool_dev->device,
			"No cpu device for cpu %d\n", cpu);
		ret = -EINVAL;
		goto unlock;
	}

	num_opps = dev_pm_opp_get_opp_count(dev);
	if (num_opps <= 0) {
		ret = (num_opps < 0)? num_opps : -EINVAL;
		goto unlock;
	}

Thanks!
Javi


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 1/9] tracing: Add array printing helpers
  2014-12-05 19:04 ` [RFC PATCH v6 1/9] tracing: Add array printing helpers Javi Merino
@ 2014-12-08 14:39   ` Dave P Martin
  2014-12-08 15:42   ` Steven Rostedt
  1 sibling, 0 replies; 48+ messages in thread
From: Dave P Martin @ 2014-12-08 14:39 UTC (permalink / raw)
  To: Javi Merino
  Cc: linux-pm, linux-kernel, Punit Agrawal, broonie, Steven Rostedt,
	Ingo Molnar

On Fri, Dec 05, 2014 at 07:04:12PM +0000, Javi Merino wrote:
> From: Dave Martin <Dave.Martin@arm.com>
> 
> If a trace event contains an array, there is currently no standard
> way to format this for text output.  Drivers are currently hacking
> around this by a) local hacks that use the trace_seq functionailty
> directly, or b) just not printing that information.  For fixed size
> arrays, formatting of the elements can be open-coded, but this gets
> cumbersome for arrays of non-trivial size.
> 
> These approaches result in non-standard content of the event format
> description delivered to userspace, so userland tools needs to be
> taught to understand and parse each array printing method
> individually.
> 
> This patch implements common __print_<type>_array() helpers that
> tracepoint implementations can use instead of reinventing them.  A
> simple C-style syntax is used to delimit the array and its elements
> {like,this}.
> 
> So that the helpers can be used with large static arrays as well as
> dynamic arrays, they take a pointer and element count: they can be
> used with __get_dynamic_array() for use with dynamic arrays.
> 
> Cc: Steven Rostedt <rostedt@goodmis.org>
> Cc: Ingo Molnar <mingo@redhat.com>
> Signed-off-by: Dave Martin <Dave.Martin@arm.com>
> ---
>  include/linux/ftrace_event.h |  9 ++++++++
>  include/trace/ftrace.h       | 17 +++++++++++++++
>  kernel/trace/trace_output.c  | 51 ++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 77 insertions(+)
> 
> diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h
> index 28672e87e910..415afc53fa51 100644
> --- a/include/linux/ftrace_event.h
> +++ b/include/linux/ftrace_event.h
> @@ -44,6 +44,15 @@ const char *ftrace_print_bitmask_seq(struct trace_seq *p, void *bitmask_ptr,
>  const char *ftrace_print_hex_seq(struct trace_seq *p,
>  				 const unsigned char *buf, int len);
>  
> +const char *ftrace_print_u8_array_seq(struct trace_seq *p,
> +				      const u8 *buf, int count);
> +const char *ftrace_print_u16_array_seq(struct trace_seq *p,
> +				       const u16 *buf, int count);
> +const char *ftrace_print_u32_array_seq(struct trace_seq *p,
> +				       const u32 *buf, int count);
> +const char *ftrace_print_u64_array_seq(struct trace_seq *p,
> +				       const u64 *buf, int count);
> +
>  struct trace_iterator;
>  struct trace_event;
>  
> diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h
> index 26b4f2e13275..15bc5d417aea 100644
> --- a/include/trace/ftrace.h
> +++ b/include/trace/ftrace.h
> @@ -263,6 +263,19 @@
>  #undef __print_hex
>  #define __print_hex(buf, buf_len) ftrace_print_hex_seq(p, buf, buf_len)
>  
> +#undef __print_u8_array
> +#define __print_u8_array(array, count)			\
> +	ftrace_print_u8_array_seq(p, array, count)
> +#undef __print_u16_array
> +#define __print_u16_array(array, count)			\
> +	ftrace_print_u16_array_seq(p, array, count)
> +#undef __print_u32_array
> +#define __print_u32_array(array, count)			\
> +	ftrace_print_u32_array_seq(p, array, count)
> +#undef __print_u64_array
> +#define __print_u64_array(array, count)			\
> +	ftrace_print_u64_array_seq(p, array, count)
> +
>  #undef DECLARE_EVENT_CLASS
>  #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print)	\
>  static notrace enum print_line_t					\
> @@ -676,6 +689,10 @@ static inline void ftrace_test_probe_##call(void)			\
>  #undef __get_dynamic_array_len
>  #undef __get_str
>  #undef __get_bitmask
> +#undef __print_u8_array
> +#undef __print_u16_array
> +#undef __print_u32_array
> +#undef __print_u64_array
>  
>  #undef TP_printk
>  #define TP_printk(fmt, args...) "\"" fmt "\", "  __stringify(args)
> diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c
> index c6977d5a9b12..4a6ee61f30b3 100644
> --- a/kernel/trace/trace_output.c
> +++ b/kernel/trace/trace_output.c
> @@ -186,6 +186,57 @@ ftrace_print_hex_seq(struct trace_seq *p, const unsigned char *buf, int buf_len)
>  }
>  EXPORT_SYMBOL(ftrace_print_hex_seq);
>  
> +static const char *
> +ftrace_print_array_seq(struct trace_seq *p, const void *buf, int buf_len,
> +		       bool (*iterator)(struct trace_seq *p, const char *prefix,
> +					const void **buf, int *buf_len))
> +{
> +	const char *ret = trace_seq_buffer_ptr(p);
> +	const char *prefix = "";
> +
> +	trace_seq_putc(p, '{');
> +
> +	while (iterator(p, prefix, &buf, &buf_len))
> +		prefix = ",";

Makes sense.

> +
> +	trace_seq_putc(p, '}');
> +	trace_seq_putc(p, 0);
> +
> +	return ret;
> +}
> +
> +#define DEFINE_PRINT_ARRAY(type, printk_type, format)			\
> +static bool								\
> +ftrace_print_array_iterator_##type(struct trace_seq *p, const char *prefix, \
> +				   const void **buf, int *buf_len)	\
> +{									\
> +	const type *__src = *buf;					\
> +									\
> +	if (*buf_len < sizeof(*__src))					\
> +		return false;						\
> +									\
> +	trace_seq_printf(p, "%s" format, prefix, (printk_type)*__src++); \
> +									\
> +	*buf = __src;							\
> +	*buf_len -= sizeof(*__src);					\
> +									\
> +	return true;							\
> +}									\
> +									\
> +const char *ftrace_print_##type##_array_seq(				\
> +	struct trace_seq *p, const type *buf, int count)		\
> +{									\
> +	return ftrace_print_array_seq(p, buf, (count) * sizeof(type),	\
> +				      ftrace_print_array_iterator_##type); \
> +}									\
> +									\
> +EXPORT_SYMBOL(ftrace_print_##type##_array_seq)
> +
> +DEFINE_PRINT_ARRAY(u8, unsigned int, "0x%x");
> +DEFINE_PRINT_ARRAY(u16, unsigned int, "0x%x");
> +DEFINE_PRINT_ARRAY(u32, unsigned int, "0x%x");
> +DEFINE_PRINT_ARRAY(u64, unsigned long long, "0x%llx");

(Responding to Steven's original comment on the choice of types here:)

Steven Rostedt wrote:

> > +DEFINE_PRINT_ARRAY(u8, unsigned int, "0x%x")
>
> Why not "unsigned char"?
>
> > +DEFINE_PRINT_ARRAY(u16, unsigned int, "0x%x")
>
> Why not "unsigned short"?

[...]

The original reason was to work around inconsistent definitions of u32
(unsigned int versus unsigned long) on different arches, so there was
no way to write the format string that wouldn't trigger GCC warnings
on some architectures.  The kernel doesn't seem to provide the nasty
C99 PRIxNN macros for now.

Now that all arches seem to use asm-generic to define the u32 type, it
looks like everyone uses the same definition, so this is probably not
needed any more.

I suggest we remove the printk_type argument completely and just use
the underlying uNN type -- but build-testing on a mix of few 32-bit and
64-bit arches would be recommended, just to be on the safe side.

Is there already precedent for this?  It seems likely, but I haven't
searched thoroughly.

Cheers
---Dave

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 1/9] tracing: Add array printing helpers
  2014-12-05 19:04 ` [RFC PATCH v6 1/9] tracing: Add array printing helpers Javi Merino
  2014-12-08 14:39   ` Dave P Martin
@ 2014-12-08 15:42   ` Steven Rostedt
  2014-12-08 16:04     ` Dave P Martin
  1 sibling, 1 reply; 48+ messages in thread
From: Steven Rostedt @ 2014-12-08 15:42 UTC (permalink / raw)
  To: Javi Merino
  Cc: linux-pm, linux-kernel, punit.agrawal, broonie, Dave Martin, Ingo Molnar

On Fri,  5 Dec 2014 19:04:12 +0000
"Javi Merino" <javi.merino@arm.com> wrote:

 
> +static const char *
> +ftrace_print_array_seq(struct trace_seq *p, const void *buf, int buf_len,
> +		       bool (*iterator)(struct trace_seq *p, const char *prefix,
> +					const void **buf, int *buf_len))
> +{
> +	const char *ret = trace_seq_buffer_ptr(p);
> +	const char *prefix = "";
> +
> +	trace_seq_putc(p, '{');
> +
> +	while (iterator(p, prefix, &buf, &buf_len))
> +		prefix = ",";
> +
> +	trace_seq_putc(p, '}');
> +	trace_seq_putc(p, 0);
> +
> +	return ret;
> +}
> +
> +#define DEFINE_PRINT_ARRAY(type, printk_type, format)			\
> +static bool								\
> +ftrace_print_array_iterator_##type(struct trace_seq *p, const char *prefix, \
> +				   const void **buf, int *buf_len)	\
> +{									\
> +	const type *__src = *buf;					\
> +									\
> +	if (*buf_len < sizeof(*__src))					\
> +		return false;						\
> +									\
> +	trace_seq_printf(p, "%s" format, prefix, (printk_type)*__src++); \
> +									\
> +	*buf = __src;							\
> +	*buf_len -= sizeof(*__src);					\
> +									\
> +	return true;							\
> +}									\
> +									\
> +const char *ftrace_print_##type##_array_seq(				\
> +	struct trace_seq *p, const type *buf, int count)		\
> +{									\
> +	return ftrace_print_array_seq(p, buf, (count) * sizeof(type),	\
> +				      ftrace_print_array_iterator_##type); \
> +}									\
> +									\
> +EXPORT_SYMBOL(ftrace_print_##type##_array_seq)
> +
> +DEFINE_PRINT_ARRAY(u8, unsigned int, "0x%x");
> +DEFINE_PRINT_ARRAY(u16, unsigned int, "0x%x");
> +DEFINE_PRINT_ARRAY(u32, unsigned int, "0x%x");
> +DEFINE_PRINT_ARRAY(u64, unsigned long long, "0x%llx");
> +

I would really like to avoid adding a bunch of macros for each type.
Can't we have something like this:

ftrace_print_array(struct trace_seq *p, void *buf, int buf_len, 
		int size)
{
	char *prefix = "";
	void *ptr = buf;

	while (ptr < buf + buf_len) {
		switch(size) {
		case 8:
			trace_seq_printf("%s0x%x", prefix,
				*(unsigned char *)ptr);
			break;
		case 16:
			trace_seq_printf("%s0x%x", prefix,
				*(unsigned short *)ptr);
			break;
		case 32:
			trace_seq_printf("%s0x%x", prefix,
				*(unsigned int *)ptr);
			break;
		case 64:
			trace_seq_printf("%s0x%llx", prefix,
				*(unsigned long long *)ptr);
			break;
		default:
			BUG();
		}
		prefix = ",";
		ptr += size;
	}

}

We probably could even make the "BUG()" into a build bug, with a little
work.

-- Steve

>  int ftrace_raw_output_prep(struct trace_iterator *iter,
>  			   struct trace_event *trace_event)
>  {


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 1/9] tracing: Add array printing helpers
  2014-12-08 15:42   ` Steven Rostedt
@ 2014-12-08 16:04     ` Dave P Martin
  2014-12-10 10:52       ` Javi Merino
  0 siblings, 1 reply; 48+ messages in thread
From: Dave P Martin @ 2014-12-08 16:04 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: Javi Merino, linux-pm, linux-kernel, Punit Agrawal, broonie, Ingo Molnar

On Mon, Dec 08, 2014 at 03:42:10PM +0000, Steven Rostedt wrote:
> On Fri,  5 Dec 2014 19:04:12 +0000
> "Javi Merino" <javi.merino@arm.com> wrote:

[...]

> > +
> > +DEFINE_PRINT_ARRAY(u8, unsigned int, "0x%x");
> > +DEFINE_PRINT_ARRAY(u16, unsigned int, "0x%x");
> > +DEFINE_PRINT_ARRAY(u32, unsigned int, "0x%x");
> > +DEFINE_PRINT_ARRAY(u64, unsigned long long, "0x%llx");
> > +
> 
> I would really like to avoid adding a bunch of macros for each type.
> Can't we have something like this:
> ftrace_print_array(struct trace_seq *p, void *buf, int buf_len, 
> 		int size)
> {
> 	char *prefix = "";
> 	void *ptr = buf;
> 
> 	while (ptr < buf + buf_len) {
> 		switch(size) {
> 		case 8:
> 			trace_seq_printf("%s0x%x", prefix,
> 				*(unsigned char *)ptr);

I think this should be *(u8 *) etc.

Otherwise, I don't have a problem with this approach.  It's less
ugly than my original.

> 			break;
> 		case 16:
> 			trace_seq_printf("%s0x%x", prefix,
> 				*(unsigned short *)ptr);
> 			break;
> 		case 32:
> 			trace_seq_printf("%s0x%x", prefix,
> 				*(unsigned int *)ptr);
> 			break;
> 		case 64:
> 			trace_seq_printf("%s0x%llx", prefix,
> 				*(unsigned long long *)ptr);
> 			break;
> 		default:
> 			BUG();
> 		}
> 		prefix = ",";
> 		ptr += size;
> 	}
> 
> }
> 
> We probably could even make the "BUG()" into a build bug, with a little
> work.

That sounds possible.

Javi?

Cheers
---Dave

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 6/9] thermal: cpu_cooling: implement the power cooling device API
  2014-12-08 14:22         ` Javi Merino
@ 2014-12-09  1:59           ` Viresh Kumar
  2014-12-09 10:32             ` Javi Merino
  0 siblings, 1 reply; 48+ messages in thread
From: Viresh Kumar @ 2014-12-09  1:59 UTC (permalink / raw)
  To: Javi Merino
  Cc: Linux PM list, linux-kernel, Punit Agrawal, Mark Brown,
	Zhang Rui, Eduardo Valentin

On 8 December 2014 at 19:52, Javi Merino <javi.merino@arm.com> wrote:
> Ok, changed it into:
>
>         cpu = cpumask_any(&cpufreq_device->allowed_cpus);
>         dev = get_cpu_device(cpu);
>         if (!dev) {
>                 dev_warn(&cpufreq_device->cool_dev->device,
>                         "No cpu device for cpu %d\n", cpu);
>                 ret = -EINVAL;
>                 goto unlock;
>         }
>
>         num_opps = dev_pm_opp_get_opp_count(dev);
>         if (num_opps <= 0) {
>                 ret = (num_opps < 0)? num_opps : -EINVAL;
>                 goto unlock;
>         }

And this might not work. This is what I said in the first reply.

So, a bit lengthy reply now :)

Every cpu has a device struct associated with it. When cpufreq
core initializes drivers, they ask for mapping (initializing) the opps.
At that point we pass policy->cpu to opp core. OPP core doesn't
know which cores share clock line (I am trying to solve that [1]) and
so it just initializes the OPPs for policy->cpu. Let us say it cpuX.

Now there will be few more CPUs which are going to share clock
line with it and hence will use the same OPPs. In thermal core,
you got clip_cpus which is exactly the masks of all these CPUs
sharing clock line.

If the OPP layer is good enough, then above code can work. But
because right now the OPPs are mapped to just cpuX, passing
any other cpu from clip_cpus will fail as it doesn't have any associated
OPPs.

Now what I asked you is to use the CPU for which
__cpufreq_cooling_register() is called. Normally we are calling
__cpufreq_cooling_register() for the CPU for which OPPs are
registered (but people might call it up for other CPUs as well)..

So, using that cpu *might* have worked here.

Now the earlier loop you used was good to get this information,
but it wasn't consistent and so I objected.

What you should do:

- Create another routine to find the cpu for which OPPs are bound
to
-  And save the cpu_dev for it in the global struct for cpu_cooling
- reuse it wherever required.

--
viresh

[1] https://www.marc.info/?t=141769172100002&r=1&w=2

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 6/9] thermal: cpu_cooling: implement the power cooling device API
  2014-12-09  1:59           ` Viresh Kumar
@ 2014-12-09 10:32             ` Javi Merino
  2014-12-09 10:36               ` Viresh Kumar
  0 siblings, 1 reply; 48+ messages in thread
From: Javi Merino @ 2014-12-09 10:32 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: Linux PM list, linux-kernel, Punit Agrawal, Mark Brown,
	Zhang Rui, Eduardo Valentin

Hi Viresh,

On Tue, Dec 09, 2014 at 01:59:39AM +0000, Viresh Kumar wrote:
> On 8 December 2014 at 19:52, Javi Merino <javi.merino@arm.com> wrote:
> > Ok, changed it into:
> >
> >         cpu = cpumask_any(&cpufreq_device->allowed_cpus);
> >         dev = get_cpu_device(cpu);
> >         if (!dev) {
> >                 dev_warn(&cpufreq_device->cool_dev->device,
> >                         "No cpu device for cpu %d\n", cpu);
> >                 ret = -EINVAL;
> >                 goto unlock;
> >         }
> >
> >         num_opps = dev_pm_opp_get_opp_count(dev);
> >         if (num_opps <= 0) {
> >                 ret = (num_opps < 0)? num_opps : -EINVAL;
> >                 goto unlock;
> >         }
> 
> And this might not work. This is what I said in the first reply.
> 
> So, a bit lengthy reply now :)
> 
> Every cpu has a device struct associated with it. When cpufreq
> core initializes drivers, they ask for mapping (initializing) the opps.
> At that point we pass policy->cpu to opp core. OPP core doesn't
> know which cores share clock line (I am trying to solve that [1]) and
> so it just initializes the OPPs for policy->cpu. Let us say it cpuX.
> 
> Now there will be few more CPUs which are going to share clock
> line with it and hence will use the same OPPs. In thermal core,
> you got clip_cpus which is exactly the masks of all these CPUs
> sharing clock line.
> 
> If the OPP layer is good enough, then above code can work. But
> because right now the OPPs are mapped to just cpuX, passing
> any other cpu from clip_cpus will fail as it doesn't have any associated
> OPPs.
> 
> Now what I asked you is to use the CPU for which
> __cpufreq_cooling_register() is called. Normally we are calling
> __cpufreq_cooling_register() for the CPU for which OPPs are
> registered (but people might call it up for other CPUs as well)..

Sorry but I don't follow.  __cpufreq_cooling_register() is passed a
clip_cpus mask, not a single cpu.  How do I get "the cpu for which
__cpufreq_cooling_register() is called" if not by looping through all
the cpus in the mask?
 
> So, using that cpu *might* have worked here.
> 
> Now the earlier loop you used was good to get this information,
> but it wasn't consistent and so I objected.
> 
> What you should do:
> 
> - Create another routine to find the cpu for which OPPs are bound
> to
> -  And save the cpu_dev for it in the global struct for cpu_cooling

This I have done, it wasn't part of the snip that I sent.

> - reuse it wherever required.

Same as above.


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 6/9] thermal: cpu_cooling: implement the power cooling device API
  2014-12-09 10:32             ` Javi Merino
@ 2014-12-09 10:36               ` Viresh Kumar
  2014-12-09 11:00                 ` Javi Merino
  0 siblings, 1 reply; 48+ messages in thread
From: Viresh Kumar @ 2014-12-09 10:36 UTC (permalink / raw)
  To: Javi Merino
  Cc: Linux PM list, linux-kernel, Punit Agrawal, Mark Brown,
	Zhang Rui, Eduardo Valentin

On 9 December 2014 at 16:02, Javi Merino <javi.merino@arm.com> wrote:
> Sorry but I don't follow.  __cpufreq_cooling_register() is passed a
> clip_cpus mask, not a single cpu.  How do I get "the cpu for which
> __cpufreq_cooling_register() is called" if not by looping through all
> the cpus in the mask?

Yeah, its np that is passed instead of cpu number. So, that wouldn't
be usable. Also because of the limitations I explained earlier, it makes
sense to iterate over all clip_cpus and finding which one owns OPPs.

--
viresh

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 6/9] thermal: cpu_cooling: implement the power cooling device API
  2014-12-09 10:36               ` Viresh Kumar
@ 2014-12-09 11:00                 ` Javi Merino
  2014-12-09 11:06                   ` Viresh Kumar
  2015-01-02 14:37                   ` Eduardo Valentin
  0 siblings, 2 replies; 48+ messages in thread
From: Javi Merino @ 2014-12-09 11:00 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: Linux PM list, linux-kernel, Punit Agrawal, Mark Brown,
	Zhang Rui, Eduardo Valentin

On Tue, Dec 09, 2014 at 10:36:46AM +0000, Viresh Kumar wrote:
> On 9 December 2014 at 16:02, Javi Merino <javi.merino@arm.com> wrote:
> > Sorry but I don't follow.  __cpufreq_cooling_register() is passed a
> > clip_cpus mask, not a single cpu.  How do I get "the cpu for which
> > __cpufreq_cooling_register() is called" if not by looping through all
> > the cpus in the mask?
> 
> Yeah, its np that is passed instead of cpu number. So, that wouldn't
> be usable. Also because of the limitations I explained earlier, it makes
> sense to iterate over all clip_cpus and finding which one owns OPPs.

Ok, how about this then?  I've pasted the whole commit so as to avoid
confusion.

diff --git a/Documentation/thermal/cpu-cooling-api.txt b/Documentation/thermal/cpu-cooling-api.txt
index fca24c931ec8..d438a900e374 100644
--- a/Documentation/thermal/cpu-cooling-api.txt
+++ b/Documentation/thermal/cpu-cooling-api.txt
@@ -25,8 +25,150 @@ the user. The registration APIs returns the cooling device pointer.
 
    clip_cpus: cpumask of cpus where the frequency constraints will happen.
 
-1.1.2 void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
+1.1.2 struct thermal_cooling_device *cpufreq_power_cooling_register(
+    const struct cpumask *clip_cpus, u32 capacitance,
+    get_static_t plat_static_func)
+
+Similar to cpufreq_cooling_register, this function registers a cpufreq
+cooling device.  Using this function, the cooling device will
+implement the power extensions by using a simple cpu power model.  The
+cpus must have registered their OPPs using the OPP library.
+
+The additional parameters are needed for the power model (See 2. Power
+models).  "capacitance" is the dynamic power coefficient (See 2.1
+Dynamic power).  "plat_static_func" is a function to calculate the
+static power consumed by these cpus (See 2.2 Static power).
+
+1.1.3 struct thermal_cooling_device *of_cpufreq_power_cooling_register(
+    struct device_node *np, const struct cpumask *clip_cpus, u32 capacitance,
+    get_static_t plat_static_func)
+
+Similar to cpufreq_power_cooling_register, this function register a
+cpufreq cooling device with power extensions using the device tree
+information supplied by the np parameter.
+
+1.1.4 void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
 
     This interface function unregisters the "thermal-cpufreq-%x" cooling device.
 
     cdev: Cooling device pointer which has to be unregistered.
+
+2. Power models
+
+The power API registration functions provide a simple power model for
+CPUs.  The current power is calculated as dynamic + (optionally)
+static power.  This power model requires that the operating-points of
+the CPUs are registered using the kernel's opp library and the
+`cpufreq_frequency_table` is assigned to the `struct device` of the
+cpu.  If you are using the `cpufreq-cpu0.c` driver then the
+`cpufreq_frequency_table` should already be assigned to the cpu
+device.
+
+The `plat_static_func` parameter of `cpufreq_power_cooling_register()`
+and `of_cpufreq_power_cooling_register()` is optional.  If you don't
+provide it, only dynamic power will be considered.
+
+2.1 Dynamic power
+
+The dynamic power consumption of a processor depends on many factors.
+For a given processor implementation the primary factors are:
+
+- The time the processor spends running, consuming dynamic power, as
+  compared to the time in idle states where dynamic consumption is
+  negligible.  Herein we refer to this as 'utilisation'.
+- The voltage and frequency levels as a result of DVFS.  The DVFS
+  level is a dominant factor governing power consumption.
+- In running time the 'execution' behaviour (instruction types, memory
+  access patterns and so forth) causes, in most cases, a second order
+  variation.  In pathological cases this variation can be significant,
+  but typically it is of a much lesser impact than the factors above.
+
+A high level dynamic power consumption model may then be represented as:
+
+Pdyn = f(run) * Voltage^2 * Frequency * Utilisation
+
+f(run) here represents the described execution behaviour and its
+result has a units of Watts/Hz/Volt^2 (this often expressed in
+mW/MHz/uVolt^2)
+
+The detailed behaviour for f(run) could be modelled on-line.  However,
+in practice, such an on-line model has dependencies on a number of
+implementation specific processor support and characterisation
+factors.  Therefore, in initial implementation that contribution is
+represented as a constant coefficient.  This is a simplification
+consistent with the relative contribution to overall power variation.
+
+In this simplified representation our model becomes:
+
+Pdyn = Kd * Voltage^2 * Frequency * Utilisation
+
+Where Kd (capacitance) represents an indicative running time dynamic
+power coefficient in fundamental units of mW/MHz/uVolt^2
+
+2.2 Static power
+
+Static leakage power consumption depends on a number of factors.  For a
+given circuit implementation the primary factors are:
+
+- Time the circuit spends in each 'power state'
+- Temperature
+- Operating voltage
+- Process grade
+
+The time the circuit spends in each 'power state' for a given
+evaluation period at first order means OFF or ON.  However,
+'retention' states can also be supported that reduce power during
+inactive periods without loss of context.
+
+Note: The visibility of state entries to the OS can vary, according to
+platform specifics, and this can then impact the accuracy of a model
+based on OS state information alone.  It might be possible in some
+cases to extract more accurate information from system resources.
+
+The temperature, operating voltage and process 'grade' (slow to fast)
+of the circuit are all significant factors in static leakage power
+consumption.  All of these have complex relationships to static power.
+
+Circuit implementation specific factors include the chosen silicon
+process as well as the type, number and size of transistors in both
+the logic gates and any RAM elements included.
+
+The static power consumption modelling must take into account the
+power managed regions that are implemented.  Taking the example of an
+ARM processor cluster, the modelling would take into account whether
+each CPU can be powered OFF separately or if only a single power
+region is implemented for the complete cluster.
+
+In one view, there are others, a static power consumption model can
+then start from a set of reference values for each power managed
+region (e.g. CPU, Cluster/L2) in each state (e.g. ON, OFF) at an
+arbitrary process grade, voltage and temperature point.  These values
+are then scaled for all of the following: the time in each state, the
+process grade, the current temperature and the operating voltage.
+However, since both implementation specific and complex relationships
+dominate the estimate, the appropriate interface to the model from the
+cpu cooling device is to provide a function callback that calculates
+the static power in this platform.  When registering the cpu cooling
+device pass a function pointer that follows the `get_static_t`
+prototype:
+
+    u32 plat_get_static(cpumask_t *cpumask, unsigned long voltage);
+
+with `cpumask` a cpumask of the cpus involved in the calculation and
+`voltage` the voltage at which they are operating.
+
+If `plat_static_func` is NULL, static power is considered to be
+negligible for this platform and only dynamic power is considered.
+
+The platform specific callback can then use any combination of tables
+and/or equations to permute the estimated value.  Process grade
+information is not passed to the model since access to such data, from
+on-chip measurement capability or manufacture time data, is platform
+specific.
+
+Note: the significance of static power for CPUs in comparison to
+dynamic power is highly dependent on implementation.  Given the
+potential complexity in implementation, the importance and accuracy of
+its inclusion when using cpu cooling devices should be assessed on a
+case by cases basis.
+
diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
index ad09e51ffae4..959a103d18ba 100644
--- a/drivers/thermal/cpu_cooling.c
+++ b/drivers/thermal/cpu_cooling.c
@@ -24,11 +24,25 @@
 #include <linux/thermal.h>
 #include <linux/cpufreq.h>
 #include <linux/err.h>
+#include <linux/pm_opp.h>
 #include <linux/slab.h>
 #include <linux/cpu.h>
 #include <linux/cpu_cooling.h>
 
 /**
+ * struct power_table - frequency to power conversion
+ * @frequency:	frequency in KHz
+ * @power:	power in mW
+ *
+ * This structure is built when the cooling device registers and helps
+ * in translating frequency to power and viceversa.
+ */
+struct power_table {
+	u32 frequency;
+	u32 power;
+};
+
+/**
  * struct cpufreq_cooling_device - data for cooling device with cpufreq
  * @id: unique integer value corresponding to each cpufreq_cooling_device
  *	registered.
@@ -39,6 +53,15 @@
  * @cpufreq_val: integer value representing the absolute value of the clipped
  *	frequency.
  * @allowed_cpus: all the cpus involved for this cpufreq_cooling_device.
+ * @last_load: load measured by the latest call to cpufreq_get_actual_power()
+ * @time_in_idle: previous reading of the absolute time that this cpu was idle
+ * @time_in_idle_timestamp: wall time of the last invocation of
+ *	get_cpu_idle_time_us()
+ * @dyn_power_table: array of struct power_table for frequency to power
+ *	conversion
+ * @dyn_power_table_entries: number of entries in the @dyn_power_table array
+ * @cpu_dev: the first cpu_device from @allowed_cpus that has OPPs registered
+ * @plat_get_static_power: callback to calculate the static power
  *
  * This structure is required for keeping information of each
  * cpufreq_cooling_device registered. In order to prevent corruption of this a
@@ -51,6 +74,13 @@ struct cpufreq_cooling_device {
 	unsigned int cpufreq_val;
 	struct cpumask allowed_cpus;
 	struct list_head node;
+	u32 last_load;
+	u64 time_in_idle[NR_CPUS];
+	u64 time_in_idle_timestamp[NR_CPUS];
+	struct power_table *dyn_power_table;
+	int dyn_power_table_entries;
+	struct device *cpu_dev;
+	get_static_t plat_get_static_power;
 };
 static DEFINE_IDR(cpufreq_idr);
 static DEFINE_MUTEX(cooling_cpufreq_lock);
@@ -338,6 +368,204 @@ static int cpufreq_thermal_notifier(struct notifier_block *nb,
 	return 0;
 }
 
+/**
+ * build_dyn_power_table() - create a dynamic power to frequency table
+ * @cpufreq_device:	the cpufreq cooling device in which to store the table
+ * @capacitance: dynamic power coefficient for these cpus
+ *
+ * Build a dynamic power to frequency table for this cpu and store it
+ * in @cpufreq_device.  This table will be used in cpu_power_to_freq() and
+ * cpu_freq_to_power() to convert between power and frequency
+ * efficiently.  Power is stored in mW, frequency in KHz.  The
+ * resulting table is in ascending order.
+ *
+ * Return: 0 on success, -E* on error.
+ */
+static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device,
+				u32 capacitance)
+{
+	struct power_table *power_table;
+	struct dev_pm_opp *opp;
+	struct device *dev = NULL;
+	int num_opps = 0, cpu, i, ret = 0;
+	unsigned long freq;
+
+	rcu_read_lock();
+
+	for_each_cpu(cpu, &cpufreq_device->allowed_cpus) {
+		dev = get_cpu_device(cpu);
+		if (!dev) {
+			dev_warn(&cpufreq_device->cool_dev->device,
+				"No cpu device for cpu %d\n", cpu);
+			continue;
+		}
+
+		num_opps = dev_pm_opp_get_opp_count(dev);
+		if (num_opps > 0) {
+			break;
+		} else if (num_opps < 0) {
+			ret = num_opps;
+			goto unlock;
+		}
+	}
+
+	if (num_opps == 0) {
+		ret = -EINVAL;
+		goto unlock;
+	}
+
+	power_table = devm_kcalloc(&cpufreq_device->cool_dev->device, num_opps,
+				sizeof(*power_table), GFP_KERNEL);
+
+	for (freq = 0, i = 0;
+	     opp = dev_pm_opp_find_freq_ceil(dev, &freq), !IS_ERR(opp);
+	     freq++, i++) {
+		u32 freq_mhz, voltage_mv;
+		u64 power;
+
+		freq_mhz = freq / 1000000;
+		voltage_mv = dev_pm_opp_get_voltage(opp) / 1000;
+
+		/*
+		 * Do the multiplication with MHz and millivolt so as
+		 * to not overflow.
+		 */
+		power = (u64)capacitance * freq_mhz * voltage_mv * voltage_mv;
+		do_div(power, 1000000000);
+
+		/* frequency is stored in power_table in KHz */
+		power_table[i].frequency = freq / 1000;
+		power_table[i].power = power;
+	}
+
+	if (i == 0) {
+		ret = PTR_ERR(opp);
+		goto unlock;
+	}
+
+	cpufreq_device->cpu_dev = dev;
+	cpufreq_device->dyn_power_table = power_table;
+	cpufreq_device->dyn_power_table_entries = i;
+
+unlock:
+	rcu_read_unlock();
+	return ret;
+}
+
+static u32 cpu_freq_to_power(struct cpufreq_cooling_device *cpufreq_device,
+			     u32 freq)
+{
+	int i;
+	struct power_table *pt = cpufreq_device->dyn_power_table;
+
+	for (i = 1; i < cpufreq_device->dyn_power_table_entries; i++)
+		if (freq < pt[i].frequency)
+			break;
+
+	return pt[i - 1].power;
+}
+
+static u32 cpu_power_to_freq(struct cpufreq_cooling_device *cpufreq_device,
+			u32 power)
+{
+	int i;
+	struct power_table *pt = cpufreq_device->dyn_power_table;
+
+	for (i = 1; i < cpufreq_device->dyn_power_table_entries; i++)
+		if (power < pt[i].power)
+			break;
+
+	return pt[i - 1].frequency;
+}
+
+/**
+ * get_load() - get load for a cpu since last updated
+ * @cpufreq_device:	&struct cpufreq_cooling_device for this cpu
+ * @cpu:	cpu number
+ *
+ * Return: The average load of cpu @cpu in percentage since this
+ * function was last called.
+ */
+static u32 get_load(struct cpufreq_cooling_device *cpufreq_device, int cpu)
+{
+	u32 load;
+	u64 now, now_idle, delta_time, delta_idle;
+
+	now_idle = get_cpu_idle_time(cpu, &now, 0);
+	delta_idle = now_idle - cpufreq_device->time_in_idle[cpu];
+	delta_time = now - cpufreq_device->time_in_idle_timestamp[cpu];
+
+	if (delta_time <= delta_idle)
+		load = 0;
+	else
+		load = div64_u64(100 * (delta_time - delta_idle), delta_time);
+
+	cpufreq_device->time_in_idle[cpu] = now_idle;
+	cpufreq_device->time_in_idle_timestamp[cpu] = now;
+
+	return load;
+}
+
+/**
+ * get_static_power() - calculate the static power consumed by the cpus
+ * @cpufreq_device:	struct &cpufreq_cooling_device for this cpu cdev
+ * @freq:	frequency in KHz
+ *
+ * Calculate the static power consumed by the cpus described by
+ * @cpu_actor running at frequency @freq.  This function relies on a
+ * platform specific function that should have been provided when the
+ * actor was registered.  If it wasn't, the static power is assumed to
+ * be negligible.
+ *
+ * Return: The static power consumed by the cpus.  It returns 0 on
+ * error or if there is no plat_get_static_power().
+ */
+static u32 get_static_power(struct cpufreq_cooling_device *cpufreq_device,
+			unsigned long freq)
+{
+	struct dev_pm_opp *opp;
+	unsigned long voltage;
+	struct cpumask *cpumask = &cpufreq_device->allowed_cpus;
+	unsigned long freq_hz = freq * 1000;
+
+	if (!cpufreq_device->plat_get_static_power)
+		return 0;
+
+	rcu_read_lock();
+
+	opp = dev_pm_opp_find_freq_exact(cpufreq_device->cpu_dev, freq_hz,
+					true);
+	voltage = dev_pm_opp_get_voltage(opp);
+
+	rcu_read_unlock();
+
+	if (voltage == 0) {
+		dev_warn_ratelimited(cpufreq_device->cpu_dev,
+				"Failed to get voltage for frequency %lu: %ld\n",
+				freq_hz, IS_ERR(opp) ? PTR_ERR(opp) : 0);
+		return 0;
+	}
+
+	return cpufreq_device->plat_get_static_power(cpumask, voltage);
+}
+
+/**
+ * get_dynamic_power() - calculate the dynamic power
+ * @cpufreq_device:	&cpufreq_cooling_device for this cdev
+ * @freq:	current frequency
+ *
+ * Return: the dynamic power consumed by the cpus described by
+ * @cpufreq_device.
+ */
+static u32 get_dynamic_power(struct cpufreq_cooling_device *cpufreq_device,
+			unsigned long freq)
+{
+	u32 raw_cpu_power;
+
+	raw_cpu_power = cpu_freq_to_power(cpufreq_device, freq);
+	return (raw_cpu_power * cpufreq_device->last_load) / 100;
+}
+
 /* cpufreq cooling device callback functions are defined below */
 
 /**
@@ -407,8 +635,106 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
 	return cpufreq_apply_cooling(cpufreq_device, state);
 }
 
+/**
+ * cpufreq_get_actual_power() - get the current power
+ * @cdev:	&thermal_cooling_device pointer
+ *
+ * Return the current power consumption of the cpus in milliwatts.
+ */
+static u32 cpufreq_get_actual_power(struct thermal_cooling_device *cdev)
+{
+	unsigned long freq;
+	int cpu;
+	u32 static_power, dynamic_power, total_load = 0;
+	struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
+
+	freq = cpufreq_quick_get(cpumask_any(&cpufreq_device->allowed_cpus));
+
+	for_each_cpu(cpu, &cpufreq_device->allowed_cpus) {
+		u32 load;
+
+		if (cpu_online(cpu))
+			load = get_load(cpufreq_device, cpu);
+		else
+			load = 0;
+
+		total_load += load;
+	}
+
+	cpufreq_device->last_load = total_load;
+
+	static_power = get_static_power(cpufreq_device, freq);
+	dynamic_power = get_dynamic_power(cpufreq_device, freq);
+
+	return static_power + dynamic_power;
+}
+
+/**
+ * cpufreq_state2power() - convert a cpu cdev state to power consumed
+ * @cdev:	&thermal_cooling_device pointer
+ * @state:	cooling device state to be converted
+ *
+ * Convert cooling device state @state into power consumption in milliwatts.
+ */
+static u32 cpufreq_state2power(struct thermal_cooling_device *cdev,
+			unsigned long state)
+{
+	unsigned int freq, num_cpus;
+	cpumask_t cpumask;
+	u32 static_power, dynamic_power;
+	struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
+
+	cpumask_and(&cpumask, &cpufreq_device->allowed_cpus, cpu_online_mask);
+	num_cpus = cpumask_weight(&cpumask);
+
+	freq = get_cpu_frequency(cpumask_any(&cpumask), state);
+	if (!freq)
+		return 0;
+
+	static_power = get_static_power(cpufreq_device, freq);
+	dynamic_power = cpu_freq_to_power(cpufreq_device, freq) * num_cpus;
+
+	return static_power + dynamic_power;
+}
+
+/**
+ * cpufreq_power2state() - convert power to a cooling device state
+ * @cdev:	&thermal_cooling_device pointer
+ * @power:	power in milliwatts to be converted
+ *
+ * Calculate a cooling device state for the cpus described by @cdev
+ * that would allow them to consume at most @power mW.
+ */
+static unsigned long cpufreq_power2state(struct thermal_cooling_device *cdev,
+					u32 power)
+{
+	unsigned int cpu, cur_freq, target_freq;
+	s32 dyn_power;
+	u32 last_load, normalised_power;
+	unsigned long cdev_state;
+	struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
+
+	cpu = cpumask_any_and(&cpufreq_device->allowed_cpus, cpu_online_mask);
+
+	cur_freq = cpufreq_quick_get(cpu);
+	dyn_power = power - get_static_power(cpufreq_device, cur_freq);
+	dyn_power = dyn_power > 0 ? dyn_power : 0;
+	last_load = cpufreq_device->last_load ?: 1;
+	normalised_power = (dyn_power * 100) / last_load;
+	target_freq = cpu_power_to_freq(cpufreq_device, normalised_power);
+
+	cdev_state = cpufreq_cooling_get_level(cpu, target_freq);
+	if (cdev_state == THERMAL_CSTATE_INVALID) {
+		pr_err_ratelimited("Failed to convert %dKHz for cpu %d into a cdev state\n",
+				target_freq, cpu);
+		return 0;
+	}
+
+	return cdev_state;
+}
+
 /* Bind cpufreq callbacks to thermal cooling device ops */
-static struct thermal_cooling_device_ops const cpufreq_cooling_ops = {
+static struct thermal_cooling_device_ops cpufreq_cooling_ops = {
 	.get_max_state = cpufreq_get_max_state,
 	.get_cur_state = cpufreq_get_cur_state,
 	.set_cur_state = cpufreq_set_cur_state,
@@ -434,7 +760,8 @@ static struct notifier_block thermal_cpufreq_notifier_block = {
  */
 static struct thermal_cooling_device *
 __cpufreq_cooling_register(struct device_node *np,
-			   const struct cpumask *clip_cpus)
+			const struct cpumask *clip_cpus, u32 capacitance,
+			get_static_t plat_static_func)
 {
 	struct thermal_cooling_device *cool_dev;
 	struct cpufreq_cooling_device *cpufreq_dev = NULL;
@@ -464,10 +791,23 @@ __cpufreq_cooling_register(struct device_node *np,
 
 	cpumask_copy(&cpufreq_dev->allowed_cpus, clip_cpus);
 
+	if (capacitance) {
+		cpufreq_cooling_ops.get_actual_power = cpufreq_get_actual_power;
+		cpufreq_cooling_ops.state2power = cpufreq_state2power;
+		cpufreq_cooling_ops.power2state = cpufreq_power2state;
+		cpufreq_dev->plat_get_static_power = plat_static_func;
+
+		ret = build_dyn_power_table(cpufreq_dev, capacitance);
+		if (ret) {
+			cool_dev = ERR_PTR(ret);
+			goto free;
+		}
+	}
+
 	ret = get_idr(&cpufreq_idr, &cpufreq_dev->id);
 	if (ret) {
-		kfree(cpufreq_dev);
-		return ERR_PTR(-EINVAL);
+		cool_dev = ERR_PTR(-EINVAL);
+		goto free;
 	}
 
 	snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d",
@@ -475,11 +815,8 @@ __cpufreq_cooling_register(struct device_node *np,
 
 	cool_dev = thermal_of_cooling_device_register(np, dev_name, cpufreq_dev,
 						      &cpufreq_cooling_ops);
-	if (IS_ERR(cool_dev)) {
-		release_idr(&cpufreq_idr, cpufreq_dev->id);
-		kfree(cpufreq_dev);
-		return cool_dev;
-	}
+	if (IS_ERR(cool_dev))
+		goto release_idr;
 	cpufreq_dev->cool_dev = cool_dev;
 	cpufreq_dev->cpufreq_state = 0;
 	mutex_lock(&cooling_cpufreq_lock);
@@ -494,6 +831,12 @@ __cpufreq_cooling_register(struct device_node *np,
 	mutex_unlock(&cooling_cpufreq_lock);
 
 	return cool_dev;
+
+release_idr:
+	release_idr(&cpufreq_idr, cpufreq_dev->id);
+free:
+	kfree(cpufreq_dev);
+	return cool_dev;
 }
 
 /**
@@ -510,7 +853,7 @@ __cpufreq_cooling_register(struct device_node *np,
 struct thermal_cooling_device *
 cpufreq_cooling_register(const struct cpumask *clip_cpus)
 {
-	return __cpufreq_cooling_register(NULL, clip_cpus);
+	return __cpufreq_cooling_register(NULL, clip_cpus, 0, NULL);
 }
 EXPORT_SYMBOL_GPL(cpufreq_cooling_register);
 
@@ -534,11 +877,77 @@ of_cpufreq_cooling_register(struct device_node *np,
 	if (!np)
 		return ERR_PTR(-EINVAL);
 
-	return __cpufreq_cooling_register(np, clip_cpus);
+	return __cpufreq_cooling_register(np, clip_cpus, 0, NULL);
 }
 EXPORT_SYMBOL_GPL(of_cpufreq_cooling_register);
 
 /**
+ * cpufreq_power_cooling_register() - create cpufreq cooling device with power extensions
+ * @clip_cpus:	cpumask of cpus where the frequency constraints will happen
+ * @capacitance:	dynamic power coefficient for these cpus
+ * @plat_static_func:	function to calculate the static power consumed by these
+ *			cpus (optional)
+ *
+ * This interface function registers the cpufreq cooling device with
+ * the name "thermal-cpufreq-%x".  This api can support multiple
+ * instances of cpufreq cooling devices.  Using this function, the
+ * cooling device will implement the power extensions by using a
+ * simple cpu power model.  The cpus must have registered their OPPs
+ * using the OPP library.
+ *
+ * An optional @plat_static_func may be provided to calculate the
+ * static power consumed by these cpus.  If the platform's static
+ * power consumption is unknown or negligible, make it NULL.
+ *
+ * Return: a valid struct thermal_cooling_device pointer on success,
+ * on failure, it returns a corresponding ERR_PTR().
+ */
+struct thermal_cooling_device *
+cpufreq_power_cooling_register(const struct cpumask *clip_cpus, u32 capacitance,
+			get_static_t plat_static_func)
+{
+	return __cpufreq_cooling_register(NULL, clip_cpus, capacitance,
+				plat_static_func);
+}
+EXPORT_SYMBOL(cpufreq_power_cooling_register);
+
+/**
+ * of_cpufreq_power_cooling_register() - create cpufreq cooling device with power extensions
+ * @np:	a valid struct device_node to the cooling device device tree node
+ * @clip_cpus:	cpumask of cpus where the frequency constraints will happen
+ * @capacitance:	dynamic power coefficient for these cpus
+ * @plat_static_func:	function to calculate the static power consumed by these
+ *			cpus (optional)
+ *
+ * This interface function registers the cpufreq cooling device with
+ * the name "thermal-cpufreq-%x".  This api can support multiple
+ * instances of cpufreq cooling devices.  Using this API, the cpufreq
+ * cooling device will be linked to the device tree node provided.
+ * Using this function, the cooling device will implement the power
+ * extensions by using a simple cpu power model.  The cpus must have
+ * registered their OPPs using the OPP library.
+ *
+ * An optional @plat_static_func may be provided to calculate the
+ * static power consumed by these cpus.  If the platform's static
+ * power consumption is unknown or negligible, make it NULL.
+ *
+ * Return: a valid struct thermal_cooling_device pointer on success,
+ * on failure, it returns a corresponding ERR_PTR().
+ */
+struct thermal_cooling_device *
+of_cpufreq_power_cooling_register(struct device_node *np,
+			const struct cpumask *clip_cpus, u32 capacitance,
+			get_static_t plat_static_func)
+{
+	if (!np)
+		return ERR_PTR(-EINVAL);
+
+	return __cpufreq_cooling_register(np, clip_cpus, capacitance,
+				plat_static_func);
+}
+EXPORT_SYMBOL(of_cpufreq_power_cooling_register);
+
+/**
  * cpufreq_cooling_unregister - function to remove cpufreq cooling device.
  * @cdev: thermal cooling device pointer.
  *
diff --git a/include/linux/cpu_cooling.h b/include/linux/cpu_cooling.h
index c303d383def1..5c4f4567acf0 100644
--- a/include/linux/cpu_cooling.h
+++ b/include/linux/cpu_cooling.h
@@ -28,6 +28,8 @@
 #include <linux/thermal.h>
 #include <linux/cpumask.h>
 
+typedef u32 (*get_static_t)(cpumask_t *cpumask, unsigned long voltage);
+
 #ifdef CONFIG_CPU_THERMAL
 /**
  * cpufreq_cooling_register - function to create cpufreq cooling device.
@@ -37,14 +39,38 @@ struct thermal_cooling_device *
 cpufreq_cooling_register(const struct cpumask *clip_cpus);
 
 /**
+ * cpufreq_power_cooling_register() - create cpufreq cooling device with power extensions
+ * @clip_cpus: cpumask of cpus where the frequency constraints will happen
+ * @capacitance:	dynamic power coefficient for these cpus
+ * @plat_static_func:	function to calculate the static power consumed by these
+ *			cpus (optional)
+ */
+struct thermal_cooling_device *
+cpufreq_power_cooling_register(const struct cpumask *clip_cpus,
+			u32 capacitance, get_static_t plat_static_func);
+
+#ifdef CONFIG_THERMAL_OF
+/**
  * of_cpufreq_cooling_register - create cpufreq cooling device based on DT.
  * @np: a valid struct device_node to the cooling device device tree node.
  * @clip_cpus: cpumask of cpus where the frequency constraints will happen
  */
-#ifdef CONFIG_THERMAL_OF
 struct thermal_cooling_device *
 of_cpufreq_cooling_register(struct device_node *np,
 			    const struct cpumask *clip_cpus);
+
+/**
+ * of_cpufreq_power_cooling_register() - create cpufreq cooling device with power extensions
+ * @np:	a valid struct device_node to the cooling device device tree node
+ * @clip_cpus:	cpumask of cpus where the frequency constraints will happen
+ * @capacitance:	dynamic power coefficient for these cpus
+ * @plat_static_func:	function to calculate the static power consumed by these
+ *			cpus (optional)
+ */
+struct thermal_cooling_device *
+of_cpufreq_power_cooling_register(struct device_node *np,
+				const struct cpumask *clip_cpus,
+				u32 capacitance, get_static_t plat_static_func);
 #else
 static inline struct thermal_cooling_device *
 of_cpufreq_cooling_register(struct device_node *np,
@@ -52,6 +78,14 @@ of_cpufreq_cooling_register(struct device_node *np,
 {
 	return NULL;
 }
+
+struct thermal_cooling_device *
+of_cpufreq_power_cooling_register(struct device_node *np,
+				const struct cpumask *clip_cpus,
+				u32 capacitance, get_static_t plat_static_func)
+{
+	return NULL;
+}
 #endif
 
 /**
@@ -68,11 +102,24 @@ cpufreq_cooling_register(const struct cpumask *clip_cpus)
 	return NULL;
 }
 static inline struct thermal_cooling_device *
+cpufreq_power_cooling_register(const struct cpumask *clip_cpus,
+			u32 capacitance, get_static_t plat_static_func)
+{
+	return NULL;
+}
+static inline struct thermal_cooling_device *
 of_cpufreq_cooling_register(struct device_node *np,
 			    const struct cpumask *clip_cpus)
 {
 	return NULL;
 }
+static inline struct thermal_cooling_device *
+of_cpufreq_power_cooling_register(struct device_node *np,
+				const struct cpumask *clip_cpus,
+				u32 capacitance, get_static_t plat_static_func)
+{
+	return NULL;
+}
 static inline
 void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
 {


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 6/9] thermal: cpu_cooling: implement the power cooling device API
  2014-12-09 11:00                 ` Javi Merino
@ 2014-12-09 11:06                   ` Viresh Kumar
  2014-12-09 11:23                     ` Javi Merino
  2015-01-02 14:37                   ` Eduardo Valentin
  1 sibling, 1 reply; 48+ messages in thread
From: Viresh Kumar @ 2014-12-09 11:06 UTC (permalink / raw)
  To: Javi Merino
  Cc: Linux PM list, linux-kernel, Punit Agrawal, Mark Brown,
	Zhang Rui, Eduardo Valentin

On 9 December 2014 at 16:30, Javi Merino <javi.merino@arm.com> wrote:
> Ok, how about this then?  I've pasted the whole commit so as to avoid
> confusion.

Yeah, the cpu_dev part looks fine now.

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 6/9] thermal: cpu_cooling: implement the power cooling device API
  2014-12-09 11:06                   ` Viresh Kumar
@ 2014-12-09 11:23                     ` Javi Merino
  0 siblings, 0 replies; 48+ messages in thread
From: Javi Merino @ 2014-12-09 11:23 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: Linux PM list, linux-kernel, Punit Agrawal, Mark Brown,
	Zhang Rui, Eduardo Valentin

On Tue, Dec 09, 2014 at 11:06:46AM +0000, Viresh Kumar wrote:
> On 9 December 2014 at 16:30, Javi Merino <javi.merino@arm.com> wrote:
> > Ok, how about this then?  I've pasted the whole commit so as to avoid
> > confusion.
> 
> Yeah, the cpu_dev part looks fine now.

Great, thanks!


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 1/9] tracing: Add array printing helpers
  2014-12-08 16:04     ` Dave P Martin
@ 2014-12-10 10:52       ` Javi Merino
  0 siblings, 0 replies; 48+ messages in thread
From: Javi Merino @ 2014-12-10 10:52 UTC (permalink / raw)
  To: Dave P Martin, Steven Rostedt
  Cc: linux-pm, linux-kernel, Punit Agrawal, broonie, Ingo Molnar

On Mon, Dec 08, 2014 at 04:04:52PM +0000, Dave P Martin wrote:
> On Mon, Dec 08, 2014 at 03:42:10PM +0000, Steven Rostedt wrote:
> > On Fri,  5 Dec 2014 19:04:12 +0000
> > "Javi Merino" <javi.merino@arm.com> wrote:
> 
> [...]
> 
> > > +
> > > +DEFINE_PRINT_ARRAY(u8, unsigned int, "0x%x");
> > > +DEFINE_PRINT_ARRAY(u16, unsigned int, "0x%x");
> > > +DEFINE_PRINT_ARRAY(u32, unsigned int, "0x%x");
> > > +DEFINE_PRINT_ARRAY(u64, unsigned long long, "0x%llx");
> > > +
> > 
> > I would really like to avoid adding a bunch of macros for each type.
> > Can't we have something like this:
> > ftrace_print_array(struct trace_seq *p, void *buf, int buf_len, 
> > 		int size)
> > {
> > 	char *prefix = "";
> > 	void *ptr = buf;
> > 
> > 	while (ptr < buf + buf_len) {
> > 		switch(size) {
> > 		case 8:
> > 			trace_seq_printf("%s0x%x", prefix,
> > 				*(unsigned char *)ptr);
> 
> I think this should be *(u8 *) etc.

Done, see below.

> Otherwise, I don't have a problem with this approach.  It's less
> ugly than my original.

It makes the lib traceevent patches uglier though ;)

> > 			break;
> > 		case 16:
> > 			trace_seq_printf("%s0x%x", prefix,
> > 				*(unsigned short *)ptr);
> > 			break;
> > 		case 32:
> > 			trace_seq_printf("%s0x%x", prefix,
> > 				*(unsigned int *)ptr);
> > 			break;
> > 		case 64:
> > 			trace_seq_printf("%s0x%llx", prefix,
> > 				*(unsigned long long *)ptr);
> > 			break;
> > 		default:
> > 			BUG();
> > 		}
> > 		prefix = ",";
> > 		ptr += size;
> > 	}
> > 
> > }
> > 
> > We probably could even make the "BUG()" into a build bug, with a little
> > work.
> 
> That sounds possible.

The only way I can think of doing that is by moving the check to the
__print_array macro:

#define __print_array(array, count, el_size)			\
	({								\
		BUILD_BUG_ON(el_size != 8 && el_size != 16 && el_size != 32 && el_size != 64); \
		ftrace_print_array_seq(p, array, count, el_size); \
	})

Is this what you have in mind?

> Javi?

What about this?

diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h
index 28672e87e910..d5bddb230ecd 100644
--- a/include/linux/ftrace_event.h
+++ b/include/linux/ftrace_event.h
@@ -44,6 +44,9 @@ const char *ftrace_print_bitmask_seq(struct trace_seq *p, void *bitmask_ptr,
 const char *ftrace_print_hex_seq(struct trace_seq *p,
 				 const unsigned char *buf, int len);
 
+const char *ftrace_print_array_seq(struct trace_seq *p,
+				const void *buf, int buf_len, size_t el_size);
+
 struct trace_iterator;
 struct trace_event;
 
diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h
index 26b4f2e13275..38c5f91f63da 100644
--- a/include/trace/ftrace.h
+++ b/include/trace/ftrace.h
@@ -263,6 +263,10 @@
 #undef __print_hex
 #define __print_hex(buf, buf_len) ftrace_print_hex_seq(p, buf, buf_len)
 
+#undef __print_array
+#define __print_array(array, count, el_size)			\
+	ftrace_print_array_seq(p, array, count, el_size)
+
 #undef DECLARE_EVENT_CLASS
 #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print)	\
 static notrace enum print_line_t					\
@@ -676,6 +680,7 @@ static inline void ftrace_test_probe_##call(void)			\
 #undef __get_dynamic_array_len
 #undef __get_str
 #undef __get_bitmask
+#undef __print_array
 
 #undef TP_printk
 #define TP_printk(fmt, args...) "\"" fmt "\", "  __stringify(args)
diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c
index c6977d5a9b12..b582261086e8 100644
--- a/kernel/trace/trace_output.c
+++ b/kernel/trace/trace_output.c
@@ -186,6 +186,48 @@ ftrace_print_hex_seq(struct trace_seq *p, const unsigned char *buf, int buf_len)
 }
 EXPORT_SYMBOL(ftrace_print_hex_seq);
 
+const char *
+ftrace_print_array_seq(struct trace_seq *p, const void *buf, int buf_len,
+		       size_t el_size)
+{
+	const char *ret = trace_seq_buffer_ptr(p);
+	const char *prefix = "";
+	void *ptr = (void *)buf;
+
+	trace_seq_putc(p, '{');
+
+	while (ptr < buf + buf_len) {
+		switch(el_size) {
+		case 8:
+			trace_seq_printf(p, "%s0x%x", prefix,
+					*(u8 *)ptr);
+			break;
+		case 16:
+			trace_seq_printf(p, "%s0x%x", prefix,
+					*(u16 *)ptr);
+			break;
+		case 32:
+			trace_seq_printf(p, "%s0x%x", prefix,
+					*(u32 *)ptr);
+			break;
+		case 64:
+			trace_seq_printf(p, "%s0x%llx", prefix,
+					*(u64 *)ptr);
+			break;
+		default:
+			BUG();
+		}
+		prefix = ",";
+		ptr += el_size / 8;
+	}
+
+	trace_seq_putc(p, '}');
+	trace_seq_putc(p, 0);
+
+	return ret;
+}
+EXPORT_SYMBOL(ftrace_print_array_seq);
+
 int ftrace_raw_output_prep(struct trace_iterator *iter,
 			   struct trace_event *trace_event)
 {
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 5/9] thermal: extend the cooling device API to include power information
  2014-12-05 19:04 ` [RFC PATCH v6 5/9] thermal: extend the cooling device API to include power information Javi Merino
@ 2014-12-23 15:14   ` Eduardo Valentin
  2015-01-05 15:37     ` Javi Merino
  0 siblings, 1 reply; 48+ messages in thread
From: Eduardo Valentin @ 2014-12-23 15:14 UTC (permalink / raw)
  To: Javi Merino; +Cc: linux-pm, linux-kernel, punit.agrawal, broonie, Zhang Rui

[-- Attachment #1: Type: text/plain, Size: 5695 bytes --]

Hi Javi

On Fri, Dec 05, 2014 at 07:04:16PM +0000, Javi Merino wrote:
> Add three optional callbacks to the cooling device interface to allow
> them to express power.  In addition to the callbacks, add helpers to
> identify cooling devices that implement the power cooling device API.
> 
> Cc: Zhang Rui <rui.zhang@intel.com>
> Cc: Eduardo Valentin <edubezval@gmail.com>
> Signed-off-by: Javi Merino <javi.merino@arm.com>
> ---
>  Documentation/thermal/power_allocator.txt | 27 ++++++++++++++++++++++
>  drivers/thermal/thermal_core.c            | 38 +++++++++++++++++++++++++++++++
>  include/linux/thermal.h                   | 12 ++++++++++
>  3 files changed, 77 insertions(+)
>  create mode 100644 Documentation/thermal/power_allocator.txt
> 
> diff --git a/Documentation/thermal/power_allocator.txt b/Documentation/thermal/power_allocator.txt
> new file mode 100644
> index 000000000000..d3bb79050c27
> --- /dev/null
> +++ b/Documentation/thermal/power_allocator.txt
> @@ -0,0 +1,27 @@
> +Cooling device power API
> +========================

Readers of this file need extra context here, IMO.

> +
> +Cooling devices controlled by this governor must supply the additional

What governor? the files says power allocator, and the title says,
cooling device power API...

> +"power" API in their `cooling_device_ops`.  It consists on three ops:
> +



> +1. u32 get_actual_power(struct thermal_cooling_device *cdev);
> +@cdev: The `struct thermal_cooling_device` pointer
> +
> +`get_actual_power()` returns the power currently consumed by the
> +device in milliwatts.
> +
> +2. u32 state2power(struct thermal_cooling_device *cdev, unsigned long
> +        state);
> +@cdev: The `struct thermal_cooling_device` pointer
> +@state: A cooling device state
> +
> +Convert cooling device state @state into power consumption in
> +milliwatts.
> +
> +3. unsigned long power2state(struct thermal_cooling_device *cdev,
> +        u32 power);
> +@cdev: The `struct thermal_cooling_device` pointer
> +@power: power in milliwatts
> +
> +Calculate a cooling device state that would make the device consume at
> +most @power mW.

I believe it would be more helpful if you could provide extra context in
which the above functions are called, and for what.

> diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
> index 9021cb72a13a..c490f262ea7f 100644
> --- a/drivers/thermal/thermal_core.c
> +++ b/drivers/thermal/thermal_core.c
> @@ -866,6 +866,44 @@ emul_temp_store(struct device *dev, struct device_attribute *attr,
>  static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store);
>  #endif/*CONFIG_THERMAL_EMULATION*/
>  
> +/**
> + * power_actor_get_max_power() - get the maximum power that a cdev can consume
> + * @cdev:	pointer to &thermal_cooling_device
> + *
> + * Calculate the maximum power consumption in milliwats that the
> + * cooling device can currently consume.  If @cdev doesn't support the
> + * power_actor API, this function returns 0.
> + */
> +u32 power_actor_get_max_power(struct thermal_cooling_device *cdev)
> +{
> +	if (!cdev_is_power_actor(cdev))
> +		return 0;
> +
> +	return cdev->ops->state2power(cdev, 0);
> +}
> +
> +/**
> + * power_actor_set_power() - limit the maximum power that a cooling device can consume
> + * @cdev:	pointer to &thermal_cooling_device
> + * @power:	the power in milliwatts
> + *
> + * Set the cooling device to consume at most @power milliwatts.
> + *
> + * Returns: 0 on success, -EINVAL if the cooling device does not
> + * implement the power actor API or -E* for other failures.
> + */
> +int power_actor_set_power(struct thermal_cooling_device *cdev, u32 power)
> +{
> +	unsigned long state;
> +
> +	if (!cdev_is_power_actor(cdev))
> +		return -EINVAL;
> +
> +	state = cdev->ops->power2state(cdev, power);
> +
> +	return cdev->ops->set_cur_state(cdev, state);
> +}
> +
>  static DEVICE_ATTR(type, 0444, type_show, NULL);
>  static DEVICE_ATTR(temp, 0444, temp_show, NULL);
>  static DEVICE_ATTR(mode, 0644, mode_show, mode_store);
> diff --git a/include/linux/thermal.h b/include/linux/thermal.h
> index 2c14ab1f5c0d..1155457caf52 100644
> --- a/include/linux/thermal.h
> +++ b/include/linux/thermal.h
> @@ -142,6 +142,9 @@ struct thermal_cooling_device_ops {
>  	int (*get_max_state) (struct thermal_cooling_device *, unsigned long *);
>  	int (*get_cur_state) (struct thermal_cooling_device *, unsigned long *);
>  	int (*set_cur_state) (struct thermal_cooling_device *, unsigned long);
> +	u32 (*get_actual_power) (struct thermal_cooling_device *);
> +	u32 (*state2power) (struct thermal_cooling_device *, unsigned long);
> +	unsigned long (*power2state) (struct thermal_cooling_device *, u32);
>  };
>  
>  struct thermal_cooling_device {
> @@ -322,6 +325,15 @@ void thermal_zone_of_sensor_unregister(struct device *dev,
>  }
>  
>  #endif
> +
> +static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev)
> +{

What would happen if one pass cdev == NULL?

> +	return cdev->ops->get_actual_power && cdev->ops->state2power &&
> +		cdev->ops->power2state;
> +}
> +
> +u32 power_actor_get_max_power(struct thermal_cooling_device *);
> +int power_actor_set_power(struct thermal_cooling_device *, u32);
>  struct thermal_zone_device *thermal_zone_device_register(const char *, int, int,
>  		void *, struct thermal_zone_device_ops *,
>  		const struct thermal_zone_params *, int, int);

I am assuming the above two new functions are expected to be used also
outside thermal core, right? If yes, I'd suggest exporting them. 

> -- 
> 1.9.1
> 
> 

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 6/9] thermal: cpu_cooling: implement the power cooling device API
  2014-12-09 11:00                 ` Javi Merino
  2014-12-09 11:06                   ` Viresh Kumar
@ 2015-01-02 14:37                   ` Eduardo Valentin
  2015-01-05 16:53                     ` Javi Merino
  1 sibling, 1 reply; 48+ messages in thread
From: Eduardo Valentin @ 2015-01-02 14:37 UTC (permalink / raw)
  To: Javi Merino
  Cc: Viresh Kumar, Linux PM list, linux-kernel, Punit Agrawal,
	Mark Brown, Zhang Rui

[-- Attachment #1: Type: text/plain, Size: 32301 bytes --]

Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

Hello Javi,

Looks like the charset seams to be scrambled. Anyways, I will attempt to
send a couple of feedback here..

On Tue, Dec 09, 2014 at 11:00:43AM +0000, Javi Merino wrote:
> On Tue, Dec 09, 2014 at 10:36:46AM +0000, Viresh Kumar wrote:
> > On 9 December 2014 at 16:02, Javi Merino <javi.merino@arm.com> wrote:
> > > Sorry but I don't follow.  __cpufreq_cooling_register() is passed a
> > > clip_cpus mask, not a single cpu.  How do I get "the cpu for which
> > > __cpufreq_cooling_register() is called" if not by looping through all
> > > the cpus in the mask?
> >=20
> > Yeah, its np that is passed instead of cpu number. So, that wouldn't
> > be usable. Also because of the limitations I explained earlier, it makes
> > sense to iterate over all clip_cpus and finding which one owns OPPs.
>=20
> Ok, how about this then?  I've pasted the whole commit so as to avoid
> confusion.

I should consider this one as V7 of this patch, probably..

>=20
> diff --git a/Documentation/thermal/cpu-cooling-api.txt b/Documentation/th=
ermal/cpu-cooling-api.txt
> index fca24c931ec8..d438a900e374 100644
> --- a/Documentation/thermal/cpu-cooling-api.txt
> +++ b/Documentation/thermal/cpu-cooling-api.txt
> @@ -25,8 +25,150 @@ the user. The registration APIs returns the cooling d=
evice pointer.
> =20
>     clip_cpus: cpumask of cpus where the frequency constraints will happe=
n.
> =20
> -1.1.2 void cpufreq_cooling_unregister(struct thermal_cooling_device *cde=
v)
> +1.1.2 struct thermal_cooling_device *cpufreq_power_cooling_register(
> +    const struct cpumask *clip_cpus, u32 capacitance,
> +    get_static_t plat_static_func)
> +
> +Similar to cpufreq_cooling_register, this function registers a cpufreq
> +cooling device.  Using this function, the cooling device will
> +implement the power extensions by using a simple cpu power model.  The
> +cpus must have registered their OPPs using the OPP library.
> +
> +The additional parameters are needed for the power model (See 2. Power
> +models).  "capacitance" is the dynamic power coefficient (See 2.1
> +Dynamic power).  "plat_static_func" is a function to calculate the
> +static power consumed by these cpus (See 2.2 Static power).
> +
> +1.1.3 struct thermal_cooling_device *of_cpufreq_power_cooling_register(
> +    struct device_node *np, const struct cpumask *clip_cpus, u32 capacit=
ance,
> +    get_static_t plat_static_func)
> +
> +Similar to cpufreq_power_cooling_register, this function register a
> +cpufreq cooling device with power extensions using the device tree
> +information supplied by the np parameter.
> +
> +1.1.4 void cpufreq_cooling_unregister(struct thermal_cooling_device *cde=
v)
> =20
>      This interface function unregisters the "thermal-cpufreq-%x" cooling=
 device.
> =20
>      cdev: Cooling device pointer which has to be unregistered.
> +
> +2. Power models
> +
> +The power API registration functions provide a simple power model for
> +CPUs.  The current power is calculated as dynamic + (optionally)
> +static power.  This power model requires that the operating-points of
> +the CPUs are registered using the kernel's opp library and the
> +`cpufreq_frequency_table` is assigned to the `struct device` of the
> +cpu.  If you are using the `cpufreq-cpu0.c` driver then the

cpufreq-cpu0.c is the old version of cpufreq-dt.c, right? I would
suggest using CONFIG_* names instead of file names though.

> +`cpufreq_frequency_table` should already be assigned to the cpu
> +device.
> +
> +The `plat_static_func` parameter of `cpufreq_power_cooling_register()`
> +and `of_cpufreq_power_cooling_register()` is optional.  If you don't
> +provide it, only dynamic power will be considered.
> +
> +2.1 Dynamic power
> +
> +The dynamic power consumption of a processor depends on many factors.
> +For a given processor implementation the primary factors are:
> +
> +- The time the processor spends running, consuming dynamic power, as
> +  compared to the time in idle states where dynamic consumption is
> +  negligible.  Herein we refer to this as 'utilisation'.
> +- The voltage and frequency levels as a result of DVFS.  The DVFS
> +  level is a dominant factor governing power consumption.
> +- In running time the 'execution' behaviour (instruction types, memory
> +  access patterns and so forth) causes, in most cases, a second order
> +  variation.  In pathological cases this variation can be significant,
> +  but typically it is of a much lesser impact than the factors above.
> +
> +A high level dynamic power consumption model may then be represented as:
> +
> +Pdyn =3D f(run) * Voltage^2 * Frequency * Utilisation
> +
> +f(run) here represents the described execution behaviour and its
> +result has a units of Watts/Hz/Volt^2 (this often expressed in
> +mW/MHz/uVolt^2)
> +
> +The detailed behaviour for f(run) could be modelled on-line.  However,
> +in practice, such an on-line model has dependencies on a number of
> +implementation specific processor support and characterisation
> +factors.  Therefore, in initial implementation that contribution is
> +represented as a constant coefficient.  This is a simplification
> +consistent with the relative contribution to overall power variation.
> +
> +In this simplified representation our model becomes:
> +
> +Pdyn =3D Kd * Voltage^2 * Frequency * Utilisation
> +
> +Where Kd (capacitance) represents an indicative running time dynamic
> +power coefficient in fundamental units of mW/MHz/uVolt^2
> +

Do we have Kd (capacitance) reference values for ARM processors? Is it
worth adding a few of them as an example table here?=20

Where does one find Kd values?

Just looking for pointers for platform driver writers (potential users
of these APIs).

> +2.2 Static power
> +
> +Static leakage power consumption depends on a number of factors.  For a
> +given circuit implementation the primary factors are:
> +
> +- Time the circuit spends in each 'power state'
> +- Temperature
> +- Operating voltage
> +- Process grade
> +
> +The time the circuit spends in each 'power state' for a given
> +evaluation period at first order means OFF or ON.  However,
> +'retention' states can also be supported that reduce power during
> +inactive periods without loss of context.
> +
> +Note: The visibility of state entries to the OS can vary, according to
> +platform specifics, and this can then impact the accuracy of a model
> +based on OS state information alone.  It might be possible in some
> +cases to extract more accurate information from system resources.
> +
> +The temperature, operating voltage and process 'grade' (slow to fast)
> +of the circuit are all significant factors in static leakage power
> +consumption.  All of these have complex relationships to static power.
> +
> +Circuit implementation specific factors include the chosen silicon
> +process as well as the type, number and size of transistors in both
> +the logic gates and any RAM elements included.
> +
> +The static power consumption modelling must take into account the
> +power managed regions that are implemented.  Taking the example of an
> +ARM processor cluster, the modelling would take into account whether
> +each CPU can be powered OFF separately or if only a single power
> +region is implemented for the complete cluster.
> +
> +In one view, there are others, a static power consumption model can
> +then start from a set of reference values for each power managed
> +region (e.g. CPU, Cluster/L2) in each state (e.g. ON, OFF) at an
> +arbitrary process grade, voltage and temperature point.  These values
> +are then scaled for all of the following: the time in each state, the
> +process grade, the current temperature and the operating voltage.
> +However, since both implementation specific and complex relationships
> +dominate the estimate, the appropriate interface to the model from the
> +cpu cooling device is to provide a function callback that calculates
> +the static power in this platform.  When registering the cpu cooling
> +device pass a function pointer that follows the `get_static_t`
> +prototype:
> +
> +    u32 plat_get_static(cpumask_t *cpumask, unsigned long voltage);
> +
> +with `cpumask` a cpumask of the cpus involved in the calculation and
> +`voltage` the voltage at which they are operating.
> +

What is the expected behavior of 'plat_get_static' if a wrong parameter
is passed? Say, a cpumask that is invalid or a unsupported voltage?
Shall it return 0? Does 0 means error?

Besides, how is the platform code supposed to return the estimate, given
it depends on time spent in state, and we are not passing any info about
time here? Same question applies to temperature. For voltage, we are
passing as parameter. For process grade, well, platform code is probably
best point to determine it, so, no need.

> +If `plat_static_func` is NULL, static power is considered to be
> +negligible for this platform and only dynamic power is considered.
> +
> +The platform specific callback can then use any combination of tables
> +and/or equations to permute the estimated value.  Process grade
> +information is not passed to the model since access to such data, from
> +on-chip measurement capability or manufacture time data, is platform
> +specific.
> +

agreed

> +Note: the significance of static power for CPUs in comparison to
> +dynamic power is highly dependent on implementation.  Given the
> +potential complexity in implementation, the importance and accuracy of
> +its inclusion when using cpu cooling devices should be assessed on a
> +case by cases basis.
> +
> diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
> index ad09e51ffae4..959a103d18ba 100644
> --- a/drivers/thermal/cpu_cooling.c
> +++ b/drivers/thermal/cpu_cooling.c
> @@ -24,11 +24,25 @@
>  #include <linux/thermal.h>
>  #include <linux/cpufreq.h>
>  #include <linux/err.h>
> +#include <linux/pm_opp.h>
>  #include <linux/slab.h>
>  #include <linux/cpu.h>
>  #include <linux/cpu_cooling.h>
> =20
>  /**
> + * struct power_table - frequency to power conversion
> + * @frequency:	frequency in KHz
> + * @power:	power in mW
> + *
> + * This structure is built when the cooling device registers and helps
> + * in translating frequency to power and viceversa.
> + */
> +struct power_table {
> +	u32 frequency;
> +	u32 power;
> +};
> +
> +/**
>   * struct cpufreq_cooling_device - data for cooling device with cpufreq
>   * @id: unique integer value corresponding to each cpufreq_cooling_device
>   *	registered.
> @@ -39,6 +53,15 @@
>   * @cpufreq_val: integer value representing the absolute value of the cl=
ipped
>   *	frequency.
>   * @allowed_cpus: all the cpus involved for this cpufreq_cooling_device.
> + * @last_load: load measured by the latest call to cpufreq_get_actual_po=
wer()
> + * @time_in_idle: previous reading of the absolute time that this cpu wa=
s idle
> + * @time_in_idle_timestamp: wall time of the last invocation of
> + *	get_cpu_idle_time_us()
> + * @dyn_power_table: array of struct power_table for frequency to power
> + *	conversion


Is this ordered somehow? Is it worth mentioning?

> + * @dyn_power_table_entries: number of entries in the @dyn_power_table a=
rray
> + * @cpu_dev: the first cpu_device from @allowed_cpus that has OPPs regis=
tered
> + * @plat_get_static_power: callback to calculate the static power
>   *
>   * This structure is required for keeping information of each
>   * cpufreq_cooling_device registered. In order to prevent corruption of =
this a
> @@ -51,6 +74,13 @@ struct cpufreq_cooling_device {
>  	unsigned int cpufreq_val;
>  	struct cpumask allowed_cpus;
>  	struct list_head node;
> +	u32 last_load;
> +	u64 time_in_idle[NR_CPUS];
> +	u64 time_in_idle_timestamp[NR_CPUS];
> +	struct power_table *dyn_power_table;
> +	int dyn_power_table_entries;
> +	struct device *cpu_dev;
> +	get_static_t plat_get_static_power;
>  };
>  static DEFINE_IDR(cpufreq_idr);
>  static DEFINE_MUTEX(cooling_cpufreq_lock);
> @@ -338,6 +368,204 @@ static int cpufreq_thermal_notifier(struct notifier=
_block *nb,
>  	return 0;
>  }
> =20
> +/**
> + * build_dyn_power_table() - create a dynamic power to frequency table
> + * @cpufreq_device:	the cpufreq cooling device in which to store the tab=
le
> + * @capacitance: dynamic power coefficient for these cpus
> + *
> + * Build a dynamic power to frequency table for this cpu and store it
> + * in @cpufreq_device.  This table will be used in cpu_power_to_freq() a=
nd
> + * cpu_freq_to_power() to convert between power and frequency
> + * efficiently.  Power is stored in mW, frequency in KHz.  The
> + * resulting table is in ascending order.

by which parameter? Do we assume a increasing convex relation between
power and frequency?

> + *
> + * Return: 0 on success, -E* on error.
> + */
> +static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_=
device,
> +				u32 capacitance)
> +{
> +	struct power_table *power_table;
> +	struct dev_pm_opp *opp;
> +	struct device *dev =3D NULL;
> +	int num_opps =3D 0, cpu, i, ret =3D 0;
> +	unsigned long freq;
> +
> +	rcu_read_lock();
> +
> +	for_each_cpu(cpu, &cpufreq_device->allowed_cpus) {
> +		dev =3D get_cpu_device(cpu);
> +		if (!dev) {
> +			dev_warn(&cpufreq_device->cool_dev->device,
> +				"No cpu device for cpu %d\n", cpu);
> +			continue;
> +		}
> +
> +		num_opps =3D dev_pm_opp_get_opp_count(dev);
> +		if (num_opps > 0) {
> +			break;
> +		} else if (num_opps < 0) {
> +			ret =3D num_opps;
> +			goto unlock;
> +		}
> +	}
> +
> +	if (num_opps =3D=3D 0) {
> +		ret =3D -EINVAL;
> +		goto unlock;
> +	}
> +
> +	power_table =3D devm_kcalloc(&cpufreq_device->cool_dev->device, num_opp=
s,
> +				sizeof(*power_table), GFP_KERNEL);
> +
> +	for (freq =3D 0, i =3D 0;
> +	     opp =3D dev_pm_opp_find_freq_ceil(dev, &freq), !IS_ERR(opp);
> +	     freq++, i++) {
> +		u32 freq_mhz, voltage_mv;
> +		u64 power;
> +
> +		freq_mhz =3D freq / 1000000;
> +		voltage_mv =3D dev_pm_opp_get_voltage(opp) / 1000;
> +
> +		/*
> +		 * Do the multiplication with MHz and millivolt so as
> +		 * to not overflow.
> +		 */
> +		power =3D (u64)capacitance * freq_mhz * voltage_mv * voltage_mv;
> +		do_div(power, 1000000000);
> +
> +		/* frequency is stored in power_table in KHz */
> +		power_table[i].frequency =3D freq / 1000;

Why do we have a comment about freq unit but no comment about power unit? :=
-)

> +		power_table[i].power =3D power;
> +	}
> +
> +	if (i =3D=3D 0) {
> +		ret =3D PTR_ERR(opp);
> +		goto unlock;
> +	}
> +
> +	cpufreq_device->cpu_dev =3D dev;
> +	cpufreq_device->dyn_power_table =3D power_table;
> +	cpufreq_device->dyn_power_table_entries =3D i;
> +
> +unlock:
> +	rcu_read_unlock();
> +	return ret;
> +}
> +
> +static u32 cpu_freq_to_power(struct cpufreq_cooling_device *cpufreq_devi=
ce,
> +			     u32 freq)
> +{
> +	int i;
> +	struct power_table *pt =3D cpufreq_device->dyn_power_table;
> +
> +	for (i =3D 1; i < cpufreq_device->dyn_power_table_entries; i++)
> +		if (freq < pt[i].frequency)
> +			break;
> +
> +	return pt[i - 1].power;
> +}
> +
> +static u32 cpu_power_to_freq(struct cpufreq_cooling_device *cpufreq_devi=
ce,
> +			u32 power)
> +{
> +	int i;
> +	struct power_table *pt =3D cpufreq_device->dyn_power_table;
> +
> +	for (i =3D 1; i < cpufreq_device->dyn_power_table_entries; i++)
> +		if (power < pt[i].power)
> +			break;
> +
> +	return pt[i - 1].frequency;
> +}
> +
> +/**
> + * get_load() - get load for a cpu since last updated
> + * @cpufreq_device:	&struct cpufreq_cooling_device for this cpu
> + * @cpu:	cpu number
> + *
> + * Return: The average load of cpu @cpu in percentage since this
> + * function was last called.
> + */
> +static u32 get_load(struct cpufreq_cooling_device *cpufreq_device, int c=
pu)
> +{
> +	u32 load;
> +	u64 now, now_idle, delta_time, delta_idle;
> +
> +	now_idle =3D get_cpu_idle_time(cpu, &now, 0);
> +	delta_idle =3D now_idle - cpufreq_device->time_in_idle[cpu];
> +	delta_time =3D now - cpufreq_device->time_in_idle_timestamp[cpu];
> +
> +	if (delta_time <=3D delta_idle)
> +		load =3D 0;
> +	else
> +		load =3D div64_u64(100 * (delta_time - delta_idle), delta_time);
> +
> +	cpufreq_device->time_in_idle[cpu] =3D now_idle;
> +	cpufreq_device->time_in_idle_timestamp[cpu] =3D now;
> +
> +	return load;
> +}
> +
> +/**
> + * get_static_power() - calculate the static power consumed by the cpus
> + * @cpufreq_device:	struct &cpufreq_cooling_device for this cpu cdev
> + * @freq:	frequency in KHz
> + *
> + * Calculate the static power consumed by the cpus described by
> + * @cpu_actor running at frequency @freq.  This function relies on a
> + * platform specific function that should have been provided when the
> + * actor was registered.  If it wasn't, the static power is assumed to
> + * be negligible.
> + *
> + * Return: The static power consumed by the cpus.  It returns 0 on
> + * error or if there is no plat_get_static_power().
> + */
> +static u32 get_static_power(struct cpufreq_cooling_device *cpufreq_devic=
e,
> +			unsigned long freq)
> +{
> +	struct dev_pm_opp *opp;
> +	unsigned long voltage;
> +	struct cpumask *cpumask =3D &cpufreq_device->allowed_cpus;
> +	unsigned long freq_hz =3D freq * 1000;
> +
> +	if (!cpufreq_device->plat_get_static_power)
> +		return 0;
> +
> +	rcu_read_lock();
> +
> +	opp =3D dev_pm_opp_find_freq_exact(cpufreq_device->cpu_dev, freq_hz,
> +					true);
> +	voltage =3D dev_pm_opp_get_voltage(opp);
> +
> +	rcu_read_unlock();
> +
> +	if (voltage =3D=3D 0) {
> +		dev_warn_ratelimited(cpufreq_device->cpu_dev,
> +				"Failed to get voltage for frequency %lu: %ld\n",
> +				freq_hz, IS_ERR(opp) ? PTR_ERR(opp) : 0);
> +		return 0;
> +	}
> +
> +	return cpufreq_device->plat_get_static_power(cpumask, voltage);

temperature ? time in idle state ?

> +}
> +
> +/**
> + * get_dynamic_power() - calculate the dynamic power
> + * @cpufreq_device:	&cpufreq_cooling_device for this cdev
> + * @freq:	current frequency
> + *

No description?


> + * Return: the dynamic power consumed by the cpus described by
> + * @cpufreq_device.
> + */
> +static u32 get_dynamic_power(struct cpufreq_cooling_device *cpufreq_devi=
ce,
> +			unsigned long freq)
> +{
> +	u32 raw_cpu_power;
> +
> +	raw_cpu_power =3D cpu_freq_to_power(cpufreq_device, freq);
> +	return (raw_cpu_power * cpufreq_device->last_load) / 100;
> +}
> +
>  /* cpufreq cooling device callback functions are defined below */
> =20
>  /**
> @@ -407,8 +635,106 @@ static int cpufreq_set_cur_state(struct thermal_coo=
ling_device *cdev,
>  	return cpufreq_apply_cooling(cpufreq_device, state);
>  }
> =20
> +/**
> + * cpufreq_get_actual_power() - get the current power
> + * @cdev:	&thermal_cooling_device pointer
> + *

ditto.

those should generate kerneldoc warns. Can you please run kerneldoc
script in your patch? make sure it does not add warns / errors.

> + * Return the current power consumption of the cpus in milliwatts.
> + */
> +static u32 cpufreq_get_actual_power(struct thermal_cooling_device *cdev)
> +{
> +	unsigned long freq;
> +	int cpu;
> +	u32 static_power, dynamic_power, total_load =3D 0;
> +	struct cpufreq_cooling_device *cpufreq_device =3D cdev->devdata;
> +
> +	freq =3D cpufreq_quick_get(cpumask_any(&cpufreq_device->allowed_cpus));
> +
> +	for_each_cpu(cpu, &cpufreq_device->allowed_cpus) {
> +		u32 load;
> +
> +		if (cpu_online(cpu))
> +			load =3D get_load(cpufreq_device, cpu);
> +		else
> +			load =3D 0;
> +
> +		total_load +=3D load;
> +	}
> +
> +	cpufreq_device->last_load =3D total_load;
> +
> +	static_power =3D get_static_power(cpufreq_device, freq);
> +	dynamic_power =3D get_dynamic_power(cpufreq_device, freq);
> +
> +	return static_power + dynamic_power;
> +}
> +
> +/**
> + * cpufreq_state2power() - convert a cpu cdev state to power consumed
> + * @cdev:	&thermal_cooling_device pointer
> + * @state:	cooling device state to be converted
> + *
> + * Convert cooling device state @state into power consumption in milliwa=
tts.

Considering 100% of utilization, right?


Return: ?

> + */
> +static u32 cpufreq_state2power(struct thermal_cooling_device *cdev,
> +			unsigned long state)
> +{
> +	unsigned int freq, num_cpus;
> +	cpumask_t cpumask;
> +	u32 static_power, dynamic_power;
> +	struct cpufreq_cooling_device *cpufreq_device =3D cdev->devdata;
> +
> +	cpumask_and(&cpumask, &cpufreq_device->allowed_cpus, cpu_online_mask);
> +	num_cpus =3D cpumask_weight(&cpumask);
> +
> +	freq =3D get_cpu_frequency(cpumask_any(&cpumask), state);
> +	if (!freq)
> +		return 0;
> +
> +	static_power =3D get_static_power(cpufreq_device, freq);
> +	dynamic_power =3D cpu_freq_to_power(cpufreq_device, freq) * num_cpus;
> +
> +	return static_power + dynamic_power;
> +}
> +
> +/**
> + * cpufreq_power2state() - convert power to a cooling device state
> + * @cdev:	&thermal_cooling_device pointer
> + * @power:	power in milliwatts to be converted
> + *
> + * Calculate a cooling device state for the cpus described by @cdev
> + * that would allow them to consume at most @power mW.

Return: ?=20

> + */
> +static unsigned long cpufreq_power2state(struct thermal_cooling_device *=
cdev,
> +					u32 power)
> +{
> +	unsigned int cpu, cur_freq, target_freq;
> +	s32 dyn_power;
> +	u32 last_load, normalised_power;
> +	unsigned long cdev_state;
> +	struct cpufreq_cooling_device *cpufreq_device =3D cdev->devdata;
> +
> +	cpu =3D cpumask_any_and(&cpufreq_device->allowed_cpus, cpu_online_mask);
> +
> +	cur_freq =3D cpufreq_quick_get(cpu);
> +	dyn_power =3D power - get_static_power(cpufreq_device, cur_freq);
> +	dyn_power =3D dyn_power > 0 ? dyn_power : 0;
> +	last_load =3D cpufreq_device->last_load ?: 1;
> +	normalised_power =3D (dyn_power * 100) / last_load;
> +	target_freq =3D cpu_power_to_freq(cpufreq_device, normalised_power);
> +


I got confused with the description vs. the implementation here.
Description says, a calculation from cooling device state to power. But
calling this function twice for the same power value, in different
moments, with difference cpu loads, (may) return different states
between calls.. Can you please improve description?

> +	cdev_state =3D cpufreq_cooling_get_level(cpu, target_freq);
> +	if (cdev_state =3D=3D THERMAL_CSTATE_INVALID) {
> +		pr_err_ratelimited("Failed to convert %dKHz for cpu %d into a cdev sta=
te\n",
> +				target_freq, cpu);
> +		return 0;

How about passing state as parameter and allowing the API to return an
error code?

> +	}
> +
> +	return cdev_state;
> +}
> +
>  /* Bind cpufreq callbacks to thermal cooling device ops */
> -static struct thermal_cooling_device_ops const cpufreq_cooling_ops =3D {
> +static struct thermal_cooling_device_ops cpufreq_cooling_ops =3D {

Why do we change the const?

>  	.get_max_state =3D cpufreq_get_max_state,
>  	.get_cur_state =3D cpufreq_get_cur_state,
>  	.set_cur_state =3D cpufreq_set_cur_state,
> @@ -434,7 +760,8 @@ static struct notifier_block thermal_cpufreq_notifier=
_block =3D {
>   */
>  static struct thermal_cooling_device *
>  __cpufreq_cooling_register(struct device_node *np,
> -			   const struct cpumask *clip_cpus)
> +			const struct cpumask *clip_cpus, u32 capacitance,
> +			get_static_t plat_static_func)
>  {
>  	struct thermal_cooling_device *cool_dev;
>  	struct cpufreq_cooling_device *cpufreq_dev =3D NULL;
> @@ -464,10 +791,23 @@ __cpufreq_cooling_register(struct device_node *np,
> =20
>  	cpumask_copy(&cpufreq_dev->allowed_cpus, clip_cpus);
> =20
> +	if (capacitance) {
> +		cpufreq_cooling_ops.get_actual_power =3D cpufreq_get_actual_power;
> +		cpufreq_cooling_ops.state2power =3D cpufreq_state2power;
> +		cpufreq_cooling_ops.power2state =3D cpufreq_power2state;
> +		cpufreq_dev->plat_get_static_power =3D plat_static_func;
> +
> +		ret =3D build_dyn_power_table(cpufreq_dev, capacitance);
> +		if (ret) {
> +			cool_dev =3D ERR_PTR(ret);
> +			goto free;
> +		}
> +	}
> +
>  	ret =3D get_idr(&cpufreq_idr, &cpufreq_dev->id);
>  	if (ret) {
> -		kfree(cpufreq_dev);
> -		return ERR_PTR(-EINVAL);
> +		cool_dev =3D ERR_PTR(-EINVAL);
> +		goto free;
>  	}
> =20
>  	snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d",
> @@ -475,11 +815,8 @@ __cpufreq_cooling_register(struct device_node *np,
> =20
>  	cool_dev =3D thermal_of_cooling_device_register(np, dev_name, cpufreq_d=
ev,
>  						      &cpufreq_cooling_ops);
> -	if (IS_ERR(cool_dev)) {
> -		release_idr(&cpufreq_idr, cpufreq_dev->id);
> -		kfree(cpufreq_dev);
> -		return cool_dev;
> -	}
> +	if (IS_ERR(cool_dev))
> +		goto release_idr;
>  	cpufreq_dev->cool_dev =3D cool_dev;
>  	cpufreq_dev->cpufreq_state =3D 0;
>  	mutex_lock(&cooling_cpufreq_lock);
> @@ -494,6 +831,12 @@ __cpufreq_cooling_register(struct device_node *np,
>  	mutex_unlock(&cooling_cpufreq_lock);
> =20
>  	return cool_dev;
> +
> +release_idr:
> +	release_idr(&cpufreq_idr, cpufreq_dev->id);
> +free:
> +	kfree(cpufreq_dev);
> +	return cool_dev;
>  }
> =20
>  /**
> @@ -510,7 +853,7 @@ __cpufreq_cooling_register(struct device_node *np,
>  struct thermal_cooling_device *
>  cpufreq_cooling_register(const struct cpumask *clip_cpus)
>  {
> -	return __cpufreq_cooling_register(NULL, clip_cpus);
> +	return __cpufreq_cooling_register(NULL, clip_cpus, 0, NULL);
>  }
>  EXPORT_SYMBOL_GPL(cpufreq_cooling_register);
> =20
> @@ -534,11 +877,77 @@ of_cpufreq_cooling_register(struct device_node *np,
>  	if (!np)
>  		return ERR_PTR(-EINVAL);
> =20
> -	return __cpufreq_cooling_register(np, clip_cpus);
> +	return __cpufreq_cooling_register(np, clip_cpus, 0, NULL);
>  }
>  EXPORT_SYMBOL_GPL(of_cpufreq_cooling_register);
> =20
>  /**
> + * cpufreq_power_cooling_register() - create cpufreq cooling device with=
 power extensions
> + * @clip_cpus:	cpumask of cpus where the frequency constraints will happ=
en
> + * @capacitance:	dynamic power coefficient for these cpus
> + * @plat_static_func:	function to calculate the static power consumed by=
 these
> + *			cpus (optional)
> + *
> + * This interface function registers the cpufreq cooling device with
> + * the name "thermal-cpufreq-%x".  This api can support multiple
> + * instances of cpufreq cooling devices.  Using this function, the
> + * cooling device will implement the power extensions by using a
> + * simple cpu power model.  The cpus must have registered their OPPs
> + * using the OPP library.
> + *
> + * An optional @plat_static_func may be provided to calculate the
> + * static power consumed by these cpus.  If the platform's static
> + * power consumption is unknown or negligible, make it NULL.
> + *
> + * Return: a valid struct thermal_cooling_device pointer on success,
> + * on failure, it returns a corresponding ERR_PTR().
> + */
> +struct thermal_cooling_device *
> +cpufreq_power_cooling_register(const struct cpumask *clip_cpus, u32 capa=
citance,
> +			get_static_t plat_static_func)
> +{
> +	return __cpufreq_cooling_register(NULL, clip_cpus, capacitance,
> +				plat_static_func);
> +}
> +EXPORT_SYMBOL(cpufreq_power_cooling_register);
> +
> +/**
> + * of_cpufreq_power_cooling_register() - create cpufreq cooling device w=
ith power extensions
> + * @np:	a valid struct device_node to the cooling device device tree node
> + * @clip_cpus:	cpumask of cpus where the frequency constraints will happ=
en
> + * @capacitance:	dynamic power coefficient for these cpus
> + * @plat_static_func:	function to calculate the static power consumed by=
 these
> + *			cpus (optional)
> + *
> + * This interface function registers the cpufreq cooling device with
> + * the name "thermal-cpufreq-%x".  This api can support multiple
> + * instances of cpufreq cooling devices.  Using this API, the cpufreq
> + * cooling device will be linked to the device tree node provided.
> + * Using this function, the cooling device will implement the power
> + * extensions by using a simple cpu power model.  The cpus must have
> + * registered their OPPs using the OPP library.
> + *
> + * An optional @plat_static_func may be provided to calculate the
> + * static power consumed by these cpus.  If the platform's static
> + * power consumption is unknown or negligible, make it NULL.
> + *
> + * Return: a valid struct thermal_cooling_device pointer on success,
> + * on failure, it returns a corresponding ERR_PTR().
> + */
> +struct thermal_cooling_device *
> +of_cpufreq_power_cooling_register(struct device_node *np,
> +			const struct cpumask *clip_cpus, u32 capacitance,
> +			get_static_t plat_static_func)
> +{
> +	if (!np)
> +		return ERR_PTR(-EINVAL);
> +
> +	return __cpufreq_cooling_register(np, clip_cpus, capacitance,
> +				plat_static_func);
> +}
> +EXPORT_SYMBOL(of_cpufreq_power_cooling_register);
> +
> +/**
>   * cpufreq_cooling_unregister - function to remove cpufreq cooling devic=
e.
>   * @cdev: thermal cooling device pointer.
>   *
> diff --git a/include/linux/cpu_cooling.h b/include/linux/cpu_cooling.h
> index c303d383def1..5c4f4567acf0 100644
> --- a/include/linux/cpu_cooling.h
> +++ b/include/linux/cpu_cooling.h
> @@ -28,6 +28,8 @@
>  #include <linux/thermal.h>
>  #include <linux/cpumask.h>
> =20
> +typedef u32 (*get_static_t)(cpumask_t *cpumask, unsigned long voltage);
> +
>  #ifdef CONFIG_CPU_THERMAL
>  /**
>   * cpufreq_cooling_register - function to create cpufreq cooling device.
> @@ -37,14 +39,38 @@ struct thermal_cooling_device *
>  cpufreq_cooling_register(const struct cpumask *clip_cpus);
> =20
>  /**
> + * cpufreq_power_cooling_register() - create cpufreq cooling device with=
 power extensions
> + * @clip_cpus: cpumask of cpus where the frequency constraints will happ=
en
> + * @capacitance:	dynamic power coefficient for these cpus
> + * @plat_static_func:	function to calculate the static power consumed by=
 these
> + *			cpus (optional)
> + */
> +struct thermal_cooling_device *
> +cpufreq_power_cooling_register(const struct cpumask *clip_cpus,
> +			u32 capacitance, get_static_t plat_static_func);
> +
> +#ifdef CONFIG_THERMAL_OF
> +/**
>   * of_cpufreq_cooling_register - create cpufreq cooling device based on =
DT.
>   * @np: a valid struct device_node to the cooling device device tree nod=
e.
>   * @clip_cpus: cpumask of cpus where the frequency constraints will happ=
en
>   */
> -#ifdef CONFIG_THERMAL_OF
>  struct thermal_cooling_device *
>  of_cpufreq_cooling_register(struct device_node *np,
>  			    const struct cpumask *clip_cpus);
> +
> +/**
> + * of_cpufreq_power_cooling_register() - create cpufreq cooling device w=
ith power extensions
> + * @np:	a valid struct device_node to the cooling device device tree node
> + * @clip_cpus:	cpumask of cpus where the frequency constraints will happ=
en
> + * @capacitance:	dynamic power coefficient for these cpus
> + * @plat_static_func:	function to calculate the static power consumed by=
 these
> + *			cpus (optional)
> + */

I think we should avoid duplicating kernel doc entries.=20

> +struct thermal_cooling_device *
> +of_cpufreq_power_cooling_register(struct device_node *np,
> +				const struct cpumask *clip_cpus,
> +				u32 capacitance, get_static_t plat_static_func);
>  #else
>  static inline struct thermal_cooling_device *
>  of_cpufreq_cooling_register(struct device_node *np,
> @@ -52,6 +78,14 @@ of_cpufreq_cooling_register(struct device_node *np,
>  {
>  	return NULL;
>  }
> +
> +struct thermal_cooling_device *
> +of_cpufreq_power_cooling_register(struct device_node *np,
> +				const struct cpumask *clip_cpus,
> +				u32 capacitance, get_static_t plat_static_func)

This is expected to be static inline.

> +{
> +	return NULL;
> +}
>  #endif
> =20
>  /**
> @@ -68,11 +102,24 @@ cpufreq_cooling_register(const struct cpumask *clip_=
cpus)
>  	return NULL;
>  }
>  static inline struct thermal_cooling_device *
> +cpufreq_power_cooling_register(const struct cpumask *clip_cpus,
> +			u32 capacitance, get_static_t plat_static_func)
> +{
> +	return NULL;
> +}
> +static inline struct thermal_cooling_device *
>  of_cpufreq_cooling_register(struct device_node *np,
>  			    const struct cpumask *clip_cpus)
>  {
>  	return NULL;
>  }
> +static inline struct thermal_cooling_device *
> +of_cpufreq_power_cooling_register(struct device_node *np,
> +				const struct cpumask *clip_cpus,
> +				u32 capacitance, get_static_t plat_static_func)
> +{
> +	return NULL;
> +}
>  static inline
>  void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
>  {
>=20

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 7/9] thermal: introduce the Power Allocator governor
  2014-12-05 19:04 ` [RFC PATCH v6 7/9] thermal: introduce the Power Allocator governor Javi Merino
@ 2015-01-02 15:46   ` Eduardo Valentin
  2015-01-06 13:23     ` Javi Merino
  2015-01-02 15:51   ` Eduardo Valentin
  1 sibling, 1 reply; 48+ messages in thread
From: Eduardo Valentin @ 2015-01-02 15:46 UTC (permalink / raw)
  To: Javi Merino; +Cc: linux-pm, linux-kernel, punit.agrawal, broonie, Zhang Rui

[-- Attachment #1: Type: text/plain, Size: 34327 bytes --]

Javi,

Minor items as follows..

On Fri, Dec 05, 2014 at 07:04:18PM +0000, Javi Merino wrote:
> The power allocator governor is a thermal governor that controls system
> and device power allocation to control temperature.  Conceptually, the
> implementation divides the sustainable power of a thermal zone among
> all the heat sources in that zone.
> 
> This governor relies on "power actors", entities that represent heat
> sources.  They can report current and maximum power consumption and
> can set a given maximum power consumption, usually via a cooling
> device.
> 
> The governor uses a Proportional Integral Derivative (PID) controller
> driven by the temperature of the thermal zone.  The output of the
> controller is a power budget that is then allocated to each power
> actor that can have bearing on the temperature we are trying to
> control.  It decides how much power to give each cooling device based
> on the performance they are requesting.  The PID controller ensures
> that the total power budget does not exceed the control temperature.
> 
> Cc: Zhang Rui <rui.zhang@intel.com>
> Cc: Eduardo Valentin <edubezval@gmail.com>
> Signed-off-by: Punit Agrawal <punit.agrawal@arm.com>
> Signed-off-by: Javi Merino <javi.merino@arm.com>
> ---
>  Documentation/thermal/power_allocator.txt | 196 ++++++++++++
>  drivers/thermal/Kconfig                   |  15 +
>  drivers/thermal/Makefile                  |   1 +
>  drivers/thermal/power_allocator.c         | 511 ++++++++++++++++++++++++++++++
>  drivers/thermal/thermal_core.c            |   7 +-
>  drivers/thermal/thermal_core.h            |   8 +
>  include/linux/thermal.h                   |  40 ++-
>  7 files changed, 774 insertions(+), 4 deletions(-)
>  create mode 100644 drivers/thermal/power_allocator.c
> 
> diff --git a/Documentation/thermal/power_allocator.txt b/Documentation/thermal/power_allocator.txt
> index d3bb79050c27..23b684afdc75 100644
> --- a/Documentation/thermal/power_allocator.txt
> +++ b/Documentation/thermal/power_allocator.txt
> @@ -1,3 +1,172 @@
> +Power allocator governor tunables
> +=================================
> +
> +Trip points
> +-----------
> +
> +The governor requires the following two passive trip points:
> +
> +1.  "switch on" trip point: temperature above which the governor
> +    control loop starts operating.
> +2.  "desired temperature" trip point: it should be higher than the
> +    "switch on" trip point. It is the target temperature the governor
> +    is controlling for.
> +
> +PID Controller
> +--------------
> +
> +The power allocator governor implements a
> +Proportional-Integral-Derivative controller (PID controller) with
> +temperature as the control input and power as the controlled output:
> +
> +    P_max = k_p * e + k_i * err_integral + k_d * diff_err + sustainable_power
> +
> +where
> +    e = desired_temperature - current_temperature
> +    err_integral is the sum of previous errors
> +    diff_err = e - previous_error
> +
> +It is similar to the one depicted below:
> +
> +                                      k_d
> +                                       |
> +current_temp                           |
> +     |                                 v
> +     |                +----------+   +---+
> +     |         +----->| diff_err |-->| X |------+
> +     |         |      +----------+   +---+      |
> +     |         |                                |      tdp        actor
> +     |         |                      k_i       |       |    get_actual_power()
> +     |         |                       |        |       |        |     |
> +     |         |                       |        |       |        |     | ...
> +     v         |                       v        v       v        v     v
> +   +---+       |      +-------+      +---+    +---+   +---+   +----------+
> +   | S |-------+----->| sum e |----->| X |--->| S |-->| S |-->|power     |
> +   +---+       |      +-------+      +---+    +---+   +---+   |allocation|
> +     ^         |                                ^             +----------+
> +     |         |                                |                |     |
> +     |         |        +---+                   |                |     |
> +     |         +------->| X |-------------------+                v     v
> +     |                  +---+                               granted performance
> +desired_temperature       ^
> +                          |
> +                          |
> +                      k_po/k_pu
> +
> +Sustainable power
> +-----------------
> +
> +An estimate of the sustainable dissipatable power (in mW) should be
> +provided while registering the thermal zone.  This estimates the
> +sustained power that can be dissipated at the desired control
> +temperature.  This is the maximum sustained power for allocation at
> +the desired maximum temperature.  The actual sustained power can vary
> +for a number of reasons.  The closed loop controller will take care of
> +variations such as environmental conditions, and some factors related
> +to the speed-grade of the silicon.  `sustainable_power` is therefore
> +simply an estimate, and may be tuned to affect the aggressiveness of
> +the thermal ramp.  For reference, this is 2000mW - 4500mW depending on
> +screen size (4" phone - 10" tablet).

I would rephrase the example as:
'For reference, the sustainable power of a 4" phone is typically 2000mW,
while on a 10" table is around 4500mW (may vary depending on screen
size).

> +
> +If you are using device tree, do add it as a property of the
> +thermal-zone.  For example:
> +
> +	thermal-zones {
> +		soc_thermal {
> +			polling-delay = <1000>;
> +			polling-delay-passive = <100>;
> +			sustainable-power = <2500>;
> +			...
> +
> +If you use platform code to register your thermal zone instead, pass a
> +`thermal_zone_params` that has a `sustainable_power`.  If you weren't
> +passing any `thermal_zone_params`, then something like this will do:
> +
> +	static const struct thermal_zone_params tz_params = {
> +		.sustainable_power = 3500,
> +	};
> +
> +and then pass `tz_params` as the 5th parameter to
> +`thermal_zone_device_register()`
> +
> +k_po and k_pu
> +-------------
> +
> +The implementation of the PID controller in the power allocator
> +thermal governor allows the configuration of two proportional term
> +constants: `k_po` and `k_pu`.  `k_po` is the proportional term
> +constant during temperature overshoot periods (current temperature is
> +above "desired temperature" trip point).  Conversely, `k_pu` is the
> +proportional term constant during temperature undershoot periods
> +(current temperature below "desired temperature" trip point).
> +
> +These controls are intended as the primary mechanism for configuring
> +the permitted thermal "ramp" of the system.  For instance, a lower
> +`k_pu` value will provide a slower ramp, at the cost of capping
> +available capacity at a low temperature.  On the other hand, a high
> +value of `k_pu` will result in the governor granting very high power
> +whilst temperature is low, and may lead to temperature overshooting.
> +
> +The default value for `k_pu` is:
> +
> +    2 * sustainable_power / (desired_temperature - switch_on_temp)
> +
> +This means that at `switch_on_temp` the output of the controller's
> +proportional term will be 2 * `sustainable_power`.  The default value
> +for `k_po` is:
> +
> +    sustainable_power / (desired_temperature - switch_on_temp)
> +
> +Focusing on the proportional and feed forward values of the PID
> +controller equation we have:
> +
> +    P_max = k_p * e + sustainable_power
> +
> +The proportional term is proportional to the difference between the
> +desired temperature and the current one.  When the current temperature
> +is the desired one, then the proportional component is zero and
> +`P_max` = `sustainable_power`.  That is, the system should operate in
> +thermal equilibrium under constant load.  `sustainable_power` is only
> +an estimate, which is the reason for closed-loop control such as this.
> +
> +Expanding `k_pu` we get:
> +    P_max = 2 * sustainable_power * (T_set - T) / (T_set - T_on) +
> +        sustainable_power
> +
> +where
> +    T_set is the desired temperature
> +    T is the current temperature
> +    T_on is the switch on temperature
> +
> +When the current temperature is the switch_on temperature, the above
> +formula becomes:
> +
> +    P_max = 2 * sustainable_power * (T_set - T_on) / (T_set - T_on) +
> +        sustainable_power = 2 * sustainable_power + sustainable_power = 
> +        3 * sustainable_power
> +
> +Therefore, the proportional term alone linearly decreases power from
> +3 * `sustainable_power` to `sustainable_power` as the temperature
> +rises from the switch on temperature to the desired temperature.
> +
> +k_i and integral_cutoff
> +-----------------------
> +
> +`k_i` configures the PID loop's integral term constant.  This term
> +allows the PID controller to compensate for long term drift and for
> +the quantized nature of the output control: cooling devices can't set
> +the exact power that the governor requests.  When the temperature
> +error is below `integral_cutoff`, errors are accumulated in the
> +integral term.  This term is then multiplied by `k_i` and the result
> +added to the output of the controller.  Typically `k_i` is set low (1
> +or 2) and `integral_cutoff` is 0.
> +
> +k_d
> +---

k_d may be conflicted with Kd  (capacitance) of the cooling device power API.

Is it possible to change / rename either one of them?

> +
> +`k_d` configures the PID loop's derivative term constant.  It's
> +recommended to leave it as the default: 0.
> +
>  Cooling device power API
>  ========================
>  
> @@ -25,3 +194,30 @@ milliwatts.
>  
>  Calculate a cooling device state that would make the device consume at
>  most @power mW.
> +
> +Cooling device weights
> +----------------------
> +
> +Weights are a mechanism to bias the allocation between cooling
> +devices.  They express the relative power efficiency of different
> +cooling devices.  Higher weight can be used to express higher power
> +efficiency.  Weighting is relative such that if each cooling device
> +has a weight of one they are considered equal.  This is particularly
> +useful in heterogeneous systems where two cooling devices may perform
> +the same kind of compute, but with different efficiency.  For example,
> +a system with two different types of processors.
> +
> +Weights shall be passed as part of the thermal zone's
> +`thermal_bind_parameters`.
> +
> +Limitations of the power allocator governor
> +===========================================
> +
> +The power allocator governor's PID controller works best if there is a
> +periodic tick.  If you have a driver that calls
> +`thermal_zone_device_update()` (or anything that ends up calling the
> +governor's `throttle()` function) repetitively, the governor response
> +won't be very good.  Note that this is not particular to this
> +governor, step-wise will also misbehave if you call its throttle()
> +faster than the normal thermal framework tick (due to interrupts for
> +example) as it will overreact.
> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> index f554d25b4399..4496fa5e4a33 100644
> --- a/drivers/thermal/Kconfig
> +++ b/drivers/thermal/Kconfig
> @@ -71,6 +71,14 @@ config THERMAL_DEFAULT_GOV_USER_SPACE
>  	  Select this if you want to let the user space manage the
>  	  platform thermals.
>  
> +config THERMAL_DEFAULT_GOV_POWER_ALLOCATOR
> +	bool "power_allocator"
> +	select THERMAL_GOV_POWER_ALLOCATOR
> +	help
> +	  Select this if you want to control temperature based on
> +	  system and device power allocation. This governor relies on
> +	  power actors to operate.
> +
>  endchoice
>  
>  config THERMAL_GOV_FAIR_SHARE
> @@ -99,6 +107,13 @@ config THERMAL_GOV_USER_SPACE
>  	help
>  	  Enable this to let the user space manage the platform thermals.
>  
> +config THERMAL_GOV_POWER_ALLOCATOR
> +	bool "Power allocator thermal governor"
> +	select THERMAL_POWER_ACTOR
> +	help
> +	  Enable this to manage platform thermals by dynamically
> +	  allocating and limiting power to devices.

I think the config entry deserves a better description, don't you
agree?

> +
>  config CPU_THERMAL
>  	bool "generic cpu cooling support"
>  	depends on CPU_FREQ
> diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> index 39c4fe87da2f..c33904848c45 100644
> --- a/drivers/thermal/Makefile
> +++ b/drivers/thermal/Makefile
> @@ -14,6 +14,7 @@ thermal_sys-$(CONFIG_THERMAL_GOV_FAIR_SHARE)	+= fair_share.o
>  thermal_sys-$(CONFIG_THERMAL_GOV_BANG_BANG)	+= gov_bang_bang.o
>  thermal_sys-$(CONFIG_THERMAL_GOV_STEP_WISE)	+= step_wise.o
>  thermal_sys-$(CONFIG_THERMAL_GOV_USER_SPACE)	+= user_space.o
> +thermal_sys-$(CONFIG_THERMAL_GOV_POWER_ALLOCATOR)	+= power_allocator.o
>  
>  # cpufreq cooling
>  thermal_sys-$(CONFIG_CPU_THERMAL)	+= cpu_cooling.o
> diff --git a/drivers/thermal/power_allocator.c b/drivers/thermal/power_allocator.c
> new file mode 100644
> index 000000000000..09e98991efbb
> --- /dev/null
> +++ b/drivers/thermal/power_allocator.c
> @@ -0,0 +1,511 @@
> +/*
> + * A power allocator to manage temperature
> + *
> + * Copyright (C) 2014 ARM Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed "as is" WITHOUT ANY WARRANTY of any
> + * kind, whether express or implied; without even the implied warranty
> + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#define pr_fmt(fmt) "Power allocator: " fmt
> +
> +#include <linux/rculist.h>
> +#include <linux/slab.h>
> +#include <linux/thermal.h>
> +
> +#include "thermal_core.h"
> +
> +#define FRAC_BITS 10
> +#define int_to_frac(x) ((x) << FRAC_BITS)
> +#define frac_to_int(x) ((x) >> FRAC_BITS)
> +
> +/**
> + * mul_frac() - multiply two fixed-point numbers
> + * @x:	first multiplicand
> + * @y:	second multiplicand
> + *

If it is a kernel doc, needs a description.

> + * Return: the result of multiplying two fixed-point numbers.  The
> + * result is also a fixed-point number.
> + */
> +static inline s64 mul_frac(s64 x, s64 y)
> +{
> +	return (x * y) >> FRAC_BITS;
> +}
> +
> +enum power_allocator_trip_levels {
> +	TRIP_SWITCH_ON = 0,	/* Switch on PID controller */
> +	TRIP_MAX_DESIRED_TEMPERATURE, /* Temperature we are controlling for */
> +
> +	THERMAL_TRIP_NUM,
> +};
> +
> +/**
> + * struct power_allocator_params - parameters for the power allocator governor
> + * @k_po:	Proportional parameter of the PID controller when overshooting
> + *		(i.e., when temperature is below the target)
> + * @k_pu:	Proportional parameter of the PID controller when undershooting
> + * @k_i:	Integral parameter of the PID controller
> + * @k_d:	Derivative parameter of the PID controller
> + * @integral_cutoff:	threshold below which the error is no longer accumulated
> +			in the PID controller
> + * @err_integral:	accumulated error in the PID controller.
> + * @prev_err:	error in the previous iteration of the PID controller.
> + *		Used to calculate the derivative term.
> + */
> +struct power_allocator_params {
> +	s32 k_po;
> +	s32 k_pu;
> +	s32 k_i;
> +	s32 k_d;
> +	s32 integral_cutoff;
> +	s64 err_integral;
> +	s32 prev_err;
> +};
> +
> +/**
> + * get_actor_weight() - get the weight for the power actor
> + * @tz:		thermal zone we are operating in
> + * @actor:	the power actor
> + *


ditto

> + * Returns: The weight inside the thermal binding parameters of the

s/Returns:/Return:/g


Please run the kernel doc script on your patches and avoid adding
warnings / errors.

> + * thermal zone.  If it could not be found, a default weight of 1 is
> + * assumed.  Weights are expressed as a FRAC_BITS (currently 10-bit)
> + * fixed point integer.
> + */
> +static int get_actor_weight(struct thermal_zone_device *tz,
> +			struct thermal_cooling_device *cdev)
> +{
> +	int i;
> +
> +	for (i = 0; i < tz->tzp->num_tbps; i++)
> +		if (tz->tzp->tbp[i].cdev == cdev)
> +			return tz->tzp->tbp[i].weight;
> +
> +	return int_to_frac(1);
> +}
> +
> +/**
> + * pid_controller() - PID controller
> + * @tz:	thermal zone we are operating in
> + * @current_temp:	the current temperature in millicelsius
> + * @control_temp:	the target temperature in millicelsius
> + * @max_allocatable_power:	maximum allocatable power for this thermal zone
> + *
> + * This PID controller increases the available power budget so that the
> + * temperature of the thermal zone gets as close as possible to
> + * @control_temp and limits the power if it exceeds it.  k_po is the
> + * proportional term when we are overshooting, k_pu is the
> + * proportional term when we are undershooting.  integral_cutoff is a
> + * threshold below which we stop accumulating the error.  The
> + * accumulated error is only valid if the requested power will make
> + * the system warmer.  If the system is mostly idle, there's no point
> + * in accumulating positive error.
> + *
> + * Return: The power budget for the next period.
> + */
> +static u32 pid_controller(struct thermal_zone_device *tz,
> +			unsigned long current_temp, unsigned long control_temp,
> +			u32 max_allocatable_power)
> +{
> +	s64 p, i, d, power_range;
> +	s32 err, max_power_frac;
> +	struct power_allocator_params *params = tz->governor_data;
> +
> +	max_power_frac = int_to_frac(max_allocatable_power);
> +
> +	err = ((s32)control_temp - (s32)current_temp);
> +	err = int_to_frac(err);
> +
> +	/* Calculate the proportional term */
> +	p = mul_frac(err < 0 ? params->k_po : params->k_pu, err);
> +
> +	/*
> +	 * Calculate the integral term
> +	 *
> +	 * if the error is less than cut off allow integration (but
> +	 * the integral is limited to max power)
> +	 */
> +	i = mul_frac(params->k_i, params->err_integral);
> +
> +	if (err < int_to_frac(params->integral_cutoff)) {
> +		s64 i_next = i + mul_frac(params->k_i, err);
> +
> +		if (abs64(i_next) < max_power_frac) {
> +			i = i_next;
> +			params->err_integral += err;
> +		}
> +	}
> +
> +	/*
> +	 * Calculate the derivative term
> +	 *
> +	 * We do err - prev_err, so with a positive k_d, a decreasing
> +	 * error (i.e. driving closer to the line) results in less
> +	 * power being applied, slowing down the controller)
> +	 */
> +	d = mul_frac(params->k_d, err - params->prev_err);
> +	params->prev_err = err;
> +
> +	power_range = p + i + d;
> +
> +	/* feed-forward the known sustainable dissipatable power */
> +	power_range = tz->tzp->sustainable_power + frac_to_int(power_range);
> +
> +	return clamp(power_range, (s64)0, (s64)max_allocatable_power);
> +}
> +
> +/**
> + * divvy_up_power() - divvy the allocated power between the actors
> + * @req_power:	each actor's requested power
> + * @max_power:	each actor's maximum available power
> + * @num_actors:	size of the @req_power, @max_power and @granted_power's array
> + * @total_req_power: sum of @req_power
> + * @power_range:	total allocated power
> + * @granted_power:	output array: each actor's granted power
> + *
> + * This function divides the total allocated power (@power_range)
> + * fairly between the actors.  It first tries to give each actor a
> + * share of the @power_range according to how much power it requested
> + * compared to the rest of the actors.  For example, if only one actor
> + * requests power, then it receives all the @power_range.  If
> + * three actors each requests 1mW, each receives a third of the
> + * @power_range.
> + *
> + * If any actor received more than their maximum power, then that
> + * surplus is re-divvied among the actors based on how far they are
> + * from their respective maximums.
> + *
> + * Granted power for each actor is written to @granted_power, which
> + * should've been allocated by the calling function.
> + */
> +static void divvy_up_power(u32 *req_power, u32 *max_power, int num_actors,
> +			u32 total_req_power, u32 power_range,
> +			u32 *granted_power)
> +{
> +	u32 extra_power, capped_extra_power, extra_actor_power[num_actors];
> +	int i;
> +
> +	if (!total_req_power) {
> +		/*
> +		 * Nobody requested anything, so just give everybody
> +		 * the maximum power
> +		 */
> +		for (i = 0; i < num_actors; i++)
> +			granted_power[i] = max_power[i];
> +
> +		return;
> +	}
> +
> +	capped_extra_power = 0;
> +	extra_power = 0;
> +	for (i = 0; i < num_actors; i++) {
> +		u64 req_range = req_power[i] * power_range;
> +
> +		granted_power[i] = div_u64(req_range, total_req_power);
> +
> +		if (granted_power[i] > max_power[i]) {
> +			extra_power += granted_power[i] - max_power[i];
> +			granted_power[i] = max_power[i];

shouldn't we continue here?

> +		}
> +
> +		extra_actor_power[i] = max_power[i] - granted_power[i];

Do we care when max_power[i] < granted_power[i]? What happens to
(overflowed) extra_actor_power[i]?

> +		capped_extra_power += extra_actor_power[i];
> +	}
> +
> +	if (!extra_power)
> +		return;
> +
> +	/*
> +	 * Re-divvy the reclaimed extra among actors based on
> +	 * how far they are from the max
> +	 */
> +	extra_power = min(extra_power, capped_extra_power);
> +	if (capped_extra_power > 0)
> +		for (i = 0; i < num_actors; i++)
> +			granted_power[i] += (extra_actor_power[i] *
> +					extra_power) / capped_extra_power;
> +}
> +
> +static int allocate_power(struct thermal_zone_device *tz,
> +			unsigned long current_temp, unsigned long control_temp)
> +{
> +	struct thermal_instance *instance;
> +	u32 *req_power, *max_power, *granted_power;
> +	u32 total_req_power, max_allocatable_power;
> +	u32 power_range;
> +	int i, num_actors, ret = 0;
> +
> +	mutex_lock(&tz->lock);
> +
> +	num_actors = 0;
> +	list_for_each_entry(instance, &tz->thermal_instances, tz_node)
> +		if ((instance->trip == TRIP_MAX_DESIRED_TEMPERATURE) &&
> +			cdev_is_power_actor(instance->cdev))
> +			num_actors++;
> +
> +	req_power = devm_kcalloc(&tz->device, num_actors, sizeof(*req_power),
> +				GFP_KERNEL);
> +	if (!req_power) {
> +		ret = -ENOMEM;
> +		goto unlock;
> +	}
> +
> +	max_power = devm_kcalloc(&tz->device, num_actors, sizeof(*max_power),
> +				GFP_KERNEL);
> +	if (!max_power) {
> +		ret = -ENOMEM;
> +		goto free_req_power;
> +	}
> +
> +	granted_power = devm_kcalloc(&tz->device, num_actors,
> +				sizeof(*granted_power), GFP_KERNEL);
> +	if (!granted_power) {
> +		ret = -ENOMEM;
> +		goto free_max_power;
> +	}
> +
> +	i = 0;
> +	total_req_power = 0;
> +	max_allocatable_power = 0;
> +
> +	list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
> +		int weight;
> +		struct thermal_cooling_device *cdev = instance->cdev;
> +
> +		if (instance->trip != TRIP_MAX_DESIRED_TEMPERATURE)
> +			continue;
> +
> +		if (!cdev_is_power_actor(cdev))
> +			continue;
> +
> +		req_power[i] = cdev->ops->get_actual_power(cdev);


Is this req_power (I read as 'requested power') or actual_power? I would
use the later naming to avoid confusions.

> +		weight = get_actor_weight(tz, cdev);
> +		req_power[i] = frac_to_int(weight * req_power[i]);
> +		total_req_power += req_power[i];

ditto for total_req_power.

> +
> +		max_power[i] = power_actor_get_max_power(cdev);
> +		max_allocatable_power += max_power[i];
> +
> +		i++;
> +	}
> +
> +	power_range = pid_controller(tz, current_temp, control_temp,
> +				max_allocatable_power);
> +
> +	divvy_up_power(req_power, max_power, num_actors, total_req_power,
> +		power_range, granted_power);
> +
> +	i = 0;
> +	list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
> +		if (instance->trip != TRIP_MAX_DESIRED_TEMPERATURE)
> +			continue;
> +
> +		if (!cdev_is_power_actor(instance->cdev))
> +			continue;
> +
> +		power_actor_set_power(instance->cdev, granted_power[i]);
> +
> +		i++;
> +	}
> +
> +	devm_kfree(&tz->device, granted_power);
> +free_max_power:
> +	devm_kfree(&tz->device, max_power);
> +free_req_power:
> +	devm_kfree(&tz->device, req_power);
> +unlock:
> +	mutex_unlock(&tz->lock);
> +
> +	return ret;
> +}
> +
> +static int check_trips(struct thermal_zone_device *tz)
> +{
> +	int ret;
> +	enum thermal_trip_type type;
> +
> +	if (tz->trips < THERMAL_TRIP_NUM)
> +		return -EINVAL;
> +
> +	ret = tz->ops->get_trip_type(tz, TRIP_SWITCH_ON, &type);
> +	if (ret)
> +		return ret;
> +
> +	if (type != THERMAL_TRIP_PASSIVE)
> +		return -EINVAL;
> +
> +	ret = tz->ops->get_trip_type(tz, TRIP_MAX_DESIRED_TEMPERATURE, &type);
> +	if (ret)
> +		return ret;
> +
> +	if (type != THERMAL_TRIP_PASSIVE)
> +		return -EINVAL;
> +
> +	return ret;
> +}
> +
> +static void reset_pid_controller(struct power_allocator_params *params)
> +{
> +	params->err_integral = 0;
> +	params->prev_err = 0;
> +}
> +
> +static void allow_maximum_power(struct thermal_zone_device *tz)
> +{
> +	struct thermal_instance *instance;
> +
> +	list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
> +		u32 max_power;
> +
> +		if ((instance->trip != TRIP_MAX_DESIRED_TEMPERATURE) ||
> +			(!cdev_is_power_actor(instance->cdev)))
> +			continue;
> +
> +		max_power = power_actor_get_max_power(instance->cdev);
> +		power_actor_set_power(instance->cdev, max_power);
> +	}
> +}
> +
> +/**
> + * power_allocator_bind() - bind the power_allocator governor to a thermal zone
> + * @tz:	thermal zone to bind it to
> + *
> + * Check that the thermal zone is valid for this governor, that is, it
> + * has two thermal trips.  If so, initialize the PID controller
> + * parameters and bind it to the thermal zone.
> + *
> + * Return: 0 on success, -EINVAL if the trips were invalid or -ENOMEM
> + * if we ran out of memory.
> + */
> +static int power_allocator_bind(struct thermal_zone_device *tz)
> +{
> +	int ret;
> +	struct power_allocator_params *params;
> +	unsigned long switch_on_temp, control_temp;
> +	u32 temperature_threshold;
> +
> +	ret = check_trips(tz);
> +	if (ret) {
> +		dev_err(&tz->device,
> +			"thermal zone %s has the wrong number of trips for this governor\n",

I would be more specific:
+			"thermal zone %s has wrong trip setup for power allocator\n",


Besides, in 'check_trips' you check more than number of trips.

> +			tz->type);
> +		return ret;
> +	}
> +
> +	if (!tz->tzp || !tz->tzp->sustainable_power) {
> +		dev_err(&tz->device,
> +			"power_allocator: missing sustainable_power\n");
> +		return -EINVAL;
> +	}
> +
> +	params = devm_kzalloc(&tz->device, sizeof(*params), GFP_KERNEL);
> +	if (!params)
> +		return -ENOMEM;
> +
> +	ret = tz->ops->get_trip_temp(tz, TRIP_SWITCH_ON, &switch_on_temp);
> +	if (ret)
> +		goto free;
> +
> +	ret = tz->ops->get_trip_temp(tz, TRIP_MAX_DESIRED_TEMPERATURE,
> +				&control_temp);
> +	if (ret)
> +		goto free;
> +
> +	temperature_threshold = control_temp - switch_on_temp;
> +
> +	params->k_po = tz->tzp->k_po ?:
> +		int_to_frac(tz->tzp->sustainable_power) / temperature_threshold;
> +	params->k_pu = tz->tzp->k_pu ?:
> +		int_to_frac(2 * tz->tzp->sustainable_power) /
> +		temperature_threshold;
> +	params->k_i = tz->tzp->k_i ?: int_to_frac(10) / 1000;
> +	params->k_d = tz->tzp->k_d ?: int_to_frac(0);
> +	params->integral_cutoff = tz->tzp->integral_cutoff ?: 0;
> +
> +	reset_pid_controller(params);
> +
> +	tz->governor_data = params;
> +
> +	return 0;
> +
> +free:
> +	devm_kfree(&tz->device, params);
> +	return ret;
> +}
> +
> +static void power_allocator_unbind(struct thermal_zone_device *tz)
> +{
> +	dev_dbg(&tz->device, "Unbinding from thermal zone %d\n", tz->id);
> +	devm_kfree(&tz->device, tz->governor_data);
> +	tz->governor_data = NULL;
> +}
> +
> +static int power_allocator_throttle(struct thermal_zone_device *tz, int trip)
> +{
> +	int ret;
> +	unsigned long switch_on_temp, control_temp, current_temp;
> +	struct power_allocator_params *params = tz->governor_data;
> +
> +	/*
> +	 * We get called for every trip point but we only need to do
> +	 * our calculations once
> +	 */
> +	if (trip != TRIP_MAX_DESIRED_TEMPERATURE)
> +		return 0;
> +
> +	ret = thermal_zone_get_temp(tz, &current_temp);
> +	if (ret) {
> +		dev_warn(&tz->device, "Failed to get temperature: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = tz->ops->get_trip_temp(tz, TRIP_SWITCH_ON, &switch_on_temp);
> +	if (ret) {
> +		dev_warn(&tz->device,
> +			"Failed to get switch on temperature: %d\n", ret);
> +		return ret;
> +	}
> +
> +	if (current_temp < switch_on_temp) {
> +		tz->passive = 0;
> +		reset_pid_controller(params);
> +		allow_maximum_power(tz);
> +		return 0;
> +	}
> +
> +	tz->passive = 1;
> +
> +	ret = tz->ops->get_trip_temp(tz, TRIP_MAX_DESIRED_TEMPERATURE,
> +				&control_temp);
> +	if (ret) {
> +		dev_warn(&tz->device,
> +			"Failed to get the maximum desired temperature: %d\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	return allocate_power(tz, current_temp, control_temp);
> +}
> +
> +static struct thermal_governor thermal_gov_power_allocator = {
> +	.name		= "power_allocator",
> +	.bind_to_tz	= power_allocator_bind,
> +	.unbind_from_tz	= power_allocator_unbind,
> +	.throttle	= power_allocator_throttle,
> +};
> +
> +int thermal_gov_power_allocator_register(void)
> +{
> +	return thermal_register_governor(&thermal_gov_power_allocator);
> +}
> +
> +void thermal_gov_power_allocator_unregister(void)
> +{
> +	thermal_unregister_governor(&thermal_gov_power_allocator);
> +}
> diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
> index c490f262ea7f..4921e084c20b 100644
> --- a/drivers/thermal/thermal_core.c
> +++ b/drivers/thermal/thermal_core.c
> @@ -1905,7 +1905,11 @@ static int __init thermal_register_governors(void)
>  	if (result)
>  		return result;
>  
> -	return thermal_gov_user_space_register();
> +	result = thermal_gov_user_space_register();
> +	if (result)
> +		return result;
> +
> +	return thermal_gov_power_allocator_register();
>  }
>  
>  static void thermal_unregister_governors(void)
> @@ -1914,6 +1918,7 @@ static void thermal_unregister_governors(void)
>  	thermal_gov_fair_share_unregister();
>  	thermal_gov_bang_bang_unregister();
>  	thermal_gov_user_space_unregister();
> +	thermal_gov_power_allocator_unregister();
>  }
>  
>  static int __init thermal_init(void)
> diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h
> index d15d243de27a..b907be823527 100644
> --- a/drivers/thermal/thermal_core.h
> +++ b/drivers/thermal/thermal_core.h
> @@ -85,6 +85,14 @@ static inline int thermal_gov_user_space_register(void) { return 0; }
>  static inline void thermal_gov_user_space_unregister(void) {}
>  #endif /* CONFIG_THERMAL_GOV_USER_SPACE */
>  
> +#ifdef CONFIG_THERMAL_GOV_POWER_ALLOCATOR
> +int thermal_gov_power_allocator_register(void);
> +void thermal_gov_power_allocator_unregister(void);
> +#else
> +static inline int thermal_gov_power_allocator_register(void) { return 0; }
> +static inline void thermal_gov_power_allocator_unregister(void) {}
> +#endif /* CONFIG_THERMAL_GOV_POWER_ALLOCATOR */
> +
>  /* device tree support */
>  #ifdef CONFIG_THERMAL_OF
>  int of_parse_thermal_zones(void);
> diff --git a/include/linux/thermal.h b/include/linux/thermal.h
> index 1155457caf52..b23e019b1761 100644
> --- a/include/linux/thermal.h
> +++ b/include/linux/thermal.h
> @@ -61,6 +61,8 @@
>  #define DEFAULT_THERMAL_GOVERNOR       "fair_share"
>  #elif defined(CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE)
>  #define DEFAULT_THERMAL_GOVERNOR       "user_space"
> +#elif defined(CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR)
> +#define DEFAULT_THERMAL_GOVERNOR       "power_allocator"
>  #endif
>  
>  struct thermal_zone_device;
> @@ -255,9 +257,14 @@ struct thermal_bind_params {
>  
>  	/*
>  	 * This is a measure of 'how effectively these devices can
> -	 * cool 'this' thermal zone. The shall be determined by platform
> -	 * characterization. This is on a 'percentage' scale.
> -	 * See Documentation/thermal/sysfs-api.txt for more information.
> +	 * cool 'this' thermal zone. The shall be determined by
> +	 * platform characterization. For the fair-share governor,
> +	 * this is on a 'percentage' scale.  See
> +	 * Documentation/thermal/sysfs-api.txt for more
> +	 * information. For the power_allocator governor, they are
> +	 * relative to each other, see
> +	 * Documentation/thermal/power_allocator.txt for more
> +	 * information.

What happens if we register a thermal zone with relative weights, at
fist the user uses power allocator, but then wants to, for some reason,
use fair share? (or vice-versa).


Can't power allocator use percentages too?

>  	 */
>  	int weight;
>  
> @@ -294,6 +301,33 @@ struct thermal_zone_params {
>  
>  	int num_tbps;	/* Number of tbp entries */
>  	struct thermal_bind_params *tbp;
> +
> +	/*
> +	 * Sustainable power (heat) that this thermal zone can dissipate in
> +	 * mW
> +	 */
> +	u32 sustainable_power;
> +
> +	/*
> +	 * Proportional parameter of the PID controller when
> +	 * overshooting (i.e., when temperature is below the target)
> +	 */
> +	s32 k_po;
> +
> +	/*
> +	 * Proportional parameter of the PID controller when
> +	 * undershooting
> +	 */
> +	s32 k_pu;
> +
> +	/* Integral parameter of the PID controller */
> +	s32 k_i;
> +
> +	/* Derivative parameter of the PID controller */
> +	s32 k_d;
> +
> +	/* threshold below which the error is no longer accumulated */
> +	s32 integral_cutoff;
>  };
>  
>  struct thermal_genl_event {
> -- 
> 1.9.1
> 
> 

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 7/9] thermal: introduce the Power Allocator governor
  2014-12-05 19:04 ` [RFC PATCH v6 7/9] thermal: introduce the Power Allocator governor Javi Merino
  2015-01-02 15:46   ` Eduardo Valentin
@ 2015-01-02 15:51   ` Eduardo Valentin
  1 sibling, 0 replies; 48+ messages in thread
From: Eduardo Valentin @ 2015-01-02 15:51 UTC (permalink / raw)
  To: Javi Merino; +Cc: linux-pm, linux-kernel, punit.agrawal, broonie, Zhang Rui

[-- Attachment #1: Type: text/plain, Size: 428 bytes --]

Forgot to mention

On Fri, Dec 05, 2014 at 07:04:18PM +0000, Javi Merino wrote:

<cut>

> +
> +If you are using device tree, do add it as a property of the
> +thermal-zone.  For example:
> +
> +	thermal-zones {
> +		soc_thermal {
> +			polling-delay = <1000>;
> +			polling-delay-passive = <100>;
> +			sustainable-power = <2500>;
> +			...
> +

The above needs to be documented in the DT thermal binding document
too.

Cheers


[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 9/9] of: thermal: Introduce sustainable power for a thermal zone
  2014-12-05 19:04 ` [RFC PATCH v6 9/9] of: thermal: Introduce sustainable power for a thermal zone Javi Merino
@ 2015-01-02 15:53   ` Eduardo Valentin
  2015-01-06  9:42     ` Javi Merino
  0 siblings, 1 reply; 48+ messages in thread
From: Eduardo Valentin @ 2015-01-02 15:53 UTC (permalink / raw)
  To: Javi Merino; +Cc: linux-pm, linux-kernel, punit.agrawal, broonie, Zhang Rui

[-- Attachment #1: Type: text/plain, Size: 2510 bytes --]

On Fri, Dec 05, 2014 at 07:04:20PM +0000, Javi Merino wrote:
> From: Punit Agrawal <punit.agrawal@arm.com>
> 
> Introduce an optional property called, sustainable-power, which
> represents the power (in mW) which the thermal zone can safely
> dissipate.
> 
> If provided the property is parsed and associated with the thermal
> zone via the thermal zone parameters.
> 
> Cc: Zhang Rui <rui.zhang@intel.com>
> Cc: Eduardo Valentin <edubezval@gmail.com>
> Signed-off-by: Punit Agrawal <punit.agrawal@arm.com>
> ---
>  Documentation/devicetree/bindings/thermal/thermal.txt | 4 ++++
>  drivers/thermal/of-thermal.c                          | 4 ++++
>  2 files changed, 8 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/thermal/thermal.txt b/Documentation/devicetree/bindings/thermal/thermal.txt
> index f5db6b72a36f..c6eb9a8d2aed 100644
> --- a/Documentation/devicetree/bindings/thermal/thermal.txt
> +++ b/Documentation/devicetree/bindings/thermal/thermal.txt
> @@ -167,6 +167,10 @@ Optional property:
>  			by means of sensor ID. Additional coefficients are
>  			interpreted as constant offset.
>  
> +- sustainable-power:	An estimate of the sustainable power (in mW) that the
> +  Type: unsigned	thermal zone can dissipate.
> +  Size: one cell
> +

Please, include examples of this property, as you mentioned in the
governor documentation.

>  Note: The delay properties are bound to the maximum dT/dt (temperature
>  derivative over time) in two situations for a thermal zone:
>  (i)  - when passive cooling is activated (polling-delay-passive); and
> diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c
> index 62143ba31001..e032b9bf4085 100644
> --- a/drivers/thermal/of-thermal.c
> +++ b/drivers/thermal/of-thermal.c
> @@ -794,6 +794,7 @@ int __init of_parse_thermal_zones(void)
>  	for_each_child_of_node(np, child) {
>  		struct thermal_zone_device *zone;
>  		struct thermal_zone_params *tzp;
> +		u32 prop;
>  
>  		/* Check whether child is enabled or not */
>  		if (!of_device_is_available(child))
> @@ -820,6 +821,9 @@ int __init of_parse_thermal_zones(void)
>  		/* No hwmon because there might be hwmon drivers registering */
>  		tzp->no_hwmon = true;
>  
> +		if (!of_property_read_u32(child, "sustainable-power", &prop))
> +			tzp->sustainable_power = prop;
> +
>  		zone = thermal_zone_device_register(child->name, tz->ntrips,
>  						    0, tz,
>  						    ops, tzp,
> -- 
> 1.9.1
> 
> 

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 5/9] thermal: extend the cooling device API to include power information
  2014-12-23 15:14   ` Eduardo Valentin
@ 2015-01-05 15:37     ` Javi Merino
  2015-01-05 21:04       ` Eduardo Valentin
  0 siblings, 1 reply; 48+ messages in thread
From: Javi Merino @ 2015-01-05 15:37 UTC (permalink / raw)
  To: Eduardo Valentin
  Cc: linux-pm, linux-kernel, Punit Agrawal, broonie, Zhang Rui

On Tue, Dec 23, 2014 at 03:14:11PM +0000, Eduardo Valentin wrote:
> Hi Javi
> 
> On Fri, Dec 05, 2014 at 07:04:16PM +0000, Javi Merino wrote:
> > Add three optional callbacks to the cooling device interface to allow
> > them to express power.  In addition to the callbacks, add helpers to
> > identify cooling devices that implement the power cooling device API.
> > 
> > Cc: Zhang Rui <rui.zhang@intel.com>
> > Cc: Eduardo Valentin <edubezval@gmail.com>
> > Signed-off-by: Javi Merino <javi.merino@arm.com>
> > ---
> >  Documentation/thermal/power_allocator.txt | 27 ++++++++++++++++++++++
> >  drivers/thermal/thermal_core.c            | 38 +++++++++++++++++++++++++++++++
> >  include/linux/thermal.h                   | 12 ++++++++++
> >  3 files changed, 77 insertions(+)
> >  create mode 100644 Documentation/thermal/power_allocator.txt
> > 
> > diff --git a/Documentation/thermal/power_allocator.txt b/Documentation/thermal/power_allocator.txt
> > new file mode 100644
> > index 000000000000..d3bb79050c27
> > --- /dev/null
> > +++ b/Documentation/thermal/power_allocator.txt
> > @@ -0,0 +1,27 @@
> > +Cooling device power API
> > +========================
> 
> Readers of this file need extra context here, IMO.

Patch 7 adds text before and after this section that provides that
context.

> > +
> > +Cooling devices controlled by this governor must supply the additional
> 
> What governor? the files says power allocator, and the title says,
> cooling device power API...

Correct, because that's added in the patch that introduces the power
allocator governor.  Therefore, it's not a problem for the readers of
this file but for the readers of the patches.  I can move this hunk to
patch 7 and introduce all the documentation at once if you think
that's clearer.

> > +"power" API in their `cooling_device_ops`.  It consists on three ops:
> > +
> 
> 
> 
> > +1. u32 get_actual_power(struct thermal_cooling_device *cdev);
> > +@cdev: The `struct thermal_cooling_device` pointer
> > +
> > +`get_actual_power()` returns the power currently consumed by the
> > +device in milliwatts.
> > +
> > +2. u32 state2power(struct thermal_cooling_device *cdev, unsigned long
> > +        state);
> > +@cdev: The `struct thermal_cooling_device` pointer
> > +@state: A cooling device state
> > +
> > +Convert cooling device state @state into power consumption in
> > +milliwatts.
> > +
> > +3. unsigned long power2state(struct thermal_cooling_device *cdev,
> > +        u32 power);
> > +@cdev: The `struct thermal_cooling_device` pointer
> > +@power: power in milliwatts
> > +
> > +Calculate a cooling device state that would make the device consume at
> > +most @power mW.
> 
> I believe it would be more helpful if you could provide extra context in
> which the above functions are called, and for what.

Ok, will do.

> > diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
> > index 9021cb72a13a..c490f262ea7f 100644
> > --- a/drivers/thermal/thermal_core.c
> > +++ b/drivers/thermal/thermal_core.c
> > @@ -866,6 +866,44 @@ emul_temp_store(struct device *dev, struct device_attribute *attr,
> >  static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store);
> >  #endif/*CONFIG_THERMAL_EMULATION*/
> >  
> > +/**
> > + * power_actor_get_max_power() - get the maximum power that a cdev can consume
> > + * @cdev:	pointer to &thermal_cooling_device
> > + *
> > + * Calculate the maximum power consumption in milliwats that the
> > + * cooling device can currently consume.  If @cdev doesn't support the
> > + * power_actor API, this function returns 0.
> > + */
> > +u32 power_actor_get_max_power(struct thermal_cooling_device *cdev)
> > +{
> > +	if (!cdev_is_power_actor(cdev))
> > +		return 0;
> > +
> > +	return cdev->ops->state2power(cdev, 0);
> > +}
> > +
> > +/**
> > + * power_actor_set_power() - limit the maximum power that a cooling device can consume
> > + * @cdev:	pointer to &thermal_cooling_device
> > + * @power:	the power in milliwatts
> > + *
> > + * Set the cooling device to consume at most @power milliwatts.
> > + *
> > + * Returns: 0 on success, -EINVAL if the cooling device does not
> > + * implement the power actor API or -E* for other failures.
> > + */
> > +int power_actor_set_power(struct thermal_cooling_device *cdev, u32 power)
> > +{
> > +	unsigned long state;
> > +
> > +	if (!cdev_is_power_actor(cdev))
> > +		return -EINVAL;
> > +
> > +	state = cdev->ops->power2state(cdev, power);
> > +
> > +	return cdev->ops->set_cur_state(cdev, state);
> > +}
> > +
> >  static DEVICE_ATTR(type, 0444, type_show, NULL);
> >  static DEVICE_ATTR(temp, 0444, temp_show, NULL);
> >  static DEVICE_ATTR(mode, 0644, mode_show, mode_store);
> > diff --git a/include/linux/thermal.h b/include/linux/thermal.h
> > index 2c14ab1f5c0d..1155457caf52 100644
> > --- a/include/linux/thermal.h
> > +++ b/include/linux/thermal.h
> > @@ -142,6 +142,9 @@ struct thermal_cooling_device_ops {
> >  	int (*get_max_state) (struct thermal_cooling_device *, unsigned long *);
> >  	int (*get_cur_state) (struct thermal_cooling_device *, unsigned long *);
> >  	int (*set_cur_state) (struct thermal_cooling_device *, unsigned long);
> > +	u32 (*get_actual_power) (struct thermal_cooling_device *);
> > +	u32 (*state2power) (struct thermal_cooling_device *, unsigned long);
> > +	unsigned long (*power2state) (struct thermal_cooling_device *, u32);
> >  };
> >  
> >  struct thermal_cooling_device {
> > @@ -322,6 +325,15 @@ void thermal_zone_of_sensor_unregister(struct device *dev,
> >  }
> >  
> >  #endif
> > +
> > +static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev)
> > +{
> 
> What would happen if one pass cdev == NULL?

Is it really worth checking it here instead of just making the caller
pass a valid cdev?  There are a number of functions in the thermal
framework that don't check for valid cdevs or thermal zone pointers
and I don't see why this one is different.

> > +	return cdev->ops->get_actual_power && cdev->ops->state2power &&
> > +		cdev->ops->power2state;
> > +}
> > +
> > +u32 power_actor_get_max_power(struct thermal_cooling_device *);
> > +int power_actor_set_power(struct thermal_cooling_device *, u32);
> >  struct thermal_zone_device *thermal_zone_device_register(const char *, int, int,
> >  		void *, struct thermal_zone_device_ops *,
> >  		const struct thermal_zone_params *, int, int);
> 
> I am assuming the above two new functions are expected to be used also
> outside thermal core, right? If yes, I'd suggest exporting them. 

I don't expect it for the time being.  Wouldn't it be preferable to
export them when its needed instead?

Cheers,
Javi

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 6/9] thermal: cpu_cooling: implement the power cooling device API
  2015-01-02 14:37                   ` Eduardo Valentin
@ 2015-01-05 16:53                     ` Javi Merino
  2015-01-05 20:44                       ` Eduardo Valentin
  0 siblings, 1 reply; 48+ messages in thread
From: Javi Merino @ 2015-01-05 16:53 UTC (permalink / raw)
  To: Eduardo Valentin
  Cc: Viresh Kumar, Linux PM list, linux-kernel, Punit Agrawal,
	Mark Brown, Zhang Rui

On Fri, Jan 02, 2015 at 02:37:23PM +0000, Eduardo Valentin wrote:
> Content-Type: text/plain; charset=us-ascii
> Content-Disposition: inline
> Content-Transfer-Encoding: quoted-printable
> 
> Hello Javi,
> 
> Looks like the charset seams to be scrambled. Anyways, I will attempt to
> send a couple of feedback here..

Yes, some SMTP servers here are known to do that and I was using the
wrong one.  Sorry for that, it should not happen again.

> On Tue, Dec 09, 2014 at 11:00:43AM +0000, Javi Merino wrote:
> > On Tue, Dec 09, 2014 at 10:36:46AM +0000, Viresh Kumar wrote:
> > > On 9 December 2014 at 16:02, Javi Merino <javi.merino@arm.com> wrote:
> > > > Sorry but I don't follow.  __cpufreq_cooling_register() is passed a
> > > > clip_cpus mask, not a single cpu.  How do I get "the cpu for which
> > > > __cpufreq_cooling_register() is called" if not by looping through all
> > > > the cpus in the mask?
> > >=20
> > > Yeah, its np that is passed instead of cpu number. So, that wouldn't
> > > be usable. Also because of the limitations I explained earlier, it makes
> > > sense to iterate over all clip_cpus and finding which one owns OPPs.
> >=20
> > Ok, how about this then?  I've pasted the whole commit so as to avoid
> > confusion.
> 
> I should consider this one as V7 of this patch, probably..
> 
> >=20
> > diff --git a/Documentation/thermal/cpu-cooling-api.txt b/Documentation/th=
> ermal/cpu-cooling-api.txt
> > index fca24c931ec8..d438a900e374 100644
> > --- a/Documentation/thermal/cpu-cooling-api.txt
> > +++ b/Documentation/thermal/cpu-cooling-api.txt
> > @@ -25,8 +25,150 @@ the user. The registration APIs returns the cooling d=
> evice pointer.
> > =20
> >     clip_cpus: cpumask of cpus where the frequency constraints will happe=
> n.
> > =20
> > -1.1.2 void cpufreq_cooling_unregister(struct thermal_cooling_device *cde=
> v)
> > +1.1.2 struct thermal_cooling_device *cpufreq_power_cooling_register(
> > +    const struct cpumask *clip_cpus, u32 capacitance,
> > +    get_static_t plat_static_func)
> > +
> > +Similar to cpufreq_cooling_register, this function registers a cpufreq
> > +cooling device.  Using this function, the cooling device will
> > +implement the power extensions by using a simple cpu power model.  The
> > +cpus must have registered their OPPs using the OPP library.
> > +
> > +The additional parameters are needed for the power model (See 2. Power
> > +models).  "capacitance" is the dynamic power coefficient (See 2.1
> > +Dynamic power).  "plat_static_func" is a function to calculate the
> > +static power consumed by these cpus (See 2.2 Static power).
> > +
> > +1.1.3 struct thermal_cooling_device *of_cpufreq_power_cooling_register(
> > +    struct device_node *np, const struct cpumask *clip_cpus, u32 capacit=
> ance,
> > +    get_static_t plat_static_func)
> > +
> > +Similar to cpufreq_power_cooling_register, this function register a
> > +cpufreq cooling device with power extensions using the device tree
> > +information supplied by the np parameter.
> > +
> > +1.1.4 void cpufreq_cooling_unregister(struct thermal_cooling_device *cde=
> v)
> > =20
> >      This interface function unregisters the "thermal-cpufreq-%x" cooling=
>  device.
> > =20
> >      cdev: Cooling device pointer which has to be unregistered.
> > +
> > +2. Power models
> > +
> > +The power API registration functions provide a simple power model for
> > +CPUs.  The current power is calculated as dynamic + (optionally)
> > +static power.  This power model requires that the operating-points of
> > +the CPUs are registered using the kernel's opp library and the
> > +`cpufreq_frequency_table` is assigned to the `struct device` of the
> > +cpu.  If you are using the `cpufreq-cpu0.c` driver then the
> 
> cpufreq-cpu0.c is the old version of cpufreq-dt.c, right? I would
> suggest using CONFIG_* names instead of file names though.

Ok.

> > +`cpufreq_frequency_table` should already be assigned to the cpu
> > +device.
> > +
> > +The `plat_static_func` parameter of `cpufreq_power_cooling_register()`
> > +and `of_cpufreq_power_cooling_register()` is optional.  If you don't
> > +provide it, only dynamic power will be considered.
> > +
> > +2.1 Dynamic power
> > +
> > +The dynamic power consumption of a processor depends on many factors.
> > +For a given processor implementation the primary factors are:
> > +
> > +- The time the processor spends running, consuming dynamic power, as
> > +  compared to the time in idle states where dynamic consumption is
> > +  negligible.  Herein we refer to this as 'utilisation'.
> > +- The voltage and frequency levels as a result of DVFS.  The DVFS
> > +  level is a dominant factor governing power consumption.
> > +- In running time the 'execution' behaviour (instruction types, memory
> > +  access patterns and so forth) causes, in most cases, a second order
> > +  variation.  In pathological cases this variation can be significant,
> > +  but typically it is of a much lesser impact than the factors above.
> > +
> > +A high level dynamic power consumption model may then be represented as:
> > +
> > +Pdyn =3D f(run) * Voltage^2 * Frequency * Utilisation
> > +
> > +f(run) here represents the described execution behaviour and its
> > +result has a units of Watts/Hz/Volt^2 (this often expressed in
> > +mW/MHz/uVolt^2)
> > +
> > +The detailed behaviour for f(run) could be modelled on-line.  However,
> > +in practice, such an on-line model has dependencies on a number of
> > +implementation specific processor support and characterisation
> > +factors.  Therefore, in initial implementation that contribution is
> > +represented as a constant coefficient.  This is a simplification
> > +consistent with the relative contribution to overall power variation.
> > +
> > +In this simplified representation our model becomes:
> > +
> > +Pdyn =3D Kd * Voltage^2 * Frequency * Utilisation
> > +
> > +Where Kd (capacitance) represents an indicative running time dynamic
> > +power coefficient in fundamental units of mW/MHz/uVolt^2
> > +
> 
> Do we have Kd (capacitance) reference values for ARM processors? Is it
> worth adding a few of them as an example table here?=20

The reference numbers correspond not only to a particular processor
(e.g. Cortex-A15) but to specific SoCs, as the implementation
technology plays a key role in this.  I'll see if we can share some
reference values for specific SoCs.

> Where does one find Kd values?
> 
> Just looking for pointers for platform driver writers (potential users
> of these APIs).

I understand your concern.  I'm afraid the best I can say here is "ask
the SoC vendor".

> > +2.2 Static power
> > +
> > +Static leakage power consumption depends on a number of factors.  For a
> > +given circuit implementation the primary factors are:
> > +
> > +- Time the circuit spends in each 'power state'
> > +- Temperature
> > +- Operating voltage
> > +- Process grade
> > +
> > +The time the circuit spends in each 'power state' for a given
> > +evaluation period at first order means OFF or ON.  However,
> > +'retention' states can also be supported that reduce power during
> > +inactive periods without loss of context.
> > +
> > +Note: The visibility of state entries to the OS can vary, according to
> > +platform specifics, and this can then impact the accuracy of a model
> > +based on OS state information alone.  It might be possible in some
> > +cases to extract more accurate information from system resources.
> > +
> > +The temperature, operating voltage and process 'grade' (slow to fast)
> > +of the circuit are all significant factors in static leakage power
> > +consumption.  All of these have complex relationships to static power.
> > +
> > +Circuit implementation specific factors include the chosen silicon
> > +process as well as the type, number and size of transistors in both
> > +the logic gates and any RAM elements included.
> > +
> > +The static power consumption modelling must take into account the
> > +power managed regions that are implemented.  Taking the example of an
> > +ARM processor cluster, the modelling would take into account whether
> > +each CPU can be powered OFF separately or if only a single power
> > +region is implemented for the complete cluster.
> > +
> > +In one view, there are others, a static power consumption model can
> > +then start from a set of reference values for each power managed
> > +region (e.g. CPU, Cluster/L2) in each state (e.g. ON, OFF) at an
> > +arbitrary process grade, voltage and temperature point.  These values
> > +are then scaled for all of the following: the time in each state, the
> > +process grade, the current temperature and the operating voltage.
> > +However, since both implementation specific and complex relationships
> > +dominate the estimate, the appropriate interface to the model from the
> > +cpu cooling device is to provide a function callback that calculates
> > +the static power in this platform.  When registering the cpu cooling
> > +device pass a function pointer that follows the `get_static_t`
> > +prototype:
> > +
> > +    u32 plat_get_static(cpumask_t *cpumask, unsigned long voltage);
> > +
> > +with `cpumask` a cpumask of the cpus involved in the calculation and
> > +`voltage` the voltage at which they are operating.
> > +
> 
> What is the expected behavior of 'plat_get_static' if a wrong parameter
> is passed? Say, a cpumask that is invalid or a unsupported voltage?
> Shall it return 0? Does 0 means error?

I guess returning 0 and pr_warn() would be the best approach.  There's
no point in propagating an error since the upper layers can't really
do anything about it (other than maybe the governor ignoring this
cooling device?).

I'll clarify it in the documentation.

> Besides, how is the platform code supposed to return the estimate, given
> it depends on time spent in state, and we are not passing any info about
> time here?

Ok, I'll look into passing the time here.

> Same question applies to temperature.

The problem here is that the cpu cooling device does not know the
temperature of the processor.  It may or may not be the temperature of
the thermal zone.  The platform code is the best place to determine
the thermal zone whose sensor is closer to the processor and get its
temperature.

Alternatively, the thermal zone for the sensor that is closer to the
cpu could be passed when the cpu cooling device is registered and that
could be used to pass the cpu's temperature to the plat_get_static()
function.  Do you prefer this approach?

>                                                      For voltage, we are
> passing as parameter. For process grade, well, platform code is probably
> best point to determine it, so, no need.
> 
> > +If `plat_static_func` is NULL, static power is considered to be
> > +negligible for this platform and only dynamic power is considered.
> > +
> > +The platform specific callback can then use any combination of tables
> > +and/or equations to permute the estimated value.  Process grade
> > +information is not passed to the model since access to such data, from
> > +on-chip measurement capability or manufacture time data, is platform
> > +specific.
> > +
> 
> agreed
> 
> > +Note: the significance of static power for CPUs in comparison to
> > +dynamic power is highly dependent on implementation.  Given the
> > +potential complexity in implementation, the importance and accuracy of
> > +its inclusion when using cpu cooling devices should be assessed on a
> > +case by cases basis.
> > +
> > diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
> > index ad09e51ffae4..959a103d18ba 100644
> > --- a/drivers/thermal/cpu_cooling.c
> > +++ b/drivers/thermal/cpu_cooling.c
> > @@ -24,11 +24,25 @@
> >  #include <linux/thermal.h>
> >  #include <linux/cpufreq.h>
> >  #include <linux/err.h>
> > +#include <linux/pm_opp.h>
> >  #include <linux/slab.h>
> >  #include <linux/cpu.h>
> >  #include <linux/cpu_cooling.h>
> > =20
> >  /**
> > + * struct power_table - frequency to power conversion
> > + * @frequency:	frequency in KHz
> > + * @power:	power in mW
> > + *
> > + * This structure is built when the cooling device registers and helps
> > + * in translating frequency to power and viceversa.
> > + */
> > +struct power_table {
> > +	u32 frequency;
> > +	u32 power;
> > +};
> > +
> > +/**
> >   * struct cpufreq_cooling_device - data for cooling device with cpufreq
> >   * @id: unique integer value corresponding to each cpufreq_cooling_device
> >   *	registered.
> > @@ -39,6 +53,15 @@
> >   * @cpufreq_val: integer value representing the absolute value of the cl=
> ipped
> >   *	frequency.
> >   * @allowed_cpus: all the cpus involved for this cpufreq_cooling_device.
> > + * @last_load: load measured by the latest call to cpufreq_get_actual_po=
> wer()
> > + * @time_in_idle: previous reading of the absolute time that this cpu wa=
> s idle
> > + * @time_in_idle_timestamp: wall time of the last invocation of
> > + *	get_cpu_idle_time_us()
> > + * @dyn_power_table: array of struct power_table for frequency to power
> > + *	conversion
> 
> 
> Is this ordered somehow? Is it worth mentioning?

It's in ascending ordered.  It's documented in
build_dyn_power_table().

> > + * @dyn_power_table_entries: number of entries in the @dyn_power_table a=
> rray
> > + * @cpu_dev: the first cpu_device from @allowed_cpus that has OPPs regis=
> tered
> > + * @plat_get_static_power: callback to calculate the static power
> >   *
> >   * This structure is required for keeping information of each
> >   * cpufreq_cooling_device registered. In order to prevent corruption of =
> this a
> > @@ -51,6 +74,13 @@ struct cpufreq_cooling_device {
> >  	unsigned int cpufreq_val;
> >  	struct cpumask allowed_cpus;
> >  	struct list_head node;
> > +	u32 last_load;
> > +	u64 time_in_idle[NR_CPUS];
> > +	u64 time_in_idle_timestamp[NR_CPUS];
> > +	struct power_table *dyn_power_table;
> > +	int dyn_power_table_entries;
> > +	struct device *cpu_dev;
> > +	get_static_t plat_get_static_power;
> >  };
> >  static DEFINE_IDR(cpufreq_idr);
> >  static DEFINE_MUTEX(cooling_cpufreq_lock);
> > @@ -338,6 +368,204 @@ static int cpufreq_thermal_notifier(struct notifier=
> _block *nb,
> >  	return 0;
> >  }
> > =20
> > +/**
> > + * build_dyn_power_table() - create a dynamic power to frequency table
> > + * @cpufreq_device:	the cpufreq cooling device in which to store the tab=
> le
> > + * @capacitance: dynamic power coefficient for these cpus
> > + *
> > + * Build a dynamic power to frequency table for this cpu and store it
> > + * in @cpufreq_device.  This table will be used in cpu_power_to_freq() a=
> nd
> > + * cpu_freq_to_power() to convert between power and frequency
> > + * efficiently.  Power is stored in mW, frequency in KHz.  The
> > + * resulting table is in ascending order.
> 
> by which parameter? Do we assume a increasing convex relation between
> power and frequency?

By both parameters.  If frequency increases, power increases.  There's
no point in building a system that for lower frequencies you get
higher power consumption, right?  It's the worst of both worlds.

> > + *
> > + * Return: 0 on success, -E* on error.
> > + */
> > +static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_=
> device,
> > +				u32 capacitance)
> > +{
> > +	struct power_table *power_table;
> > +	struct dev_pm_opp *opp;
> > +	struct device *dev =3D NULL;
> > +	int num_opps =3D 0, cpu, i, ret =3D 0;
> > +	unsigned long freq;
> > +
> > +	rcu_read_lock();
> > +
> > +	for_each_cpu(cpu, &cpufreq_device->allowed_cpus) {
> > +		dev =3D get_cpu_device(cpu);
> > +		if (!dev) {
> > +			dev_warn(&cpufreq_device->cool_dev->device,
> > +				"No cpu device for cpu %d\n", cpu);
> > +			continue;
> > +		}
> > +
> > +		num_opps =3D dev_pm_opp_get_opp_count(dev);
> > +		if (num_opps > 0) {
> > +			break;
> > +		} else if (num_opps < 0) {
> > +			ret =3D num_opps;
> > +			goto unlock;
> > +		}
> > +	}
> > +
> > +	if (num_opps =3D=3D 0) {
> > +		ret =3D -EINVAL;
> > +		goto unlock;
> > +	}
> > +
> > +	power_table =3D devm_kcalloc(&cpufreq_device->cool_dev->device, num_opp=
> s,
> > +				sizeof(*power_table), GFP_KERNEL);
> > +
> > +	for (freq =3D 0, i =3D 0;
> > +	     opp =3D dev_pm_opp_find_freq_ceil(dev, &freq), !IS_ERR(opp);
> > +	     freq++, i++) {
> > +		u32 freq_mhz, voltage_mv;
> > +		u64 power;
> > +
> > +		freq_mhz =3D freq / 1000000;
> > +		voltage_mv =3D dev_pm_opp_get_voltage(opp) / 1000;
> > +
> > +		/*
> > +		 * Do the multiplication with MHz and millivolt so as
> > +		 * to not overflow.
> > +		 */
> > +		power =3D (u64)capacitance * freq_mhz * voltage_mv * voltage_mv;
> > +		do_div(power, 1000000000);
> > +
> > +		/* frequency is stored in power_table in KHz */
> > +		power_table[i].frequency =3D freq / 1000;
> 
> Why do we have a comment about freq unit but no comment about power unit? :=
> -)

It's in the documentation of the function.  I can repeat it here if
you want.

> > +		power_table[i].power =3D power;
> > +	}
> > +
> > +	if (i =3D=3D 0) {
> > +		ret =3D PTR_ERR(opp);
> > +		goto unlock;
> > +	}
> > +
> > +	cpufreq_device->cpu_dev =3D dev;
> > +	cpufreq_device->dyn_power_table =3D power_table;
> > +	cpufreq_device->dyn_power_table_entries =3D i;
> > +
> > +unlock:
> > +	rcu_read_unlock();
> > +	return ret;
> > +}
> > +
> > +static u32 cpu_freq_to_power(struct cpufreq_cooling_device *cpufreq_devi=
> ce,
> > +			     u32 freq)
> > +{
> > +	int i;
> > +	struct power_table *pt =3D cpufreq_device->dyn_power_table;
> > +
> > +	for (i =3D 1; i < cpufreq_device->dyn_power_table_entries; i++)
> > +		if (freq < pt[i].frequency)
> > +			break;
> > +
> > +	return pt[i - 1].power;
> > +}
> > +
> > +static u32 cpu_power_to_freq(struct cpufreq_cooling_device *cpufreq_devi=
> ce,
> > +			u32 power)
> > +{
> > +	int i;
> > +	struct power_table *pt =3D cpufreq_device->dyn_power_table;
> > +
> > +	for (i =3D 1; i < cpufreq_device->dyn_power_table_entries; i++)
> > +		if (power < pt[i].power)
> > +			break;
> > +
> > +	return pt[i - 1].frequency;
> > +}
> > +
> > +/**
> > + * get_load() - get load for a cpu since last updated
> > + * @cpufreq_device:	&struct cpufreq_cooling_device for this cpu
> > + * @cpu:	cpu number
> > + *
> > + * Return: The average load of cpu @cpu in percentage since this
> > + * function was last called.
> > + */
> > +static u32 get_load(struct cpufreq_cooling_device *cpufreq_device, int c=
> pu)
> > +{
> > +	u32 load;
> > +	u64 now, now_idle, delta_time, delta_idle;
> > +
> > +	now_idle =3D get_cpu_idle_time(cpu, &now, 0);
> > +	delta_idle =3D now_idle - cpufreq_device->time_in_idle[cpu];
> > +	delta_time =3D now - cpufreq_device->time_in_idle_timestamp[cpu];
> > +
> > +	if (delta_time <=3D delta_idle)
> > +		load =3D 0;
> > +	else
> > +		load =3D div64_u64(100 * (delta_time - delta_idle), delta_time);
> > +
> > +	cpufreq_device->time_in_idle[cpu] =3D now_idle;
> > +	cpufreq_device->time_in_idle_timestamp[cpu] =3D now;
> > +
> > +	return load;
> > +}
> > +
> > +/**
> > + * get_static_power() - calculate the static power consumed by the cpus
> > + * @cpufreq_device:	struct &cpufreq_cooling_device for this cpu cdev
> > + * @freq:	frequency in KHz
> > + *
> > + * Calculate the static power consumed by the cpus described by
> > + * @cpu_actor running at frequency @freq.  This function relies on a
> > + * platform specific function that should have been provided when the
> > + * actor was registered.  If it wasn't, the static power is assumed to
> > + * be negligible.
> > + *
> > + * Return: The static power consumed by the cpus.  It returns 0 on
> > + * error or if there is no plat_get_static_power().
> > + */
> > +static u32 get_static_power(struct cpufreq_cooling_device *cpufreq_devic=
> e,
> > +			unsigned long freq)
> > +{
> > +	struct dev_pm_opp *opp;
> > +	unsigned long voltage;
> > +	struct cpumask *cpumask =3D &cpufreq_device->allowed_cpus;
> > +	unsigned long freq_hz =3D freq * 1000;
> > +
> > +	if (!cpufreq_device->plat_get_static_power)
> > +		return 0;
> > +
> > +	rcu_read_lock();
> > +
> > +	opp =3D dev_pm_opp_find_freq_exact(cpufreq_device->cpu_dev, freq_hz,
> > +					true);
> > +	voltage =3D dev_pm_opp_get_voltage(opp);
> > +
> > +	rcu_read_unlock();
> > +
> > +	if (voltage =3D=3D 0) {
> > +		dev_warn_ratelimited(cpufreq_device->cpu_dev,
> > +				"Failed to get voltage for frequency %lu: %ld\n",
> > +				freq_hz, IS_ERR(opp) ? PTR_ERR(opp) : 0);
> > +		return 0;
> > +	}
> > +
> > +	return cpufreq_device->plat_get_static_power(cpumask, voltage);
> 
> temperature ? time in idle state ?

Replied above.

> > +}
> > +
> > +/**
> > + * get_dynamic_power() - calculate the dynamic power
> > + * @cpufreq_device:	&cpufreq_cooling_device for this cdev
> > + * @freq:	current frequency
> > + *
> 
> No description?

Well, the short description in the first line reads "calculate the
dynamic power" and the return value is "the dynamic power consumed by
the cpus described by @cpufreq_device".  There's really nothing more
that can be said about this function.

> > + * Return: the dynamic power consumed by the cpus described by
> > + * @cpufreq_device.
> > + */
> > +static u32 get_dynamic_power(struct cpufreq_cooling_device *cpufreq_devi=
> ce,
> > +			unsigned long freq)
> > +{
> > +	u32 raw_cpu_power;
> > +
> > +	raw_cpu_power =3D cpu_freq_to_power(cpufreq_device, freq);
> > +	return (raw_cpu_power * cpufreq_device->last_load) / 100;
> > +}
> > +
> >  /* cpufreq cooling device callback functions are defined below */
> > =20
> >  /**
> > @@ -407,8 +635,106 @@ static int cpufreq_set_cur_state(struct thermal_coo=
> ling_device *cdev,
> >  	return cpufreq_apply_cooling(cpufreq_device, state);
> >  }
> > =20
> > +/**
> > + * cpufreq_get_actual_power() - get the current power
> > + * @cdev:	&thermal_cooling_device pointer
> > + *
> 
> ditto.
> 
> those should generate kerneldoc warns. Can you please run kerneldoc
> script in your patch? make sure it does not add warns / errors.

It doesn't because the description is "Return the current power
consumption of the cpus in milliwatts."  Again, I don't see what else
can be said about these functions.

> > + * Return the current power consumption of the cpus in milliwatts.
> > + */
> > +static u32 cpufreq_get_actual_power(struct thermal_cooling_device *cdev)
> > +{
> > +	unsigned long freq;
> > +	int cpu;
> > +	u32 static_power, dynamic_power, total_load =3D 0;
> > +	struct cpufreq_cooling_device *cpufreq_device =3D cdev->devdata;
> > +
> > +	freq =3D cpufreq_quick_get(cpumask_any(&cpufreq_device->allowed_cpus));
> > +
> > +	for_each_cpu(cpu, &cpufreq_device->allowed_cpus) {
> > +		u32 load;
> > +
> > +		if (cpu_online(cpu))
> > +			load =3D get_load(cpufreq_device, cpu);
> > +		else
> > +			load =3D 0;
> > +
> > +		total_load +=3D load;
> > +	}
> > +
> > +	cpufreq_device->last_load =3D total_load;
> > +
> > +	static_power =3D get_static_power(cpufreq_device, freq);
> > +	dynamic_power =3D get_dynamic_power(cpufreq_device, freq);
> > +
> > +	return static_power + dynamic_power;
> > +}
> > +
> > +/**
> > + * cpufreq_state2power() - convert a cpu cdev state to power consumed
> > + * @cdev:	&thermal_cooling_device pointer
> > + * @state:	cooling device state to be converted
> > + *
> > + * Convert cooling device state @state into power consumption in milliwa=
> tts.
> 
> Considering 100% of utilization, right?
> 
> 
> Return: ?

Ok, I'll add:

Return: the power consumption.

> > + */
> > +static u32 cpufreq_state2power(struct thermal_cooling_device *cdev,
> > +			unsigned long state)
> > +{
> > +	unsigned int freq, num_cpus;
> > +	cpumask_t cpumask;
> > +	u32 static_power, dynamic_power;
> > +	struct cpufreq_cooling_device *cpufreq_device =3D cdev->devdata;
> > +
> > +	cpumask_and(&cpumask, &cpufreq_device->allowed_cpus, cpu_online_mask);
> > +	num_cpus =3D cpumask_weight(&cpumask);
> > +
> > +	freq =3D get_cpu_frequency(cpumask_any(&cpumask), state);
> > +	if (!freq)
> > +		return 0;
> > +
> > +	static_power =3D get_static_power(cpufreq_device, freq);
> > +	dynamic_power =3D cpu_freq_to_power(cpufreq_device, freq) * num_cpus;
> > +
> > +	return static_power + dynamic_power;
> > +}
> > +
> > +/**
> > + * cpufreq_power2state() - convert power to a cooling device state
> > + * @cdev:	&thermal_cooling_device pointer
> > + * @power:	power in milliwatts to be converted
> > + *
> > + * Calculate a cooling device state for the cpus described by @cdev
> > + * that would allow them to consume at most @power mW.
> 
> Return: ?=20

I'll add:

Return: the cooling device state

> > + */
> > +static unsigned long cpufreq_power2state(struct thermal_cooling_device *=
> cdev,
> > +					u32 power)
> > +{
> > +	unsigned int cpu, cur_freq, target_freq;
> > +	s32 dyn_power;
> > +	u32 last_load, normalised_power;
> > +	unsigned long cdev_state;
> > +	struct cpufreq_cooling_device *cpufreq_device =3D cdev->devdata;
> > +
> > +	cpu =3D cpumask_any_and(&cpufreq_device->allowed_cpus, cpu_online_mask);
> > +
> > +	cur_freq =3D cpufreq_quick_get(cpu);
> > +	dyn_power =3D power - get_static_power(cpufreq_device, cur_freq);
> > +	dyn_power =3D dyn_power > 0 ? dyn_power : 0;
> > +	last_load =3D cpufreq_device->last_load ?: 1;
> > +	normalised_power =3D (dyn_power * 100) / last_load;
> > +	target_freq =3D cpu_power_to_freq(cpufreq_device, normalised_power);
> > +
> 
> 
> I got confused with the description vs. the implementation here.
> Description says, a calculation from cooling device state to power. But
> calling this function twice for the same power value, in different
> moments, with difference cpu loads, (may) return different states
> between calls.. Can you please improve description?

Sure, I'll update the documentation.

> > +	cdev_state =3D cpufreq_cooling_get_level(cpu, target_freq);
> > +	if (cdev_state =3D=3D THERMAL_CSTATE_INVALID) {
> > +		pr_err_ratelimited("Failed to convert %dKHz for cpu %d into a cdev sta=
> te\n",
> > +				target_freq, cpu);
> > +		return 0;
> 
> How about passing state as parameter and allowing the API to return an
> error code?

You are right, it makes the cpufreq cooling device API more
consistent.  I'll make cpufreq_state2power() and cpufreq_power2state()
return 0 or error code and pass the power/state in a parameter.

> > +	}
> > +
> > +	return cdev_state;
> > +}
> > +
> >  /* Bind cpufreq callbacks to thermal cooling device ops */
> > -static struct thermal_cooling_device_ops const cpufreq_cooling_ops =3D {
> > +static struct thermal_cooling_device_ops cpufreq_cooling_ops =3D {
> 
> Why do we change the const?

Because below we do:

	if (capacitance) {
		cpufreq_cooling_ops.get_actual_power = cpufreq_get_actual_power;
		cpufreq_cooling_ops.state2power = cpufreq_state2power;
		cpufreq_cooling_ops.power2state = cpufreq_power2state;
...

> >  	.get_max_state =3D cpufreq_get_max_state,
> >  	.get_cur_state =3D cpufreq_get_cur_state,
> >  	.set_cur_state =3D cpufreq_set_cur_state,
> > @@ -434,7 +760,8 @@ static struct notifier_block thermal_cpufreq_notifier=
> _block =3D {
> >   */
> >  static struct thermal_cooling_device *
> >  __cpufreq_cooling_register(struct device_node *np,
> > -			   const struct cpumask *clip_cpus)
> > +			const struct cpumask *clip_cpus, u32 capacitance,
> > +			get_static_t plat_static_func)
> >  {
> >  	struct thermal_cooling_device *cool_dev;
> >  	struct cpufreq_cooling_device *cpufreq_dev =3D NULL;
> > @@ -464,10 +791,23 @@ __cpufreq_cooling_register(struct device_node *np,
> > =20
> >  	cpumask_copy(&cpufreq_dev->allowed_cpus, clip_cpus);
> > =20
> > +	if (capacitance) {
> > +		cpufreq_cooling_ops.get_actual_power =3D cpufreq_get_actual_power;
> > +		cpufreq_cooling_ops.state2power =3D cpufreq_state2power;
> > +		cpufreq_cooling_ops.power2state =3D cpufreq_power2state;
> > +		cpufreq_dev->plat_get_static_power =3D plat_static_func;
> > +
> > +		ret =3D build_dyn_power_table(cpufreq_dev, capacitance);
> > +		if (ret) {
> > +			cool_dev =3D ERR_PTR(ret);
> > +			goto free;
> > +		}
> > +	}
> > +
> >  	ret =3D get_idr(&cpufreq_idr, &cpufreq_dev->id);
> >  	if (ret) {
> > -		kfree(cpufreq_dev);
> > -		return ERR_PTR(-EINVAL);
> > +		cool_dev =3D ERR_PTR(-EINVAL);
> > +		goto free;
> >  	}
> > =20
> >  	snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d",
> > @@ -475,11 +815,8 @@ __cpufreq_cooling_register(struct device_node *np,
> > =20
> >  	cool_dev =3D thermal_of_cooling_device_register(np, dev_name, cpufreq_d=
> ev,
> >  						      &cpufreq_cooling_ops);
> > -	if (IS_ERR(cool_dev)) {
> > -		release_idr(&cpufreq_idr, cpufreq_dev->id);
> > -		kfree(cpufreq_dev);
> > -		return cool_dev;
> > -	}
> > +	if (IS_ERR(cool_dev))
> > +		goto release_idr;
> >  	cpufreq_dev->cool_dev =3D cool_dev;
> >  	cpufreq_dev->cpufreq_state =3D 0;
> >  	mutex_lock(&cooling_cpufreq_lock);
> > @@ -494,6 +831,12 @@ __cpufreq_cooling_register(struct device_node *np,
> >  	mutex_unlock(&cooling_cpufreq_lock);
> > =20
> >  	return cool_dev;
> > +
> > +release_idr:
> > +	release_idr(&cpufreq_idr, cpufreq_dev->id);
> > +free:
> > +	kfree(cpufreq_dev);
> > +	return cool_dev;
> >  }
> > =20
> >  /**
> > @@ -510,7 +853,7 @@ __cpufreq_cooling_register(struct device_node *np,
> >  struct thermal_cooling_device *
> >  cpufreq_cooling_register(const struct cpumask *clip_cpus)
> >  {
> > -	return __cpufreq_cooling_register(NULL, clip_cpus);
> > +	return __cpufreq_cooling_register(NULL, clip_cpus, 0, NULL);
> >  }
> >  EXPORT_SYMBOL_GPL(cpufreq_cooling_register);
> > =20
> > @@ -534,11 +877,77 @@ of_cpufreq_cooling_register(struct device_node *np,
> >  	if (!np)
> >  		return ERR_PTR(-EINVAL);
> > =20
> > -	return __cpufreq_cooling_register(np, clip_cpus);
> > +	return __cpufreq_cooling_register(np, clip_cpus, 0, NULL);
> >  }
> >  EXPORT_SYMBOL_GPL(of_cpufreq_cooling_register);
> > =20
> >  /**
> > + * cpufreq_power_cooling_register() - create cpufreq cooling device with=
>  power extensions
> > + * @clip_cpus:	cpumask of cpus where the frequency constraints will happ=
> en
> > + * @capacitance:	dynamic power coefficient for these cpus
> > + * @plat_static_func:	function to calculate the static power consumed by=
>  these
> > + *			cpus (optional)
> > + *
> > + * This interface function registers the cpufreq cooling device with
> > + * the name "thermal-cpufreq-%x".  This api can support multiple
> > + * instances of cpufreq cooling devices.  Using this function, the
> > + * cooling device will implement the power extensions by using a
> > + * simple cpu power model.  The cpus must have registered their OPPs
> > + * using the OPP library.
> > + *
> > + * An optional @plat_static_func may be provided to calculate the
> > + * static power consumed by these cpus.  If the platform's static
> > + * power consumption is unknown or negligible, make it NULL.
> > + *
> > + * Return: a valid struct thermal_cooling_device pointer on success,
> > + * on failure, it returns a corresponding ERR_PTR().
> > + */
> > +struct thermal_cooling_device *
> > +cpufreq_power_cooling_register(const struct cpumask *clip_cpus, u32 capa=
> citance,
> > +			get_static_t plat_static_func)
> > +{
> > +	return __cpufreq_cooling_register(NULL, clip_cpus, capacitance,
> > +				plat_static_func);
> > +}
> > +EXPORT_SYMBOL(cpufreq_power_cooling_register);
> > +
> > +/**
> > + * of_cpufreq_power_cooling_register() - create cpufreq cooling device w=
> ith power extensions
> > + * @np:	a valid struct device_node to the cooling device device tree node
> > + * @clip_cpus:	cpumask of cpus where the frequency constraints will happ=
> en
> > + * @capacitance:	dynamic power coefficient for these cpus
> > + * @plat_static_func:	function to calculate the static power consumed by=
>  these
> > + *			cpus (optional)
> > + *
> > + * This interface function registers the cpufreq cooling device with
> > + * the name "thermal-cpufreq-%x".  This api can support multiple
> > + * instances of cpufreq cooling devices.  Using this API, the cpufreq
> > + * cooling device will be linked to the device tree node provided.
> > + * Using this function, the cooling device will implement the power
> > + * extensions by using a simple cpu power model.  The cpus must have
> > + * registered their OPPs using the OPP library.
> > + *
> > + * An optional @plat_static_func may be provided to calculate the
> > + * static power consumed by these cpus.  If the platform's static
> > + * power consumption is unknown or negligible, make it NULL.
> > + *
> > + * Return: a valid struct thermal_cooling_device pointer on success,
> > + * on failure, it returns a corresponding ERR_PTR().
> > + */
> > +struct thermal_cooling_device *
> > +of_cpufreq_power_cooling_register(struct device_node *np,
> > +			const struct cpumask *clip_cpus, u32 capacitance,
> > +			get_static_t plat_static_func)
> > +{
> > +	if (!np)
> > +		return ERR_PTR(-EINVAL);
> > +
> > +	return __cpufreq_cooling_register(np, clip_cpus, capacitance,
> > +				plat_static_func);
> > +}
> > +EXPORT_SYMBOL(of_cpufreq_power_cooling_register);
> > +
> > +/**
> >   * cpufreq_cooling_unregister - function to remove cpufreq cooling devic=
> e.
> >   * @cdev: thermal cooling device pointer.
> >   *
> > diff --git a/include/linux/cpu_cooling.h b/include/linux/cpu_cooling.h
> > index c303d383def1..5c4f4567acf0 100644
> > --- a/include/linux/cpu_cooling.h
> > +++ b/include/linux/cpu_cooling.h
> > @@ -28,6 +28,8 @@
> >  #include <linux/thermal.h>
> >  #include <linux/cpumask.h>
> > =20
> > +typedef u32 (*get_static_t)(cpumask_t *cpumask, unsigned long voltage);
> > +
> >  #ifdef CONFIG_CPU_THERMAL
> >  /**
> >   * cpufreq_cooling_register - function to create cpufreq cooling device.
> > @@ -37,14 +39,38 @@ struct thermal_cooling_device *
> >  cpufreq_cooling_register(const struct cpumask *clip_cpus);
> > =20
> >  /**
> > + * cpufreq_power_cooling_register() - create cpufreq cooling device with=
>  power extensions
> > + * @clip_cpus: cpumask of cpus where the frequency constraints will happ=
> en
> > + * @capacitance:	dynamic power coefficient for these cpus
> > + * @plat_static_func:	function to calculate the static power consumed by=
>  these
> > + *			cpus (optional)
> > + */
> > +struct thermal_cooling_device *
> > +cpufreq_power_cooling_register(const struct cpumask *clip_cpus,
> > +			u32 capacitance, get_static_t plat_static_func);
> > +
> > +#ifdef CONFIG_THERMAL_OF
> > +/**
> >   * of_cpufreq_cooling_register - create cpufreq cooling device based on =
> DT.
> >   * @np: a valid struct device_node to the cooling device device tree nod=
> e.
> >   * @clip_cpus: cpumask of cpus where the frequency constraints will happ=
> en
> >   */
> > -#ifdef CONFIG_THERMAL_OF
> >  struct thermal_cooling_device *
> >  of_cpufreq_cooling_register(struct device_node *np,
> >  			    const struct cpumask *clip_cpus);
> > +
> > +/**
> > + * of_cpufreq_power_cooling_register() - create cpufreq cooling device w=
> ith power extensions
> > + * @np:	a valid struct device_node to the cooling device device tree node
> > + * @clip_cpus:	cpumask of cpus where the frequency constraints will happ=
> en
> > + * @capacitance:	dynamic power coefficient for these cpus
> > + * @plat_static_func:	function to calculate the static power consumed by=
>  these
> > + *			cpus (optional)
> > + */
> 
> I think we should avoid duplicating kernel doc entries.=20

I totally agree, but I was just trying to be consistent.
cpufreq_cooling_register() and cpufreq_cooling_unregister() have
kernel doc entries here and in cpu_cooling.c.  I'm happy to send a
patch that removes the duplicated kernel doc for
cpufreq_cooling_register() and friends in include/linux/cpu_cooling.h
and drop the duplication from this patch as well.

> > +struct thermal_cooling_device *
> > +of_cpufreq_power_cooling_register(struct device_node *np,
> > +				const struct cpumask *clip_cpus,
> > +				u32 capacitance, get_static_t plat_static_func);
> >  #else
> >  static inline struct thermal_cooling_device *
> >  of_cpufreq_cooling_register(struct device_node *np,
> > @@ -52,6 +78,14 @@ of_cpufreq_cooling_register(struct device_node *np,
> >  {
> >  	return NULL;
> >  }
> > +
> > +struct thermal_cooling_device *
> > +of_cpufreq_power_cooling_register(struct device_node *np,
> > +				const struct cpumask *clip_cpus,
> > +				u32 capacitance, get_static_t plat_static_func)
> 
> This is expected to be static inline.

Yes, I'll change it.

Thanks for reviewing a patch with such a horrible encoding,
Javi

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 6/9] thermal: cpu_cooling: implement the power cooling device API
  2015-01-05 16:53                     ` Javi Merino
@ 2015-01-05 20:44                       ` Eduardo Valentin
  2015-01-06 11:01                         ` Javi Merino
  0 siblings, 1 reply; 48+ messages in thread
From: Eduardo Valentin @ 2015-01-05 20:44 UTC (permalink / raw)
  To: Javi Merino
  Cc: Viresh Kumar, Linux PM list, linux-kernel, Punit Agrawal,
	Mark Brown, Zhang Rui

[-- Attachment #1: Type: text/plain, Size: 41188 bytes --]

Javi,

On Mon, Jan 05, 2015 at 04:53:40PM +0000, Javi Merino wrote:
> On Fri, Jan 02, 2015 at 02:37:23PM +0000, Eduardo Valentin wrote:
> > Content-Type: text/plain; charset=us-ascii
> > Content-Disposition: inline
> > Content-Transfer-Encoding: quoted-printable
> > 
> > Hello Javi,
> > 
> > Looks like the charset seams to be scrambled. Anyways, I will attempt to
> > send a couple of feedback here..
> 
> Yes, some SMTP servers here are known to do that and I was using the
> wrong one.  Sorry for that, it should not happen again.
> 
> > On Tue, Dec 09, 2014 at 11:00:43AM +0000, Javi Merino wrote:
> > > On Tue, Dec 09, 2014 at 10:36:46AM +0000, Viresh Kumar wrote:
> > > > On 9 December 2014 at 16:02, Javi Merino <javi.merino@arm.com> wrote:
> > > > > Sorry but I don't follow.  __cpufreq_cooling_register() is passed a
> > > > > clip_cpus mask, not a single cpu.  How do I get "the cpu for which
> > > > > __cpufreq_cooling_register() is called" if not by looping through all
> > > > > the cpus in the mask?
> > > >=20
> > > > Yeah, its np that is passed instead of cpu number. So, that wouldn't
> > > > be usable. Also because of the limitations I explained earlier, it makes
> > > > sense to iterate over all clip_cpus and finding which one owns OPPs.
> > >=20
> > > Ok, how about this then?  I've pasted the whole commit so as to avoid
> > > confusion.
> > 
> > I should consider this one as V7 of this patch, probably..
> > 
> > >=20
> > > diff --git a/Documentation/thermal/cpu-cooling-api.txt b/Documentation/th=
> > ermal/cpu-cooling-api.txt
> > > index fca24c931ec8..d438a900e374 100644
> > > --- a/Documentation/thermal/cpu-cooling-api.txt
> > > +++ b/Documentation/thermal/cpu-cooling-api.txt
> > > @@ -25,8 +25,150 @@ the user. The registration APIs returns the cooling d=
> > evice pointer.
> > > =20
> > >     clip_cpus: cpumask of cpus where the frequency constraints will happe=
> > n.
> > > =20
> > > -1.1.2 void cpufreq_cooling_unregister(struct thermal_cooling_device *cde=
> > v)
> > > +1.1.2 struct thermal_cooling_device *cpufreq_power_cooling_register(
> > > +    const struct cpumask *clip_cpus, u32 capacitance,
> > > +    get_static_t plat_static_func)
> > > +
> > > +Similar to cpufreq_cooling_register, this function registers a cpufreq
> > > +cooling device.  Using this function, the cooling device will
> > > +implement the power extensions by using a simple cpu power model.  The
> > > +cpus must have registered their OPPs using the OPP library.
> > > +
> > > +The additional parameters are needed for the power model (See 2. Power
> > > +models).  "capacitance" is the dynamic power coefficient (See 2.1
> > > +Dynamic power).  "plat_static_func" is a function to calculate the
> > > +static power consumed by these cpus (See 2.2 Static power).
> > > +
> > > +1.1.3 struct thermal_cooling_device *of_cpufreq_power_cooling_register(
> > > +    struct device_node *np, const struct cpumask *clip_cpus, u32 capacit=
> > ance,
> > > +    get_static_t plat_static_func)
> > > +
> > > +Similar to cpufreq_power_cooling_register, this function register a
> > > +cpufreq cooling device with power extensions using the device tree
> > > +information supplied by the np parameter.
> > > +
> > > +1.1.4 void cpufreq_cooling_unregister(struct thermal_cooling_device *cde=
> > v)
> > > =20
> > >      This interface function unregisters the "thermal-cpufreq-%x" cooling=
> >  device.
> > > =20
> > >      cdev: Cooling device pointer which has to be unregistered.
> > > +
> > > +2. Power models
> > > +
> > > +The power API registration functions provide a simple power model for
> > > +CPUs.  The current power is calculated as dynamic + (optionally)
> > > +static power.  This power model requires that the operating-points of
> > > +the CPUs are registered using the kernel's opp library and the
> > > +`cpufreq_frequency_table` is assigned to the `struct device` of the
> > > +cpu.  If you are using the `cpufreq-cpu0.c` driver then the
> > 
> > cpufreq-cpu0.c is the old version of cpufreq-dt.c, right? I would
> > suggest using CONFIG_* names instead of file names though.
> 
> Ok.
> 
> > > +`cpufreq_frequency_table` should already be assigned to the cpu
> > > +device.
> > > +
> > > +The `plat_static_func` parameter of `cpufreq_power_cooling_register()`
> > > +and `of_cpufreq_power_cooling_register()` is optional.  If you don't
> > > +provide it, only dynamic power will be considered.
> > > +
> > > +2.1 Dynamic power
> > > +
> > > +The dynamic power consumption of a processor depends on many factors.
> > > +For a given processor implementation the primary factors are:
> > > +
> > > +- The time the processor spends running, consuming dynamic power, as
> > > +  compared to the time in idle states where dynamic consumption is
> > > +  negligible.  Herein we refer to this as 'utilisation'.
> > > +- The voltage and frequency levels as a result of DVFS.  The DVFS
> > > +  level is a dominant factor governing power consumption.
> > > +- In running time the 'execution' behaviour (instruction types, memory
> > > +  access patterns and so forth) causes, in most cases, a second order
> > > +  variation.  In pathological cases this variation can be significant,
> > > +  but typically it is of a much lesser impact than the factors above.
> > > +
> > > +A high level dynamic power consumption model may then be represented as:
> > > +
> > > +Pdyn =3D f(run) * Voltage^2 * Frequency * Utilisation
> > > +
> > > +f(run) here represents the described execution behaviour and its
> > > +result has a units of Watts/Hz/Volt^2 (this often expressed in
> > > +mW/MHz/uVolt^2)
> > > +
> > > +The detailed behaviour for f(run) could be modelled on-line.  However,
> > > +in practice, such an on-line model has dependencies on a number of
> > > +implementation specific processor support and characterisation
> > > +factors.  Therefore, in initial implementation that contribution is
> > > +represented as a constant coefficient.  This is a simplification
> > > +consistent with the relative contribution to overall power variation.
> > > +
> > > +In this simplified representation our model becomes:
> > > +
> > > +Pdyn =3D Kd * Voltage^2 * Frequency * Utilisation
> > > +
> > > +Where Kd (capacitance) represents an indicative running time dynamic
> > > +power coefficient in fundamental units of mW/MHz/uVolt^2
> > > +
> > 
> > Do we have Kd (capacitance) reference values for ARM processors? Is it
> > worth adding a few of them as an example table here?=20
> 
> The reference numbers correspond not only to a particular processor
> (e.g. Cortex-A15) but to specific SoCs, as the implementation
> technology plays a key role in this.  I'll see if we can share some
> reference values for specific SoCs.
> 


It does not need to be a extensive / exhaustive list. A small set of
examples should do it.

> > Where does one find Kd values?
> > 
> > Just looking for pointers for platform driver writers (potential users
> > of these APIs).
> 
> I understand your concern.  I'm afraid the best I can say here is "ask
> the SoC vendor".

OK. Adding the above hint + a small set of examples should do it.


> 
> > > +2.2 Static power
> > > +
> > > +Static leakage power consumption depends on a number of factors.  For a
> > > +given circuit implementation the primary factors are:
> > > +
> > > +- Time the circuit spends in each 'power state'
> > > +- Temperature
> > > +- Operating voltage
> > > +- Process grade
> > > +
> > > +The time the circuit spends in each 'power state' for a given
> > > +evaluation period at first order means OFF or ON.  However,
> > > +'retention' states can also be supported that reduce power during
> > > +inactive periods without loss of context.
> > > +
> > > +Note: The visibility of state entries to the OS can vary, according to
> > > +platform specifics, and this can then impact the accuracy of a model
> > > +based on OS state information alone.  It might be possible in some
> > > +cases to extract more accurate information from system resources.
> > > +
> > > +The temperature, operating voltage and process 'grade' (slow to fast)
> > > +of the circuit are all significant factors in static leakage power
> > > +consumption.  All of these have complex relationships to static power.
> > > +
> > > +Circuit implementation specific factors include the chosen silicon
> > > +process as well as the type, number and size of transistors in both
> > > +the logic gates and any RAM elements included.
> > > +
> > > +The static power consumption modelling must take into account the
> > > +power managed regions that are implemented.  Taking the example of an
> > > +ARM processor cluster, the modelling would take into account whether
> > > +each CPU can be powered OFF separately or if only a single power
> > > +region is implemented for the complete cluster.
> > > +
> > > +In one view, there are others, a static power consumption model can
> > > +then start from a set of reference values for each power managed
> > > +region (e.g. CPU, Cluster/L2) in each state (e.g. ON, OFF) at an
> > > +arbitrary process grade, voltage and temperature point.  These values
> > > +are then scaled for all of the following: the time in each state, the
> > > +process grade, the current temperature and the operating voltage.
> > > +However, since both implementation specific and complex relationships
> > > +dominate the estimate, the appropriate interface to the model from the
> > > +cpu cooling device is to provide a function callback that calculates
> > > +the static power in this platform.  When registering the cpu cooling
> > > +device pass a function pointer that follows the `get_static_t`
> > > +prototype:
> > > +
> > > +    u32 plat_get_static(cpumask_t *cpumask, unsigned long voltage);
> > > +
> > > +with `cpumask` a cpumask of the cpus involved in the calculation and
> > > +`voltage` the voltage at which they are operating.
> > > +
> > 
> > What is the expected behavior of 'plat_get_static' if a wrong parameter
> > is passed? Say, a cpumask that is invalid or a unsupported voltage?
> > Shall it return 0? Does 0 means error?
> 
> I guess returning 0 and pr_warn() would be the best approach.  There's
> no point in propagating an error since the upper layers can't really
> do anything about it (other than maybe the governor ignoring this
> cooling device?).
> 

Yeah, governors may ignore them. IMO, that is the best expected
behavior, instead of using wrong values silently.

> I'll clarify it in the documentation.

cool.

> 
> > Besides, how is the platform code supposed to return the estimate, given
> > it depends on time spent in state, and we are not passing any info about
> > time here?
> 
> Ok, I'll look into passing the time here.
> 

nice!

> > Same question applies to temperature.
> 
> The problem here is that the cpu cooling device does not know the
> temperature of the processor.  It may or may not be the temperature of
> the thermal zone.  The platform code is the best place to determine
> the thermal zone whose sensor is closer to the processor and get its
> temperature.
> 
> Alternatively, the thermal zone for the sensor that is closer to the
> cpu could be passed when the cpu cooling device is registered and that
> could be used to pass the cpu's temperature to the plat_get_static()
> function.  Do you prefer this approach?
> 


I believe the former is better. You may leave to platform layer to
request temperature from appropriate thermal zone (s) and then deriving the
correct extrapolated temperature. 

However, the way the document text is now may confuse readers. We should
mention in the text how to get temperature, given that it is not passed
by the API.

> >                                                      For voltage, we are
> > passing as parameter. For process grade, well, platform code is probably
> > best point to determine it, so, no need.
> > 
> > > +If `plat_static_func` is NULL, static power is considered to be
> > > +negligible for this platform and only dynamic power is considered.
> > > +
> > > +The platform specific callback can then use any combination of tables
> > > +and/or equations to permute the estimated value.  Process grade
> > > +information is not passed to the model since access to such data, from
> > > +on-chip measurement capability or manufacture time data, is platform
> > > +specific.
> > > +
> > 
> > agreed
> > 
> > > +Note: the significance of static power for CPUs in comparison to
> > > +dynamic power is highly dependent on implementation.  Given the
> > > +potential complexity in implementation, the importance and accuracy of
> > > +its inclusion when using cpu cooling devices should be assessed on a
> > > +case by cases basis.
> > > +
> > > diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
> > > index ad09e51ffae4..959a103d18ba 100644
> > > --- a/drivers/thermal/cpu_cooling.c
> > > +++ b/drivers/thermal/cpu_cooling.c
> > > @@ -24,11 +24,25 @@
> > >  #include <linux/thermal.h>
> > >  #include <linux/cpufreq.h>
> > >  #include <linux/err.h>
> > > +#include <linux/pm_opp.h>
> > >  #include <linux/slab.h>
> > >  #include <linux/cpu.h>
> > >  #include <linux/cpu_cooling.h>
> > > =20
> > >  /**
> > > + * struct power_table - frequency to power conversion
> > > + * @frequency:	frequency in KHz
> > > + * @power:	power in mW
> > > + *
> > > + * This structure is built when the cooling device registers and helps
> > > + * in translating frequency to power and viceversa.
> > > + */
> > > +struct power_table {
> > > +	u32 frequency;
> > > +	u32 power;
> > > +};
> > > +
> > > +/**
> > >   * struct cpufreq_cooling_device - data for cooling device with cpufreq
> > >   * @id: unique integer value corresponding to each cpufreq_cooling_device
> > >   *	registered.
> > > @@ -39,6 +53,15 @@
> > >   * @cpufreq_val: integer value representing the absolute value of the cl=
> > ipped
> > >   *	frequency.
> > >   * @allowed_cpus: all the cpus involved for this cpufreq_cooling_device.
> > > + * @last_load: load measured by the latest call to cpufreq_get_actual_po=
> > wer()
> > > + * @time_in_idle: previous reading of the absolute time that this cpu wa=
> > s idle
> > > + * @time_in_idle_timestamp: wall time of the last invocation of
> > > + *	get_cpu_idle_time_us()
> > > + * @dyn_power_table: array of struct power_table for frequency to power
> > > + *	conversion
> > 
> > 
> > Is this ordered somehow? Is it worth mentioning?
> 
> It's in ascending ordered.  It's documented in
> build_dyn_power_table().
> 

I see.. but the field here needs to be documented too, don't you
agree? I would mention here also that this field is expected to be
ordered. Just for the sake of having a good kernel doc entry.

> > > + * @dyn_power_table_entries: number of entries in the @dyn_power_table a=
> > rray
> > > + * @cpu_dev: the first cpu_device from @allowed_cpus that has OPPs regis=
> > tered
> > > + * @plat_get_static_power: callback to calculate the static power
> > >   *
> > >   * This structure is required for keeping information of each
> > >   * cpufreq_cooling_device registered. In order to prevent corruption of =
> > this a
> > > @@ -51,6 +74,13 @@ struct cpufreq_cooling_device {
> > >  	unsigned int cpufreq_val;
> > >  	struct cpumask allowed_cpus;
> > >  	struct list_head node;
> > > +	u32 last_load;
> > > +	u64 time_in_idle[NR_CPUS];
> > > +	u64 time_in_idle_timestamp[NR_CPUS];
> > > +	struct power_table *dyn_power_table;
> > > +	int dyn_power_table_entries;
> > > +	struct device *cpu_dev;
> > > +	get_static_t plat_get_static_power;
> > >  };
> > >  static DEFINE_IDR(cpufreq_idr);
> > >  static DEFINE_MUTEX(cooling_cpufreq_lock);
> > > @@ -338,6 +368,204 @@ static int cpufreq_thermal_notifier(struct notifier=
> > _block *nb,
> > >  	return 0;
> > >  }
> > > =20
> > > +/**
> > > + * build_dyn_power_table() - create a dynamic power to frequency table
> > > + * @cpufreq_device:	the cpufreq cooling device in which to store the tab=
> > le
> > > + * @capacitance: dynamic power coefficient for these cpus
> > > + *
> > > + * Build a dynamic power to frequency table for this cpu and store it
> > > + * in @cpufreq_device.  This table will be used in cpu_power_to_freq() a=
> > nd
> > > + * cpu_freq_to_power() to convert between power and frequency
> > > + * efficiently.  Power is stored in mW, frequency in KHz.  The
> > > + * resulting table is in ascending order.
> > 
> > by which parameter? Do we assume a increasing convex relation between
> > power and frequency?
> 
> By both parameters.  If frequency increases, power increases.  There's
> no point in building a system that for lower frequencies you get
> higher power consumption, right?  It's the worst of both worlds.

OK. Agreed, let's not make it overcomplicated.

> 
> > > + *
> > > + * Return: 0 on success, -E* on error.
> > > + */
> > > +static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_=
> > device,
> > > +				u32 capacitance)
> > > +{
> > > +	struct power_table *power_table;
> > > +	struct dev_pm_opp *opp;
> > > +	struct device *dev =3D NULL;
> > > +	int num_opps =3D 0, cpu, i, ret =3D 0;
> > > +	unsigned long freq;
> > > +
> > > +	rcu_read_lock();
> > > +
> > > +	for_each_cpu(cpu, &cpufreq_device->allowed_cpus) {
> > > +		dev =3D get_cpu_device(cpu);
> > > +		if (!dev) {
> > > +			dev_warn(&cpufreq_device->cool_dev->device,
> > > +				"No cpu device for cpu %d\n", cpu);
> > > +			continue;
> > > +		}
> > > +
> > > +		num_opps =3D dev_pm_opp_get_opp_count(dev);
> > > +		if (num_opps > 0) {
> > > +			break;
> > > +		} else if (num_opps < 0) {
> > > +			ret =3D num_opps;
> > > +			goto unlock;
> > > +		}
> > > +	}
> > > +
> > > +	if (num_opps =3D=3D 0) {
> > > +		ret =3D -EINVAL;
> > > +		goto unlock;
> > > +	}
> > > +
> > > +	power_table =3D devm_kcalloc(&cpufreq_device->cool_dev->device, num_opp=
> > s,
> > > +				sizeof(*power_table), GFP_KERNEL);
> > > +
> > > +	for (freq =3D 0, i =3D 0;
> > > +	     opp =3D dev_pm_opp_find_freq_ceil(dev, &freq), !IS_ERR(opp);
> > > +	     freq++, i++) {
> > > +		u32 freq_mhz, voltage_mv;
> > > +		u64 power;
> > > +
> > > +		freq_mhz =3D freq / 1000000;
> > > +		voltage_mv =3D dev_pm_opp_get_voltage(opp) / 1000;
> > > +
> > > +		/*
> > > +		 * Do the multiplication with MHz and millivolt so as
> > > +		 * to not overflow.
> > > +		 */
> > > +		power =3D (u64)capacitance * freq_mhz * voltage_mv * voltage_mv;
> > > +		do_div(power, 1000000000);
> > > +
> > > +		/* frequency is stored in power_table in KHz */
> > > +		power_table[i].frequency =3D freq / 1000;
> > 
> > Why do we have a comment about freq unit but no comment about power unit? :=
> > -)
> 
> It's in the documentation of the function.  I can repeat it here if
> you want.

Well, I would say, you either comment both here, or remove the above.

> 
> > > +		power_table[i].power =3D power;
> > > +	}
> > > +
> > > +	if (i =3D=3D 0) {
> > > +		ret =3D PTR_ERR(opp);
> > > +		goto unlock;
> > > +	}
> > > +
> > > +	cpufreq_device->cpu_dev =3D dev;
> > > +	cpufreq_device->dyn_power_table =3D power_table;
> > > +	cpufreq_device->dyn_power_table_entries =3D i;
> > > +
> > > +unlock:
> > > +	rcu_read_unlock();
> > > +	return ret;
> > > +}
> > > +
> > > +static u32 cpu_freq_to_power(struct cpufreq_cooling_device *cpufreq_devi=
> > ce,
> > > +			     u32 freq)
> > > +{
> > > +	int i;
> > > +	struct power_table *pt =3D cpufreq_device->dyn_power_table;
> > > +
> > > +	for (i =3D 1; i < cpufreq_device->dyn_power_table_entries; i++)
> > > +		if (freq < pt[i].frequency)
> > > +			break;
> > > +
> > > +	return pt[i - 1].power;
> > > +}
> > > +
> > > +static u32 cpu_power_to_freq(struct cpufreq_cooling_device *cpufreq_devi=
> > ce,
> > > +			u32 power)
> > > +{
> > > +	int i;
> > > +	struct power_table *pt =3D cpufreq_device->dyn_power_table;
> > > +
> > > +	for (i =3D 1; i < cpufreq_device->dyn_power_table_entries; i++)
> > > +		if (power < pt[i].power)
> > > +			break;
> > > +
> > > +	return pt[i - 1].frequency;
> > > +}
> > > +
> > > +/**
> > > + * get_load() - get load for a cpu since last updated
> > > + * @cpufreq_device:	&struct cpufreq_cooling_device for this cpu
> > > + * @cpu:	cpu number
> > > + *
> > > + * Return: The average load of cpu @cpu in percentage since this
> > > + * function was last called.
> > > + */
> > > +static u32 get_load(struct cpufreq_cooling_device *cpufreq_device, int c=
> > pu)
> > > +{
> > > +	u32 load;
> > > +	u64 now, now_idle, delta_time, delta_idle;
> > > +
> > > +	now_idle =3D get_cpu_idle_time(cpu, &now, 0);
> > > +	delta_idle =3D now_idle - cpufreq_device->time_in_idle[cpu];
> > > +	delta_time =3D now - cpufreq_device->time_in_idle_timestamp[cpu];
> > > +
> > > +	if (delta_time <=3D delta_idle)
> > > +		load =3D 0;
> > > +	else
> > > +		load =3D div64_u64(100 * (delta_time - delta_idle), delta_time);
> > > +
> > > +	cpufreq_device->time_in_idle[cpu] =3D now_idle;
> > > +	cpufreq_device->time_in_idle_timestamp[cpu] =3D now;
> > > +
> > > +	return load;
> > > +}
> > > +
> > > +/**
> > > + * get_static_power() - calculate the static power consumed by the cpus
> > > + * @cpufreq_device:	struct &cpufreq_cooling_device for this cpu cdev
> > > + * @freq:	frequency in KHz
> > > + *
> > > + * Calculate the static power consumed by the cpus described by
> > > + * @cpu_actor running at frequency @freq.  This function relies on a
> > > + * platform specific function that should have been provided when the
> > > + * actor was registered.  If it wasn't, the static power is assumed to
> > > + * be negligible.
> > > + *
> > > + * Return: The static power consumed by the cpus.  It returns 0 on
> > > + * error or if there is no plat_get_static_power().
> > > + */
> > > +static u32 get_static_power(struct cpufreq_cooling_device *cpufreq_devic=
> > e,
> > > +			unsigned long freq)
> > > +{
> > > +	struct dev_pm_opp *opp;
> > > +	unsigned long voltage;
> > > +	struct cpumask *cpumask =3D &cpufreq_device->allowed_cpus;
> > > +	unsigned long freq_hz =3D freq * 1000;
> > > +
> > > +	if (!cpufreq_device->plat_get_static_power)
> > > +		return 0;
> > > +
> > > +	rcu_read_lock();
> > > +
> > > +	opp =3D dev_pm_opp_find_freq_exact(cpufreq_device->cpu_dev, freq_hz,
> > > +					true);
> > > +	voltage =3D dev_pm_opp_get_voltage(opp);
> > > +
> > > +	rcu_read_unlock();
> > > +
> > > +	if (voltage =3D=3D 0) {
> > > +		dev_warn_ratelimited(cpufreq_device->cpu_dev,
> > > +				"Failed to get voltage for frequency %lu: %ld\n",
> > > +				freq_hz, IS_ERR(opp) ? PTR_ERR(opp) : 0);
> > > +		return 0;
> > > +	}
> > > +
> > > +	return cpufreq_device->plat_get_static_power(cpumask, voltage);
> > 
> > temperature ? time in idle state ?
> 
> Replied above.


ok

> 
> > > +}
> > > +
> > > +/**
> > > + * get_dynamic_power() - calculate the dynamic power
> > > + * @cpufreq_device:	&cpufreq_cooling_device for this cdev
> > > + * @freq:	current frequency
> > > + *
> > 
> > No description?
> 
> Well, the short description in the first line reads "calculate the
> dynamic power" and the return value is "the dynamic power consumed by
> the cpus described by @cpufreq_device".  There's really nothing more
> that can be said about this function.


Yeah, but kerneldoc still complains about entries without descriptions
(and without Return: entries too, or missing parameter descriptions).
So, you should describe the entry properly, with all required fields.

> 
> > > + * Return: the dynamic power consumed by the cpus described by
> > > + * @cpufreq_device.
> > > + */
> > > +static u32 get_dynamic_power(struct cpufreq_cooling_device *cpufreq_devi=
> > ce,
> > > +			unsigned long freq)
> > > +{
> > > +	u32 raw_cpu_power;
> > > +
> > > +	raw_cpu_power =3D cpu_freq_to_power(cpufreq_device, freq);
> > > +	return (raw_cpu_power * cpufreq_device->last_load) / 100;
> > > +}
> > > +
> > >  /* cpufreq cooling device callback functions are defined below */
> > > =20
> > >  /**
> > > @@ -407,8 +635,106 @@ static int cpufreq_set_cur_state(struct thermal_coo=
> > ling_device *cdev,
> > >  	return cpufreq_apply_cooling(cpufreq_device, state);
> > >  }
> > > =20
> > > +/**
> > > + * cpufreq_get_actual_power() - get the current power
> > > + * @cdev:	&thermal_cooling_device pointer
> > > + *
> > 
> > ditto.
> > 
> > those should generate kerneldoc warns. Can you please run kerneldoc
> > script in your patch? make sure it does not add warns / errors.
> 
> It doesn't because the description is "Return the current power
> consumption of the cpus in milliwatts."  Again, I don't see what else
> can be said about these functions.


I see, then you must include a 'Return:' section for this kerneldoc
entry.

> 
> > > + * Return the current power consumption of the cpus in milliwatts.
> > > + */
> > > +static u32 cpufreq_get_actual_power(struct thermal_cooling_device *cdev)
> > > +{
> > > +	unsigned long freq;
> > > +	int cpu;
> > > +	u32 static_power, dynamic_power, total_load =3D 0;
> > > +	struct cpufreq_cooling_device *cpufreq_device =3D cdev->devdata;
> > > +
> > > +	freq =3D cpufreq_quick_get(cpumask_any(&cpufreq_device->allowed_cpus));
> > > +
> > > +	for_each_cpu(cpu, &cpufreq_device->allowed_cpus) {
> > > +		u32 load;
> > > +
> > > +		if (cpu_online(cpu))
> > > +			load =3D get_load(cpufreq_device, cpu);
> > > +		else
> > > +			load =3D 0;
> > > +
> > > +		total_load +=3D load;
> > > +	}
> > > +
> > > +	cpufreq_device->last_load =3D total_load;
> > > +
> > > +	static_power =3D get_static_power(cpufreq_device, freq);
> > > +	dynamic_power =3D get_dynamic_power(cpufreq_device, freq);
> > > +
> > > +	return static_power + dynamic_power;
> > > +}
> > > +
> > > +/**
> > > + * cpufreq_state2power() - convert a cpu cdev state to power consumed
> > > + * @cdev:	&thermal_cooling_device pointer
> > > + * @state:	cooling device state to be converted
> > > + *
> > > + * Convert cooling device state @state into power consumption in milliwa=
> > tts.
> > 
> > Considering 100% of utilization, right?
> > 
> > 
> > Return: ?
> 
> Ok, I'll add:
> 
> Return: the power consumption.


Is there any fail case? 

> 
> > > + */
> > > +static u32 cpufreq_state2power(struct thermal_cooling_device *cdev,
> > > +			unsigned long state)
> > > +{
> > > +	unsigned int freq, num_cpus;
> > > +	cpumask_t cpumask;
> > > +	u32 static_power, dynamic_power;
> > > +	struct cpufreq_cooling_device *cpufreq_device =3D cdev->devdata;
> > > +
> > > +	cpumask_and(&cpumask, &cpufreq_device->allowed_cpus, cpu_online_mask);
> > > +	num_cpus =3D cpumask_weight(&cpumask);
> > > +
> > > +	freq =3D get_cpu_frequency(cpumask_any(&cpumask), state);
> > > +	if (!freq)
> > > +		return 0;

Looks like 0 means 0 mW and error situation, this needs to go into
kernel doc.

> > > +
> > > +	static_power =3D get_static_power(cpufreq_device, freq);
> > > +	dynamic_power =3D cpu_freq_to_power(cpufreq_device, freq) * num_cpus;
> > > +
> > > +	return static_power + dynamic_power;
> > > +}
> > > +
> > > +/**
> > > + * cpufreq_power2state() - convert power to a cooling device state
> > > + * @cdev:	&thermal_cooling_device pointer
> > > + * @power:	power in milliwatts to be converted
> > > + *
> > > + * Calculate a cooling device state for the cpus described by @cdev
> > > + * that would allow them to consume at most @power mW.
> > 
> > Return: ?=20
> 
> I'll add:
> 
> Return: the cooling device state

Fail case?


> 
> > > + */
> > > +static unsigned long cpufreq_power2state(struct thermal_cooling_device *=
> > cdev,
> > > +					u32 power)
> > > +{
> > > +	unsigned int cpu, cur_freq, target_freq;
> > > +	s32 dyn_power;
> > > +	u32 last_load, normalised_power;
> > > +	unsigned long cdev_state;
> > > +	struct cpufreq_cooling_device *cpufreq_device =3D cdev->devdata;
> > > +
> > > +	cpu =3D cpumask_any_and(&cpufreq_device->allowed_cpus, cpu_online_mask);
> > > +
> > > +	cur_freq =3D cpufreq_quick_get(cpu);
> > > +	dyn_power =3D power - get_static_power(cpufreq_device, cur_freq);
> > > +	dyn_power =3D dyn_power > 0 ? dyn_power : 0;
> > > +	last_load =3D cpufreq_device->last_load ?: 1;
> > > +	normalised_power =3D (dyn_power * 100) / last_load;
> > > +	target_freq =3D cpu_power_to_freq(cpufreq_device, normalised_power);
> > > +
> > 
> > 
> > I got confused with the description vs. the implementation here.
> > Description says, a calculation from cooling device state to power. But
> > calling this function twice for the same power value, in different
> > moments, with difference cpu loads, (may) return different states
> > between calls.. Can you please improve description?
> 
> Sure, I'll update the documentation.
> 
> > > +	cdev_state =3D cpufreq_cooling_get_level(cpu, target_freq);
> > > +	if (cdev_state =3D=3D THERMAL_CSTATE_INVALID) {
> > > +		pr_err_ratelimited("Failed to convert %dKHz for cpu %d into a cdev sta=
> > te\n",
> > > +				target_freq, cpu);
> > > +		return 0;
> > 
> > How about passing state as parameter and allowing the API to return an
> > error code?
> 
> You are right, it makes the cpufreq cooling device API more
> consistent.  I'll make cpufreq_state2power() and cpufreq_power2state()
> return 0 or error code and pass the power/state in a parameter.
> 

good.


> > > +	}
> > > +
> > > +	return cdev_state;
> > > +}
> > > +
> > >  /* Bind cpufreq callbacks to thermal cooling device ops */
> > > -static struct thermal_cooling_device_ops const cpufreq_cooling_ops =3D {
> > > +static struct thermal_cooling_device_ops cpufreq_cooling_ops =3D {
> > 
> > Why do we change the const?
> 
> Because below we do:
> 
> 	if (capacitance) {
> 		cpufreq_cooling_ops.get_actual_power = cpufreq_get_actual_power;
> 		cpufreq_cooling_ops.state2power = cpufreq_state2power;
> 		cpufreq_cooling_ops.power2state = cpufreq_power2state;
> ...
> 

ok... I missed that.

> > >  	.get_max_state =3D cpufreq_get_max_state,
> > >  	.get_cur_state =3D cpufreq_get_cur_state,
> > >  	.set_cur_state =3D cpufreq_set_cur_state,
> > > @@ -434,7 +760,8 @@ static struct notifier_block thermal_cpufreq_notifier=
> > _block =3D {
> > >   */
> > >  static struct thermal_cooling_device *
> > >  __cpufreq_cooling_register(struct device_node *np,
> > > -			   const struct cpumask *clip_cpus)
> > > +			const struct cpumask *clip_cpus, u32 capacitance,
> > > +			get_static_t plat_static_func)
> > >  {
> > >  	struct thermal_cooling_device *cool_dev;
> > >  	struct cpufreq_cooling_device *cpufreq_dev =3D NULL;
> > > @@ -464,10 +791,23 @@ __cpufreq_cooling_register(struct device_node *np,
> > > =20
> > >  	cpumask_copy(&cpufreq_dev->allowed_cpus, clip_cpus);
> > > =20
> > > +	if (capacitance) {
> > > +		cpufreq_cooling_ops.get_actual_power =3D cpufreq_get_actual_power;
> > > +		cpufreq_cooling_ops.state2power =3D cpufreq_state2power;
> > > +		cpufreq_cooling_ops.power2state =3D cpufreq_power2state;
> > > +		cpufreq_dev->plat_get_static_power =3D plat_static_func;
> > > +
> > > +		ret =3D build_dyn_power_table(cpufreq_dev, capacitance);
> > > +		if (ret) {
> > > +			cool_dev =3D ERR_PTR(ret);
> > > +			goto free;
> > > +		}
> > > +	}
> > > +
> > >  	ret =3D get_idr(&cpufreq_idr, &cpufreq_dev->id);
> > >  	if (ret) {
> > > -		kfree(cpufreq_dev);
> > > -		return ERR_PTR(-EINVAL);
> > > +		cool_dev =3D ERR_PTR(-EINVAL);
> > > +		goto free;
> > >  	}
> > > =20
> > >  	snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d",
> > > @@ -475,11 +815,8 @@ __cpufreq_cooling_register(struct device_node *np,
> > > =20
> > >  	cool_dev =3D thermal_of_cooling_device_register(np, dev_name, cpufreq_d=
> > ev,
> > >  						      &cpufreq_cooling_ops);
> > > -	if (IS_ERR(cool_dev)) {
> > > -		release_idr(&cpufreq_idr, cpufreq_dev->id);
> > > -		kfree(cpufreq_dev);
> > > -		return cool_dev;
> > > -	}
> > > +	if (IS_ERR(cool_dev))
> > > +		goto release_idr;
> > >  	cpufreq_dev->cool_dev =3D cool_dev;
> > >  	cpufreq_dev->cpufreq_state =3D 0;
> > >  	mutex_lock(&cooling_cpufreq_lock);
> > > @@ -494,6 +831,12 @@ __cpufreq_cooling_register(struct device_node *np,
> > >  	mutex_unlock(&cooling_cpufreq_lock);
> > > =20
> > >  	return cool_dev;
> > > +
> > > +release_idr:
> > > +	release_idr(&cpufreq_idr, cpufreq_dev->id);
> > > +free:
> > > +	kfree(cpufreq_dev);
> > > +	return cool_dev;
> > >  }
> > > =20
> > >  /**
> > > @@ -510,7 +853,7 @@ __cpufreq_cooling_register(struct device_node *np,
> > >  struct thermal_cooling_device *
> > >  cpufreq_cooling_register(const struct cpumask *clip_cpus)
> > >  {
> > > -	return __cpufreq_cooling_register(NULL, clip_cpus);
> > > +	return __cpufreq_cooling_register(NULL, clip_cpus, 0, NULL);
> > >  }
> > >  EXPORT_SYMBOL_GPL(cpufreq_cooling_register);
> > > =20
> > > @@ -534,11 +877,77 @@ of_cpufreq_cooling_register(struct device_node *np,
> > >  	if (!np)
> > >  		return ERR_PTR(-EINVAL);
> > > =20
> > > -	return __cpufreq_cooling_register(np, clip_cpus);
> > > +	return __cpufreq_cooling_register(np, clip_cpus, 0, NULL);
> > >  }
> > >  EXPORT_SYMBOL_GPL(of_cpufreq_cooling_register);
> > > =20
> > >  /**
> > > + * cpufreq_power_cooling_register() - create cpufreq cooling device with=
> >  power extensions
> > > + * @clip_cpus:	cpumask of cpus where the frequency constraints will happ=
> > en
> > > + * @capacitance:	dynamic power coefficient for these cpus
> > > + * @plat_static_func:	function to calculate the static power consumed by=
> >  these
> > > + *			cpus (optional)
> > > + *
> > > + * This interface function registers the cpufreq cooling device with
> > > + * the name "thermal-cpufreq-%x".  This api can support multiple
> > > + * instances of cpufreq cooling devices.  Using this function, the
> > > + * cooling device will implement the power extensions by using a
> > > + * simple cpu power model.  The cpus must have registered their OPPs
> > > + * using the OPP library.
> > > + *
> > > + * An optional @plat_static_func may be provided to calculate the
> > > + * static power consumed by these cpus.  If the platform's static
> > > + * power consumption is unknown or negligible, make it NULL.
> > > + *
> > > + * Return: a valid struct thermal_cooling_device pointer on success,
> > > + * on failure, it returns a corresponding ERR_PTR().
> > > + */
> > > +struct thermal_cooling_device *
> > > +cpufreq_power_cooling_register(const struct cpumask *clip_cpus, u32 capa=
> > citance,
> > > +			get_static_t plat_static_func)
> > > +{
> > > +	return __cpufreq_cooling_register(NULL, clip_cpus, capacitance,
> > > +				plat_static_func);
> > > +}
> > > +EXPORT_SYMBOL(cpufreq_power_cooling_register);
> > > +
> > > +/**
> > > + * of_cpufreq_power_cooling_register() - create cpufreq cooling device w=
> > ith power extensions
> > > + * @np:	a valid struct device_node to the cooling device device tree node
> > > + * @clip_cpus:	cpumask of cpus where the frequency constraints will happ=
> > en
> > > + * @capacitance:	dynamic power coefficient for these cpus
> > > + * @plat_static_func:	function to calculate the static power consumed by=
> >  these
> > > + *			cpus (optional)
> > > + *
> > > + * This interface function registers the cpufreq cooling device with
> > > + * the name "thermal-cpufreq-%x".  This api can support multiple
> > > + * instances of cpufreq cooling devices.  Using this API, the cpufreq
> > > + * cooling device will be linked to the device tree node provided.
> > > + * Using this function, the cooling device will implement the power
> > > + * extensions by using a simple cpu power model.  The cpus must have
> > > + * registered their OPPs using the OPP library.
> > > + *
> > > + * An optional @plat_static_func may be provided to calculate the
> > > + * static power consumed by these cpus.  If the platform's static
> > > + * power consumption is unknown or negligible, make it NULL.
> > > + *
> > > + * Return: a valid struct thermal_cooling_device pointer on success,
> > > + * on failure, it returns a corresponding ERR_PTR().
> > > + */
> > > +struct thermal_cooling_device *
> > > +of_cpufreq_power_cooling_register(struct device_node *np,
> > > +			const struct cpumask *clip_cpus, u32 capacitance,
> > > +			get_static_t plat_static_func)
> > > +{
> > > +	if (!np)
> > > +		return ERR_PTR(-EINVAL);
> > > +
> > > +	return __cpufreq_cooling_register(np, clip_cpus, capacitance,
> > > +				plat_static_func);
> > > +}
> > > +EXPORT_SYMBOL(of_cpufreq_power_cooling_register);
> > > +
> > > +/**
> > >   * cpufreq_cooling_unregister - function to remove cpufreq cooling devic=
> > e.
> > >   * @cdev: thermal cooling device pointer.
> > >   *
> > > diff --git a/include/linux/cpu_cooling.h b/include/linux/cpu_cooling.h
> > > index c303d383def1..5c4f4567acf0 100644
> > > --- a/include/linux/cpu_cooling.h
> > > +++ b/include/linux/cpu_cooling.h
> > > @@ -28,6 +28,8 @@
> > >  #include <linux/thermal.h>
> > >  #include <linux/cpumask.h>
> > > =20
> > > +typedef u32 (*get_static_t)(cpumask_t *cpumask, unsigned long voltage);
> > > +
> > >  #ifdef CONFIG_CPU_THERMAL
> > >  /**
> > >   * cpufreq_cooling_register - function to create cpufreq cooling device.
> > > @@ -37,14 +39,38 @@ struct thermal_cooling_device *
> > >  cpufreq_cooling_register(const struct cpumask *clip_cpus);
> > > =20
> > >  /**
> > > + * cpufreq_power_cooling_register() - create cpufreq cooling device with=
> >  power extensions
> > > + * @clip_cpus: cpumask of cpus where the frequency constraints will happ=
> > en
> > > + * @capacitance:	dynamic power coefficient for these cpus
> > > + * @plat_static_func:	function to calculate the static power consumed by=
> >  these
> > > + *			cpus (optional)
> > > + */
> > > +struct thermal_cooling_device *
> > > +cpufreq_power_cooling_register(const struct cpumask *clip_cpus,
> > > +			u32 capacitance, get_static_t plat_static_func);
> > > +
> > > +#ifdef CONFIG_THERMAL_OF
> > > +/**
> > >   * of_cpufreq_cooling_register - create cpufreq cooling device based on =
> > DT.
> > >   * @np: a valid struct device_node to the cooling device device tree nod=
> > e.
> > >   * @clip_cpus: cpumask of cpus where the frequency constraints will happ=
> > en
> > >   */
> > > -#ifdef CONFIG_THERMAL_OF
> > >  struct thermal_cooling_device *
> > >  of_cpufreq_cooling_register(struct device_node *np,
> > >  			    const struct cpumask *clip_cpus);
> > > +
> > > +/**
> > > + * of_cpufreq_power_cooling_register() - create cpufreq cooling device w=
> > ith power extensions
> > > + * @np:	a valid struct device_node to the cooling device device tree node
> > > + * @clip_cpus:	cpumask of cpus where the frequency constraints will happ=
> > en
> > > + * @capacitance:	dynamic power coefficient for these cpus
> > > + * @plat_static_func:	function to calculate the static power consumed by=
> >  these
> > > + *			cpus (optional)
> > > + */
> > 
> > I think we should avoid duplicating kernel doc entries.=20
> 
> I totally agree, but I was just trying to be consistent.
> cpufreq_cooling_register() and cpufreq_cooling_unregister() have
> kernel doc entries here and in cpu_cooling.c.  I'm happy to send a
> patch that removes the duplicated kernel doc for
> cpufreq_cooling_register() and friends in include/linux/cpu_cooling.h
> and drop the duplication from this patch as well.

I should have one removing them here:
https://git.kernel.org/cgit/linux/kernel/git/evalenti/linux.git/commit/?h=thermal-docbook&id=5ea03ddc0ba1a4cadcfdc8954f1ccce0013eddb3

I will post the series soon.

> 
> > > +struct thermal_cooling_device *
> > > +of_cpufreq_power_cooling_register(struct device_node *np,
> > > +				const struct cpumask *clip_cpus,
> > > +				u32 capacitance, get_static_t plat_static_func);
> > >  #else
> > >  static inline struct thermal_cooling_device *
> > >  of_cpufreq_cooling_register(struct device_node *np,
> > > @@ -52,6 +78,14 @@ of_cpufreq_cooling_register(struct device_node *np,
> > >  {
> > >  	return NULL;
> > >  }
> > > +
> > > +struct thermal_cooling_device *
> > > +of_cpufreq_power_cooling_register(struct device_node *np,
> > > +				const struct cpumask *clip_cpus,
> > > +				u32 capacitance, get_static_t plat_static_func)
> > 
> > This is expected to be static inline.
> 
> Yes, I'll change it.
> 
> Thanks for reviewing a patch with such a horrible encoding,

No issue, as you are fixing for next version :-).

> Javi

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 5/9] thermal: extend the cooling device API to include power information
  2015-01-05 15:37     ` Javi Merino
@ 2015-01-05 21:04       ` Eduardo Valentin
  2015-01-06 10:34         ` Javi Merino
  0 siblings, 1 reply; 48+ messages in thread
From: Eduardo Valentin @ 2015-01-05 21:04 UTC (permalink / raw)
  To: Javi Merino; +Cc: linux-pm, linux-kernel, Punit Agrawal, broonie, Zhang Rui

[-- Attachment #1: Type: text/plain, Size: 7652 bytes --]

Javi,

On Mon, Jan 05, 2015 at 03:37:10PM +0000, Javi Merino wrote:
> On Tue, Dec 23, 2014 at 03:14:11PM +0000, Eduardo Valentin wrote:
> > Hi Javi
> > 
> > On Fri, Dec 05, 2014 at 07:04:16PM +0000, Javi Merino wrote:
> > > Add three optional callbacks to the cooling device interface to allow
> > > them to express power.  In addition to the callbacks, add helpers to
> > > identify cooling devices that implement the power cooling device API.
> > > 
> > > Cc: Zhang Rui <rui.zhang@intel.com>
> > > Cc: Eduardo Valentin <edubezval@gmail.com>
> > > Signed-off-by: Javi Merino <javi.merino@arm.com>
> > > ---
> > >  Documentation/thermal/power_allocator.txt | 27 ++++++++++++++++++++++
> > >  drivers/thermal/thermal_core.c            | 38 +++++++++++++++++++++++++++++++
> > >  include/linux/thermal.h                   | 12 ++++++++++
> > >  3 files changed, 77 insertions(+)
> > >  create mode 100644 Documentation/thermal/power_allocator.txt
> > > 
> > > diff --git a/Documentation/thermal/power_allocator.txt b/Documentation/thermal/power_allocator.txt
> > > new file mode 100644
> > > index 000000000000..d3bb79050c27
> > > --- /dev/null
> > > +++ b/Documentation/thermal/power_allocator.txt
> > > @@ -0,0 +1,27 @@
> > > +Cooling device power API
> > > +========================
> > 
> > Readers of this file need extra context here, IMO.
> 
> Patch 7 adds text before and after this section that provides that
> context.
> 
> > > +
> > > +Cooling devices controlled by this governor must supply the additional
> > 
> > What governor? the files says power allocator, and the title says,
> > cooling device power API...
> 
> Correct, because that's added in the patch that introduces the power
> allocator governor.  Therefore, it's not a problem for the readers of
> this file but for the readers of the patches.  I can move this hunk to
> patch 7 and introduce all the documentation at once if you think
> that's clearer.
> 

Thinking of the atomicity of each patch/commit, I would prefer you to
move all documentation to a single patch then.

> > > +"power" API in their `cooling_device_ops`.  It consists on three ops:
> > > +
> > 
> > 
> > 
> > > +1. u32 get_actual_power(struct thermal_cooling_device *cdev);
> > > +@cdev: The `struct thermal_cooling_device` pointer
> > > +
> > > +`get_actual_power()` returns the power currently consumed by the
> > > +device in milliwatts.
> > > +
> > > +2. u32 state2power(struct thermal_cooling_device *cdev, unsigned long
> > > +        state);
> > > +@cdev: The `struct thermal_cooling_device` pointer
> > > +@state: A cooling device state
> > > +
> > > +Convert cooling device state @state into power consumption in
> > > +milliwatts.
> > > +
> > > +3. unsigned long power2state(struct thermal_cooling_device *cdev,
> > > +        u32 power);
> > > +@cdev: The `struct thermal_cooling_device` pointer
> > > +@power: power in milliwatts
> > > +
> > > +Calculate a cooling device state that would make the device consume at
> > > +most @power mW.
> > 
> > I believe it would be more helpful if you could provide extra context in
> > which the above functions are called, and for what.
> 
> Ok, will do.
> 

tks.

> > > diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
> > > index 9021cb72a13a..c490f262ea7f 100644
> > > --- a/drivers/thermal/thermal_core.c
> > > +++ b/drivers/thermal/thermal_core.c
> > > @@ -866,6 +866,44 @@ emul_temp_store(struct device *dev, struct device_attribute *attr,
> > >  static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store);
> > >  #endif/*CONFIG_THERMAL_EMULATION*/
> > >  
> > > +/**
> > > + * power_actor_get_max_power() - get the maximum power that a cdev can consume
> > > + * @cdev:	pointer to &thermal_cooling_device
> > > + *
> > > + * Calculate the maximum power consumption in milliwats that the
> > > + * cooling device can currently consume.  If @cdev doesn't support the
> > > + * power_actor API, this function returns 0.
> > > + */
> > > +u32 power_actor_get_max_power(struct thermal_cooling_device *cdev)
> > > +{
> > > +	if (!cdev_is_power_actor(cdev))
> > > +		return 0;
> > > +
> > > +	return cdev->ops->state2power(cdev, 0);
> > > +}
> > > +
> > > +/**
> > > + * power_actor_set_power() - limit the maximum power that a cooling device can consume
> > > + * @cdev:	pointer to &thermal_cooling_device
> > > + * @power:	the power in milliwatts
> > > + *
> > > + * Set the cooling device to consume at most @power milliwatts.
> > > + *
> > > + * Returns: 0 on success, -EINVAL if the cooling device does not
> > > + * implement the power actor API or -E* for other failures.
> > > + */
> > > +int power_actor_set_power(struct thermal_cooling_device *cdev, u32 power)
> > > +{
> > > +	unsigned long state;
> > > +
> > > +	if (!cdev_is_power_actor(cdev))
> > > +		return -EINVAL;
> > > +
> > > +	state = cdev->ops->power2state(cdev, power);
> > > +
> > > +	return cdev->ops->set_cur_state(cdev, state);
> > > +}
> > > +
> > >  static DEVICE_ATTR(type, 0444, type_show, NULL);
> > >  static DEVICE_ATTR(temp, 0444, temp_show, NULL);
> > >  static DEVICE_ATTR(mode, 0644, mode_show, mode_store);
> > > diff --git a/include/linux/thermal.h b/include/linux/thermal.h
> > > index 2c14ab1f5c0d..1155457caf52 100644
> > > --- a/include/linux/thermal.h
> > > +++ b/include/linux/thermal.h
> > > @@ -142,6 +142,9 @@ struct thermal_cooling_device_ops {
> > >  	int (*get_max_state) (struct thermal_cooling_device *, unsigned long *);
> > >  	int (*get_cur_state) (struct thermal_cooling_device *, unsigned long *);
> > >  	int (*set_cur_state) (struct thermal_cooling_device *, unsigned long);
> > > +	u32 (*get_actual_power) (struct thermal_cooling_device *);
> > > +	u32 (*state2power) (struct thermal_cooling_device *, unsigned long);
> > > +	unsigned long (*power2state) (struct thermal_cooling_device *, u32);
> > >  };
> > >  
> > >  struct thermal_cooling_device {
> > > @@ -322,6 +325,15 @@ void thermal_zone_of_sensor_unregister(struct device *dev,
> > >  }
> > >  
> > >  #endif
> > > +
> > > +static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev)
> > > +{
> > 
> > What would happen if one pass cdev == NULL?
> 
> Is it really worth checking it here instead of just making the caller
> pass a valid cdev?  There are a number of functions in the thermal
> framework that don't check for valid cdevs or thermal zone pointers
> and I don't see why this one is different.
> 


I don't know, maybe we should fix those misbehaving functions then? Can
you please point them out?

> > > +	return cdev->ops->get_actual_power && cdev->ops->state2power &&
> > > +		cdev->ops->power2state;
> > > +}
> > > +
> > > +u32 power_actor_get_max_power(struct thermal_cooling_device *);
> > > +int power_actor_set_power(struct thermal_cooling_device *, u32);
> > >  struct thermal_zone_device *thermal_zone_device_register(const char *, int, int,
> > >  		void *, struct thermal_zone_device_ops *,
> > >  		const struct thermal_zone_params *, int, int);
> > 
> > I am assuming the above two new functions are expected to be used also
> > outside thermal core, right? If yes, I'd suggest exporting them. 
> 
> I don't expect it for the time being.  Wouldn't it be preferable to
> export them when its needed instead?

I believe functions in this header are exported. For functions used
inside thermal core and code that goes builtin always, they go into
drivers/thermal/thermal_core.h, and are not exported.

> 
> Cheers,
> Javi

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 9/9] of: thermal: Introduce sustainable power for a thermal zone
  2015-01-02 15:53   ` Eduardo Valentin
@ 2015-01-06  9:42     ` Javi Merino
  2015-01-06 13:13       ` Eduardo Valentin
  0 siblings, 1 reply; 48+ messages in thread
From: Javi Merino @ 2015-01-06  9:42 UTC (permalink / raw)
  To: Eduardo Valentin
  Cc: linux-pm, linux-kernel, Punit Agrawal, broonie, Zhang Rui

On Fri, Jan 02, 2015 at 03:53:00PM +0000, Eduardo Valentin wrote:
> On Fri, Dec 05, 2014 at 07:04:20PM +0000, Javi Merino wrote:
> > From: Punit Agrawal <punit.agrawal@arm.com>
> > 
> > Introduce an optional property called, sustainable-power, which
> > represents the power (in mW) which the thermal zone can safely
> > dissipate.
> > 
> > If provided the property is parsed and associated with the thermal
> > zone via the thermal zone parameters.
> > 
> > Cc: Zhang Rui <rui.zhang@intel.com>
> > Cc: Eduardo Valentin <edubezval@gmail.com>
> > Signed-off-by: Punit Agrawal <punit.agrawal@arm.com>
> > ---
> >  Documentation/devicetree/bindings/thermal/thermal.txt | 4 ++++
> >  drivers/thermal/of-thermal.c                          | 4 ++++
> >  2 files changed, 8 insertions(+)
> > 
> > diff --git a/Documentation/devicetree/bindings/thermal/thermal.txt b/Documentation/devicetree/bindings/thermal/thermal.txt
> > index f5db6b72a36f..c6eb9a8d2aed 100644
> > --- a/Documentation/devicetree/bindings/thermal/thermal.txt
> > +++ b/Documentation/devicetree/bindings/thermal/thermal.txt
> > @@ -167,6 +167,10 @@ Optional property:
> >  			by means of sensor ID. Additional coefficients are
> >  			interpreted as constant offset.
> >  
> > +- sustainable-power:	An estimate of the sustainable power (in mW) that the
> > +  Type: unsigned	thermal zone can dissipate.
> > +  Size: one cell
> > +
> 
> Please, include examples of this property, as you mentioned in the
> governor documentation.

I'd rather put a pointer to the documentation instead of repeating the
same thing here.  What do you think?

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 5/9] thermal: extend the cooling device API to include power information
  2015-01-05 21:04       ` Eduardo Valentin
@ 2015-01-06 10:34         ` Javi Merino
  2015-01-06 13:08           ` Eduardo Valentin
  0 siblings, 1 reply; 48+ messages in thread
From: Javi Merino @ 2015-01-06 10:34 UTC (permalink / raw)
  To: Eduardo Valentin
  Cc: linux-pm, linux-kernel, Punit Agrawal, broonie, Zhang Rui

Hi Eduardo,

On Mon, Jan 05, 2015 at 09:04:09PM +0000, Eduardo Valentin wrote:
> On Mon, Jan 05, 2015 at 03:37:10PM +0000, Javi Merino wrote:
> > On Tue, Dec 23, 2014 at 03:14:11PM +0000, Eduardo Valentin wrote:
> > > Hi Javi
> > > 
> > > On Fri, Dec 05, 2014 at 07:04:16PM +0000, Javi Merino wrote:
> > > > Add three optional callbacks to the cooling device interface to allow
> > > > them to express power.  In addition to the callbacks, add helpers to
> > > > identify cooling devices that implement the power cooling device API.
> > > > 
> > > > Cc: Zhang Rui <rui.zhang@intel.com>
> > > > Cc: Eduardo Valentin <edubezval@gmail.com>
> > > > Signed-off-by: Javi Merino <javi.merino@arm.com>
> > > > ---
> > > >  Documentation/thermal/power_allocator.txt | 27 ++++++++++++++++++++++
> > > >  drivers/thermal/thermal_core.c            | 38 +++++++++++++++++++++++++++++++
> > > >  include/linux/thermal.h                   | 12 ++++++++++
> > > >  3 files changed, 77 insertions(+)
> > > >  create mode 100644 Documentation/thermal/power_allocator.txt
> > > > 
> > > > diff --git a/Documentation/thermal/power_allocator.txt b/Documentation/thermal/power_allocator.txt
> > > > new file mode 100644
> > > > index 000000000000..d3bb79050c27
> > > > --- /dev/null
> > > > +++ b/Documentation/thermal/power_allocator.txt
> > > > @@ -0,0 +1,27 @@
> > > > +Cooling device power API
> > > > +========================
> > > 
> > > Readers of this file need extra context here, IMO.
> > 
> > Patch 7 adds text before and after this section that provides that
> > context.
> > 
> > > > +
> > > > +Cooling devices controlled by this governor must supply the additional
> > > 
> > > What governor? the files says power allocator, and the title says,
> > > cooling device power API...
> > 
> > Correct, because that's added in the patch that introduces the power
> > allocator governor.  Therefore, it's not a problem for the readers of
> > this file but for the readers of the patches.  I can move this hunk to
> > patch 7 and introduce all the documentation at once if you think
> > that's clearer.
> > 
> 
> Thinking of the atomicity of each patch/commit, I would prefer you to
> move all documentation to a single patch then.

Ok, I'll move it to the patch that introduces the power allocator.

[...]
> > > > diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
> > > > index 9021cb72a13a..c490f262ea7f 100644
> > > > --- a/drivers/thermal/thermal_core.c
> > > > +++ b/drivers/thermal/thermal_core.c
> > > > @@ -866,6 +866,44 @@ emul_temp_store(struct device *dev, struct device_attribute *attr,
> > > >  static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store);
> > > >  #endif/*CONFIG_THERMAL_EMULATION*/
> > > >  
> > > > +/**
> > > > + * power_actor_get_max_power() - get the maximum power that a cdev can consume
> > > > + * @cdev:	pointer to &thermal_cooling_device
> > > > + *
> > > > + * Calculate the maximum power consumption in milliwats that the
> > > > + * cooling device can currently consume.  If @cdev doesn't support the
> > > > + * power_actor API, this function returns 0.
> > > > + */
> > > > +u32 power_actor_get_max_power(struct thermal_cooling_device *cdev)
> > > > +{
> > > > +	if (!cdev_is_power_actor(cdev))
> > > > +		return 0;
> > > > +
> > > > +	return cdev->ops->state2power(cdev, 0);
> > > > +}
> > > > +
> > > > +/**
> > > > + * power_actor_set_power() - limit the maximum power that a cooling device can consume
> > > > + * @cdev:	pointer to &thermal_cooling_device
> > > > + * @power:	the power in milliwatts
> > > > + *
> > > > + * Set the cooling device to consume at most @power milliwatts.
> > > > + *
> > > > + * Returns: 0 on success, -EINVAL if the cooling device does not
> > > > + * implement the power actor API or -E* for other failures.
> > > > + */
> > > > +int power_actor_set_power(struct thermal_cooling_device *cdev, u32 power)
> > > > +{
> > > > +	unsigned long state;
> > > > +
> > > > +	if (!cdev_is_power_actor(cdev))
> > > > +		return -EINVAL;
> > > > +
> > > > +	state = cdev->ops->power2state(cdev, power);
> > > > +
> > > > +	return cdev->ops->set_cur_state(cdev, state);
> > > > +}
> > > > +
> > > >  static DEVICE_ATTR(type, 0444, type_show, NULL);
> > > >  static DEVICE_ATTR(temp, 0444, temp_show, NULL);
> > > >  static DEVICE_ATTR(mode, 0644, mode_show, mode_store);
> > > > diff --git a/include/linux/thermal.h b/include/linux/thermal.h
> > > > index 2c14ab1f5c0d..1155457caf52 100644
> > > > --- a/include/linux/thermal.h
> > > > +++ b/include/linux/thermal.h
> > > > @@ -142,6 +142,9 @@ struct thermal_cooling_device_ops {
> > > >  	int (*get_max_state) (struct thermal_cooling_device *, unsigned long *);
> > > >  	int (*get_cur_state) (struct thermal_cooling_device *, unsigned long *);
> > > >  	int (*set_cur_state) (struct thermal_cooling_device *, unsigned long);
> > > > +	u32 (*get_actual_power) (struct thermal_cooling_device *);
> > > > +	u32 (*state2power) (struct thermal_cooling_device *, unsigned long);
> > > > +	unsigned long (*power2state) (struct thermal_cooling_device *, u32);
> > > >  };
> > > >  
> > > >  struct thermal_cooling_device {
> > > > @@ -322,6 +325,15 @@ void thermal_zone_of_sensor_unregister(struct device *dev,
> > > >  }
> > > >  
> > > >  #endif
> > > > +
> > > > +static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev)
> > > > +{
> > > 
> > > What would happen if one pass cdev == NULL?
> > 
> > Is it really worth checking it here instead of just making the caller
> > pass a valid cdev?  There are a number of functions in the thermal
> > framework that don't check for valid cdevs or thermal zone pointers
> > and I don't see why this one is different.
> 
> I don't know, maybe we should fix those misbehaving functions then? Can
> you please point them out?

This is not an exhaustive list but a quick grep only in thermal_core.c
yields:
  - get_tz_trend() doesn't check tz
  - get_thermal_instance() doesn't check tz or cdev
  - thermal_zone_device_update() doesn't check tz
  - thermal_zone_bind_cooling_device() doesn't check tz or cdev
  - thermal_zone_unbind_cooling_device() doesn't check tz or cdev
  - thermal_cdev_update() doesn't check cdev

> > > > +	return cdev->ops->get_actual_power && cdev->ops->state2power &&
> > > > +		cdev->ops->power2state;
> > > > +}
> > > > +
> > > > +u32 power_actor_get_max_power(struct thermal_cooling_device *);
> > > > +int power_actor_set_power(struct thermal_cooling_device *, u32);
> > > >  struct thermal_zone_device *thermal_zone_device_register(const char *, int, int,
> > > >  		void *, struct thermal_zone_device_ops *,
> > > >  		const struct thermal_zone_params *, int, int);
> > > 
> > > I am assuming the above two new functions are expected to be used also
> > > outside thermal core, right? If yes, I'd suggest exporting them. 
> > 
> > I don't expect it for the time being.  Wouldn't it be preferable to
> > export them when its needed instead?
> 
> I believe functions in this header are exported. For functions used
> inside thermal core and code that goes builtin always, they go into
> drivers/thermal/thermal_core.h, and are not exported.

Ok, I'll move them to drivers/thermal/thermal_core.h .

Cheers,
Javi

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 6/9] thermal: cpu_cooling: implement the power cooling device API
  2015-01-05 20:44                       ` Eduardo Valentin
@ 2015-01-06 11:01                         ` Javi Merino
  0 siblings, 0 replies; 48+ messages in thread
From: Javi Merino @ 2015-01-06 11:01 UTC (permalink / raw)
  To: Eduardo Valentin
  Cc: Viresh Kumar, Linux PM list, linux-kernel, Punit Agrawal,
	Mark Brown, Zhang Rui

Hi Eduardo,

On Mon, Jan 05, 2015 at 08:44:53PM +0000, Eduardo Valentin wrote:
> On Mon, Jan 05, 2015 at 04:53:40PM +0000, Javi Merino wrote:
> > On Fri, Jan 02, 2015 at 02:37:23PM +0000, Eduardo Valentin wrote:
> > > On Tue, Dec 09, 2014 at 11:00:43AM +0000, Javi Merino wrote:
> > > > On Tue, Dec 09, 2014 at 10:36:46AM +0000, Viresh Kumar wrote:
> > > > > On 9 December 2014 at 16:02, Javi Merino <javi.merino@arm.com> wrote:
> > > > diff --git a/Documentation/thermal/cpu-cooling-api.txt b/Documentation/th=
> > > ermal/cpu-cooling-api.txt
> > > > index fca24c931ec8..d438a900e374 100644
> > > > --- a/Documentation/thermal/cpu-cooling-api.txt
> > > > +++ b/Documentation/thermal/cpu-cooling-api.txt
[...]
> > > > +
> > > > +In this simplified representation our model becomes:
> > > > +
> > > > +Pdyn =3D Kd * Voltage^2 * Frequency * Utilisation
> > > > +
> > > > +Where Kd (capacitance) represents an indicative running time dynamic
> > > > +power coefficient in fundamental units of mW/MHz/uVolt^2
> > > > +
> > > 
> > > Do we have Kd (capacitance) reference values for ARM processors? Is it
> > > worth adding a few of them as an example table here?=20
> > 
> > The reference numbers correspond not only to a particular processor
> > (e.g. Cortex-A15) but to specific SoCs, as the implementation
> > technology plays a key role in this.  I'll see if we can share some
> > reference values for specific SoCs.
> 
> It does not need to be a extensive / exhaustive list. A small set of
> examples should do it.
> 
> > > Where does one find Kd values?
> > > 
> > > Just looking for pointers for platform driver writers (potential users
> > > of these APIs).
> > 
> > I understand your concern.  I'm afraid the best I can say here is "ask
> > the SoC vendor".
> 
> OK. Adding the above hint + a small set of examples should do it.

I'll do that then.

> > > > +2.2 Static power
> > > > +
> > > > +Static leakage power consumption depends on a number of factors.  For a
> > > > +given circuit implementation the primary factors are:
> > > > +
> > > > +- Time the circuit spends in each 'power state'
> > > > +- Temperature
> > > > +- Operating voltage
> > > > +- Process grade
> > > > +
> > > > +The time the circuit spends in each 'power state' for a given
> > > > +evaluation period at first order means OFF or ON.  However,
> > > > +'retention' states can also be supported that reduce power during
> > > > +inactive periods without loss of context.
> > > > +
> > > > +Note: The visibility of state entries to the OS can vary, according to
> > > > +platform specifics, and this can then impact the accuracy of a model
> > > > +based on OS state information alone.  It might be possible in some
> > > > +cases to extract more accurate information from system resources.
> > > > +
> > > > +The temperature, operating voltage and process 'grade' (slow to fast)
> > > > +of the circuit are all significant factors in static leakage power
> > > > +consumption.  All of these have complex relationships to static power.
> > > > +
> > > > +Circuit implementation specific factors include the chosen silicon
> > > > +process as well as the type, number and size of transistors in both
> > > > +the logic gates and any RAM elements included.
> > > > +
> > > > +The static power consumption modelling must take into account the
> > > > +power managed regions that are implemented.  Taking the example of an
> > > > +ARM processor cluster, the modelling would take into account whether
> > > > +each CPU can be powered OFF separately or if only a single power
> > > > +region is implemented for the complete cluster.
> > > > +
> > > > +In one view, there are others, a static power consumption model can
> > > > +then start from a set of reference values for each power managed
> > > > +region (e.g. CPU, Cluster/L2) in each state (e.g. ON, OFF) at an
> > > > +arbitrary process grade, voltage and temperature point.  These values
> > > > +are then scaled for all of the following: the time in each state, the
> > > > +process grade, the current temperature and the operating voltage.
> > > > +However, since both implementation specific and complex relationships
> > > > +dominate the estimate, the appropriate interface to the model from the
> > > > +cpu cooling device is to provide a function callback that calculates
> > > > +the static power in this platform.  When registering the cpu cooling
> > > > +device pass a function pointer that follows the `get_static_t`
> > > > +prototype:
> > > > +
> > > > +    u32 plat_get_static(cpumask_t *cpumask, unsigned long voltage);
> > > > +
> > > > +with `cpumask` a cpumask of the cpus involved in the calculation and
> > > > +`voltage` the voltage at which they are operating.
> > > > +
> > > 
> > > What is the expected behavior of 'plat_get_static' if a wrong parameter
> > > is passed? Say, a cpumask that is invalid or a unsupported voltage?
> > > Shall it return 0? Does 0 means error?
> > 
> > I guess returning 0 and pr_warn() would be the best approach.  There's
> > no point in propagating an error since the upper layers can't really
> > do anything about it (other than maybe the governor ignoring this
> > cooling device?).
> > 
> 
> Yeah, governors may ignore them. IMO, that is the best expected
> behavior, instead of using wrong values silently.

Ok, I'll propagate the error then so that governors know about it and
can ignore it.

> > I'll clarify it in the documentation.
> 
> cool.
> 
> > 
> > > Besides, how is the platform code supposed to return the estimate, given
> > > it depends on time spent in state, and we are not passing any info about
> > > time here?
> > 
> > Ok, I'll look into passing the time here.
> > 
> 
> nice!
> 
> > > Same question applies to temperature.
> > 
> > The problem here is that the cpu cooling device does not know the
> > temperature of the processor.  It may or may not be the temperature of
> > the thermal zone.  The platform code is the best place to determine
> > the thermal zone whose sensor is closer to the processor and get its
> > temperature.
> > 
> > Alternatively, the thermal zone for the sensor that is closer to the
> > cpu could be passed when the cpu cooling device is registered and that
> > could be used to pass the cpu's temperature to the plat_get_static()
> > function.  Do you prefer this approach?
> 
> I believe the former is better. You may leave to platform layer to
> request temperature from appropriate thermal zone (s) and then deriving the
> correct extrapolated temperature. 
> 
> However, the way the document text is now may confuse readers. We should
> mention in the text how to get temperature, given that it is not passed
> by the API.

Ok, I'll clarify the documentation and include part of what I said up
there.

> > >                                                      For voltage, we are
> > > passing as parameter. For process grade, well, platform code is probably
> > > best point to determine it, so, no need.
> > > 
> > > > +If `plat_static_func` is NULL, static power is considered to be
> > > > +negligible for this platform and only dynamic power is considered.
> > > > +
> > > > +The platform specific callback can then use any combination of tables
> > > > +and/or equations to permute the estimated value.  Process grade
> > > > +information is not passed to the model since access to such data, from
> > > > +on-chip measurement capability or manufacture time data, is platform
> > > > +specific.
> > > > +
> > > 
> > > agreed
> > > 
> > > > +Note: the significance of static power for CPUs in comparison to
> > > > +dynamic power is highly dependent on implementation.  Given the
> > > > +potential complexity in implementation, the importance and accuracy of
> > > > +its inclusion when using cpu cooling devices should be assessed on a
> > > > +case by cases basis.
> > > > +
> > > > diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
> > > > index ad09e51ffae4..959a103d18ba 100644
> > > > --- a/drivers/thermal/cpu_cooling.c
> > > > +++ b/drivers/thermal/cpu_cooling.c
> > > > @@ -24,11 +24,25 @@
> > > >  #include <linux/thermal.h>
> > > >  #include <linux/cpufreq.h>
> > > >  #include <linux/err.h>
> > > > +#include <linux/pm_opp.h>
> > > >  #include <linux/slab.h>
> > > >  #include <linux/cpu.h>
> > > >  #include <linux/cpu_cooling.h>
> > > > =20
> > > >  /**
> > > > + * struct power_table - frequency to power conversion
> > > > + * @frequency:	frequency in KHz
> > > > + * @power:	power in mW
> > > > + *
> > > > + * This structure is built when the cooling device registers and helps
> > > > + * in translating frequency to power and viceversa.
> > > > + */
> > > > +struct power_table {
> > > > +	u32 frequency;
> > > > +	u32 power;
> > > > +};
> > > > +
> > > > +/**
> > > >   * struct cpufreq_cooling_device - data for cooling device with cpufreq
> > > >   * @id: unique integer value corresponding to each cpufreq_cooling_device
> > > >   *	registered.
> > > > @@ -39,6 +53,15 @@
> > > >   * @cpufreq_val: integer value representing the absolute value of the cl=
> > > ipped
> > > >   *	frequency.
> > > >   * @allowed_cpus: all the cpus involved for this cpufreq_cooling_device.
> > > > + * @last_load: load measured by the latest call to cpufreq_get_actual_po=
> > > wer()
> > > > + * @time_in_idle: previous reading of the absolute time that this cpu wa=
> > > s idle
> > > > + * @time_in_idle_timestamp: wall time of the last invocation of
> > > > + *	get_cpu_idle_time_us()
> > > > + * @dyn_power_table: array of struct power_table for frequency to power
> > > > + *	conversion
> > > 
> > > 
> > > Is this ordered somehow? Is it worth mentioning?
> > 
> > It's in ascending ordered.  It's documented in
> > build_dyn_power_table().
> > 
> 
> I see.. but the field here needs to be documented too, don't you
> agree? I would mention here also that this field is expected to be
> ordered. Just for the sake of having a good kernel doc entry.

Fair enough, I'll include it here as well.

> > > > + * @dyn_power_table_entries: number of entries in the @dyn_power_table a=
> > > rray
> > > > + * @cpu_dev: the first cpu_device from @allowed_cpus that has OPPs regis=
> > > tered
> > > > + * @plat_get_static_power: callback to calculate the static power
> > > >   *
> > > >   * This structure is required for keeping information of each
> > > >   * cpufreq_cooling_device registered. In order to prevent corruption of =
> > > this a
> > > > @@ -51,6 +74,13 @@ struct cpufreq_cooling_device {
> > > >  	unsigned int cpufreq_val;
> > > >  	struct cpumask allowed_cpus;
> > > >  	struct list_head node;
> > > > +	u32 last_load;
> > > > +	u64 time_in_idle[NR_CPUS];
> > > > +	u64 time_in_idle_timestamp[NR_CPUS];
> > > > +	struct power_table *dyn_power_table;
> > > > +	int dyn_power_table_entries;
> > > > +	struct device *cpu_dev;
> > > > +	get_static_t plat_get_static_power;
> > > >  };
> > > >  static DEFINE_IDR(cpufreq_idr);
> > > >  static DEFINE_MUTEX(cooling_cpufreq_lock);
> > > > @@ -338,6 +368,204 @@ static int cpufreq_thermal_notifier(struct notifier=
> > > _block *nb,
> > > >  	return 0;
> > > >  }
> > > > =20
> > > > +/**
> > > > + * build_dyn_power_table() - create a dynamic power to frequency table
> > > > + * @cpufreq_device:	the cpufreq cooling device in which to store the tab=
> > > le
> > > > + * @capacitance: dynamic power coefficient for these cpus
> > > > + *
> > > > + * Build a dynamic power to frequency table for this cpu and store it
> > > > + * in @cpufreq_device.  This table will be used in cpu_power_to_freq() a=
> > > nd
> > > > + * cpu_freq_to_power() to convert between power and frequency
> > > > + * efficiently.  Power is stored in mW, frequency in KHz.  The
> > > > + * resulting table is in ascending order.
> > > 
> > > by which parameter? Do we assume a increasing convex relation between
> > > power and frequency?
> > 
> > By both parameters.  If frequency increases, power increases.  There's
> > no point in building a system that for lower frequencies you get
> > higher power consumption, right?  It's the worst of both worlds.
> 
> OK. Agreed, let's not make it overcomplicated.

Good

> > > > + *
> > > > + * Return: 0 on success, -E* on error.
> > > > + */
> > > > +static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_=
> > > device,
> > > > +				u32 capacitance)
> > > > +{
> > > > +	struct power_table *power_table;
> > > > +	struct dev_pm_opp *opp;
> > > > +	struct device *dev =3D NULL;
> > > > +	int num_opps =3D 0, cpu, i, ret =3D 0;
> > > > +	unsigned long freq;
> > > > +
> > > > +	rcu_read_lock();
> > > > +
> > > > +	for_each_cpu(cpu, &cpufreq_device->allowed_cpus) {
> > > > +		dev =3D get_cpu_device(cpu);
> > > > +		if (!dev) {
> > > > +			dev_warn(&cpufreq_device->cool_dev->device,
> > > > +				"No cpu device for cpu %d\n", cpu);
> > > > +			continue;
> > > > +		}
> > > > +
> > > > +		num_opps =3D dev_pm_opp_get_opp_count(dev);
> > > > +		if (num_opps > 0) {
> > > > +			break;
> > > > +		} else if (num_opps < 0) {
> > > > +			ret =3D num_opps;
> > > > +			goto unlock;
> > > > +		}
> > > > +	}
> > > > +
> > > > +	if (num_opps =3D=3D 0) {
> > > > +		ret =3D -EINVAL;
> > > > +		goto unlock;
> > > > +	}
> > > > +
> > > > +	power_table =3D devm_kcalloc(&cpufreq_device->cool_dev->device, num_opp=
> > > s,
> > > > +				sizeof(*power_table), GFP_KERNEL);
> > > > +
> > > > +	for (freq =3D 0, i =3D 0;
> > > > +	     opp =3D dev_pm_opp_find_freq_ceil(dev, &freq), !IS_ERR(opp);
> > > > +	     freq++, i++) {
> > > > +		u32 freq_mhz, voltage_mv;
> > > > +		u64 power;
> > > > +
> > > > +		freq_mhz =3D freq / 1000000;
> > > > +		voltage_mv =3D dev_pm_opp_get_voltage(opp) / 1000;
> > > > +
> > > > +		/*
> > > > +		 * Do the multiplication with MHz and millivolt so as
> > > > +		 * to not overflow.
> > > > +		 */
> > > > +		power =3D (u64)capacitance * freq_mhz * voltage_mv * voltage_mv;
> > > > +		do_div(power, 1000000000);
> > > > +
> > > > +		/* frequency is stored in power_table in KHz */
> > > > +		power_table[i].frequency =3D freq / 1000;
> > > 
> > > Why do we have a comment about freq unit but no comment about power unit? :=
> > > -)
> > 
> > It's in the documentation of the function.  I can repeat it here if
> > you want.
> 
> Well, I would say, you either comment both here, or remove the above.

I'll repeat it here then.

> > > > +}
> > > > +
> > > > +/**
> > > > + * get_dynamic_power() - calculate the dynamic power
> > > > + * @cpufreq_device:	&cpufreq_cooling_device for this cdev
> > > > + * @freq:	current frequency
> > > > + *
> > > 
> > > No description?
> > 
> > Well, the short description in the first line reads "calculate the
> > dynamic power" and the return value is "the dynamic power consumed by
> > the cpus described by @cpufreq_device".  There's really nothing more
> > that can be said about this function.
> 
> 
> Yeah, but kerneldoc still complains about entries without descriptions
> (and without Return: entries too, or missing parameter descriptions).
> So, you should describe the entry properly, with all required fields.

Ok, ok, I'll add a sentence as long description.

> > > > + * Return: the dynamic power consumed by the cpus described by
> > > > + * @cpufreq_device.
> > > > + */
> > > > +static u32 get_dynamic_power(struct cpufreq_cooling_device *cpufreq_devi=
> > > ce,
> > > > +			unsigned long freq)
> > > > +{
> > > > +	u32 raw_cpu_power;
> > > > +
> > > > +	raw_cpu_power =3D cpu_freq_to_power(cpufreq_device, freq);
> > > > +	return (raw_cpu_power * cpufreq_device->last_load) / 100;
> > > > +}
> > > > +
> > > >  /* cpufreq cooling device callback functions are defined below */
> > > > =20
> > > >  /**
> > > > @@ -407,8 +635,106 @@ static int cpufreq_set_cur_state(struct thermal_coo=
> > > ling_device *cdev,
> > > >  	return cpufreq_apply_cooling(cpufreq_device, state);
> > > >  }
> > > > =20
> > > > +/**
> > > > + * cpufreq_get_actual_power() - get the current power
> > > > + * @cdev:	&thermal_cooling_device pointer
> > > > + *
> > > 
> > > ditto.
> > > 
> > > those should generate kerneldoc warns. Can you please run kerneldoc
> > > script in your patch? make sure it does not add warns / errors.
> > 
> > It doesn't because the description is "Return the current power
> > consumption of the cpus in milliwatts."  Again, I don't see what else
> > can be said about these functions.
> 
> 
> I see, then you must include a 'Return:' section for this kerneldoc
> entry.
> 
> > 
> > > > + * Return the current power consumption of the cpus in milliwatts.
> > > > + */
> > > > +static u32 cpufreq_get_actual_power(struct thermal_cooling_device *cdev)
> > > > +{
> > > > +	unsigned long freq;
> > > > +	int cpu;
> > > > +	u32 static_power, dynamic_power, total_load =3D 0;
> > > > +	struct cpufreq_cooling_device *cpufreq_device =3D cdev->devdata;
> > > > +
> > > > +	freq =3D cpufreq_quick_get(cpumask_any(&cpufreq_device->allowed_cpus));
> > > > +
> > > > +	for_each_cpu(cpu, &cpufreq_device->allowed_cpus) {
> > > > +		u32 load;
> > > > +
> > > > +		if (cpu_online(cpu))
> > > > +			load =3D get_load(cpufreq_device, cpu);
> > > > +		else
> > > > +			load =3D 0;
> > > > +
> > > > +		total_load +=3D load;
> > > > +	}
> > > > +
> > > > +	cpufreq_device->last_load =3D total_load;
> > > > +
> > > > +	static_power =3D get_static_power(cpufreq_device, freq);
> > > > +	dynamic_power =3D get_dynamic_power(cpufreq_device, freq);
> > > > +
> > > > +	return static_power + dynamic_power;
> > > > +}
> > > > +
> > > > +/**
> > > > + * cpufreq_state2power() - convert a cpu cdev state to power consumed
> > > > + * @cdev:	&thermal_cooling_device pointer
> > > > + * @state:	cooling device state to be converted
> > > > + *
> > > > + * Convert cooling device state @state into power consumption in milliwa=
> > > tts.
> > > 
> > > Considering 100% of utilization, right?
> > > 
> > > 
> > > Return: ?
> > 
> > Ok, I'll add:
> > 
> > Return: the power consumption.
> 
> 
> Is there any fail case? 

There will be now that I'm going to return 0 or error on failure and
the power will be returned in a parameter.

> > > > + */
> > > > +static u32 cpufreq_state2power(struct thermal_cooling_device *cdev,
> > > > +			unsigned long state)
> > > > +{
> > > > +	unsigned int freq, num_cpus;
> > > > +	cpumask_t cpumask;
> > > > +	u32 static_power, dynamic_power;
> > > > +	struct cpufreq_cooling_device *cpufreq_device =3D cdev->devdata;
> > > > +
> > > > +	cpumask_and(&cpumask, &cpufreq_device->allowed_cpus, cpu_online_mask);
> > > > +	num_cpus =3D cpumask_weight(&cpumask);
> > > > +
> > > > +	freq =3D get_cpu_frequency(cpumask_any(&cpumask), state);
> > > > +	if (!freq)
> > > > +		return 0;
> 
> Looks like 0 means 0 mW and error situation, this needs to go into
> kernel doc.

Similar to above.

> > > > +
> > > > +	static_power =3D get_static_power(cpufreq_device, freq);
> > > > +	dynamic_power =3D cpu_freq_to_power(cpufreq_device, freq) * num_cpus;
> > > > +
> > > > +	return static_power + dynamic_power;
> > > > +}
> > > > +
> > > > +/**
> > > > + * cpufreq_power2state() - convert power to a cooling device state
> > > > + * @cdev:	&thermal_cooling_device pointer
> > > > + * @power:	power in milliwatts to be converted
> > > > + *
> > > > + * Calculate a cooling device state for the cpus described by @cdev
> > > > + * that would allow them to consume at most @power mW.
> > > 
> > > Return: ?=20
> > 
> > I'll add:
> > 
> > Return: the cooling device state
> 
> Fail case?

Same as above.

> > > > + */
> > > > +static unsigned long cpufreq_power2state(struct thermal_cooling_device *=
> > > cdev,
> > > > +					u32 power)
> > > > +{
> > > > +	unsigned int cpu, cur_freq, target_freq;
> > > > +	s32 dyn_power;
> > > > +	u32 last_load, normalised_power;
> > > > +	unsigned long cdev_state;
> > > > +	struct cpufreq_cooling_device *cpufreq_device =3D cdev->devdata;
> > > > +
> > > > +	cpu =3D cpumask_any_and(&cpufreq_device->allowed_cpus, cpu_online_mask);
> > > > +
> > > > +	cur_freq =3D cpufreq_quick_get(cpu);
> > > > +	dyn_power =3D power - get_static_power(cpufreq_device, cur_freq);
> > > > +	dyn_power =3D dyn_power > 0 ? dyn_power : 0;
> > > > +	last_load =3D cpufreq_device->last_load ?: 1;
> > > > +	normalised_power =3D (dyn_power * 100) / last_load;
> > > > +	target_freq =3D cpu_power_to_freq(cpufreq_device, normalised_power);
> > > > +
> > > 
> > > 
> > > I got confused with the description vs. the implementation here.
> > > Description says, a calculation from cooling device state to power. But
> > > calling this function twice for the same power value, in different
> > > moments, with difference cpu loads, (may) return different states
> > > between calls.. Can you please improve description?
> > 
> > Sure, I'll update the documentation.
> > 
> > > > +	cdev_state =3D cpufreq_cooling_get_level(cpu, target_freq);
> > > > +	if (cdev_state =3D=3D THERMAL_CSTATE_INVALID) {
> > > > +		pr_err_ratelimited("Failed to convert %dKHz for cpu %d into a cdev sta=
> > > te\n",
> > > > +				target_freq, cpu);
> > > > +		return 0;
> > > 
> > > How about passing state as parameter and allowing the API to return an
> > > error code?
> > 
> > You are right, it makes the cpufreq cooling device API more
> > consistent.  I'll make cpufreq_state2power() and cpufreq_power2state()
> > return 0 or error code and pass the power/state in a parameter.
> > 
> 
> good.
> 

[...]
> > > > diff --git a/include/linux/cpu_cooling.h b/include/linux/cpu_cooling.h
> > > > index c303d383def1..5c4f4567acf0 100644
> > > > --- a/include/linux/cpu_cooling.h
> > > > +++ b/include/linux/cpu_cooling.h
> > > > @@ -28,6 +28,8 @@
> > > >  #include <linux/thermal.h>
> > > >  #include <linux/cpumask.h>
> > > > =20
> > > > +typedef u32 (*get_static_t)(cpumask_t *cpumask, unsigned long voltage);
> > > > +
> > > >  #ifdef CONFIG_CPU_THERMAL
> > > >  /**
> > > >   * cpufreq_cooling_register - function to create cpufreq cooling device.
> > > > @@ -37,14 +39,38 @@ struct thermal_cooling_device *
> > > >  cpufreq_cooling_register(const struct cpumask *clip_cpus);
> > > > =20
> > > >  /**
> > > > + * cpufreq_power_cooling_register() - create cpufreq cooling device with=
> > >  power extensions
> > > > + * @clip_cpus: cpumask of cpus where the frequency constraints will happ=
> > > en
> > > > + * @capacitance:	dynamic power coefficient for these cpus
> > > > + * @plat_static_func:	function to calculate the static power consumed by=
> > >  these
> > > > + *			cpus (optional)
> > > > + */
> > > > +struct thermal_cooling_device *
> > > > +cpufreq_power_cooling_register(const struct cpumask *clip_cpus,
> > > > +			u32 capacitance, get_static_t plat_static_func);
> > > > +
> > > > +#ifdef CONFIG_THERMAL_OF
> > > > +/**
> > > >   * of_cpufreq_cooling_register - create cpufreq cooling device based on =
> > > DT.
> > > >   * @np: a valid struct device_node to the cooling device device tree nod=
> > > e.
> > > >   * @clip_cpus: cpumask of cpus where the frequency constraints will happ=
> > > en
> > > >   */
> > > > -#ifdef CONFIG_THERMAL_OF
> > > >  struct thermal_cooling_device *
> > > >  of_cpufreq_cooling_register(struct device_node *np,
> > > >  			    const struct cpumask *clip_cpus);
> > > > +
> > > > +/**
> > > > + * of_cpufreq_power_cooling_register() - create cpufreq cooling device w=
> > > ith power extensions
> > > > + * @np:	a valid struct device_node to the cooling device device tree node
> > > > + * @clip_cpus:	cpumask of cpus where the frequency constraints will happ=
> > > en
> > > > + * @capacitance:	dynamic power coefficient for these cpus
> > > > + * @plat_static_func:	function to calculate the static power consumed by=
> > >  these
> > > > + *			cpus (optional)
> > > > + */
> > > 
> > > I think we should avoid duplicating kernel doc entries.=20
> > 
> > I totally agree, but I was just trying to be consistent.
> > cpufreq_cooling_register() and cpufreq_cooling_unregister() have
> > kernel doc entries here and in cpu_cooling.c.  I'm happy to send a
> > patch that removes the duplicated kernel doc for
> > cpufreq_cooling_register() and friends in include/linux/cpu_cooling.h
> > and drop the duplication from this patch as well.
> 
> I should have one removing them here:
> https://git.kernel.org/cgit/linux/kernel/git/evalenti/linux.git/commit/?h=thermal-docbook&id=5ea03ddc0ba1a4cadcfdc8954f1ccce0013eddb3
> 
> I will post the series soon.

Great, then I'll remove the redundant entries here in my patch.

Cheers,
Javi

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 5/9] thermal: extend the cooling device API to include power information
  2015-01-06 10:34         ` Javi Merino
@ 2015-01-06 13:08           ` Eduardo Valentin
  0 siblings, 0 replies; 48+ messages in thread
From: Eduardo Valentin @ 2015-01-06 13:08 UTC (permalink / raw)
  To: Javi Merino; +Cc: linux-pm, linux-kernel, Punit Agrawal, broonie, Zhang Rui

[-- Attachment #1: Type: text/plain, Size: 8027 bytes --]

On Tue, Jan 06, 2015 at 10:34:31AM +0000, Javi Merino wrote:
> Hi Eduardo,
> 
> On Mon, Jan 05, 2015 at 09:04:09PM +0000, Eduardo Valentin wrote:
> > On Mon, Jan 05, 2015 at 03:37:10PM +0000, Javi Merino wrote:
> > > On Tue, Dec 23, 2014 at 03:14:11PM +0000, Eduardo Valentin wrote:
> > > > Hi Javi
> > > > 
> > > > On Fri, Dec 05, 2014 at 07:04:16PM +0000, Javi Merino wrote:
> > > > > Add three optional callbacks to the cooling device interface to allow
> > > > > them to express power.  In addition to the callbacks, add helpers to
> > > > > identify cooling devices that implement the power cooling device API.
> > > > > 
> > > > > Cc: Zhang Rui <rui.zhang@intel.com>
> > > > > Cc: Eduardo Valentin <edubezval@gmail.com>
> > > > > Signed-off-by: Javi Merino <javi.merino@arm.com>
> > > > > ---
> > > > >  Documentation/thermal/power_allocator.txt | 27 ++++++++++++++++++++++
> > > > >  drivers/thermal/thermal_core.c            | 38 +++++++++++++++++++++++++++++++
> > > > >  include/linux/thermal.h                   | 12 ++++++++++
> > > > >  3 files changed, 77 insertions(+)
> > > > >  create mode 100644 Documentation/thermal/power_allocator.txt
> > > > > 
> > > > > diff --git a/Documentation/thermal/power_allocator.txt b/Documentation/thermal/power_allocator.txt
> > > > > new file mode 100644
> > > > > index 000000000000..d3bb79050c27
> > > > > --- /dev/null
> > > > > +++ b/Documentation/thermal/power_allocator.txt
> > > > > @@ -0,0 +1,27 @@
> > > > > +Cooling device power API
> > > > > +========================
> > > > 
> > > > Readers of this file need extra context here, IMO.
> > > 
> > > Patch 7 adds text before and after this section that provides that
> > > context.
> > > 
> > > > > +
> > > > > +Cooling devices controlled by this governor must supply the additional
> > > > 
> > > > What governor? the files says power allocator, and the title says,
> > > > cooling device power API...
> > > 
> > > Correct, because that's added in the patch that introduces the power
> > > allocator governor.  Therefore, it's not a problem for the readers of
> > > this file but for the readers of the patches.  I can move this hunk to
> > > patch 7 and introduce all the documentation at once if you think
> > > that's clearer.
> > > 
> > 
> > Thinking of the atomicity of each patch/commit, I would prefer you to
> > move all documentation to a single patch then.
> 
> Ok, I'll move it to the patch that introduces the power allocator.
> 
> [...]
> > > > > diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
> > > > > index 9021cb72a13a..c490f262ea7f 100644
> > > > > --- a/drivers/thermal/thermal_core.c
> > > > > +++ b/drivers/thermal/thermal_core.c
> > > > > @@ -866,6 +866,44 @@ emul_temp_store(struct device *dev, struct device_attribute *attr,
> > > > >  static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store);
> > > > >  #endif/*CONFIG_THERMAL_EMULATION*/
> > > > >  
> > > > > +/**
> > > > > + * power_actor_get_max_power() - get the maximum power that a cdev can consume
> > > > > + * @cdev:	pointer to &thermal_cooling_device
> > > > > + *
> > > > > + * Calculate the maximum power consumption in milliwats that the
> > > > > + * cooling device can currently consume.  If @cdev doesn't support the
> > > > > + * power_actor API, this function returns 0.
> > > > > + */
> > > > > +u32 power_actor_get_max_power(struct thermal_cooling_device *cdev)
> > > > > +{
> > > > > +	if (!cdev_is_power_actor(cdev))
> > > > > +		return 0;
> > > > > +
> > > > > +	return cdev->ops->state2power(cdev, 0);
> > > > > +}
> > > > > +
> > > > > +/**
> > > > > + * power_actor_set_power() - limit the maximum power that a cooling device can consume
> > > > > + * @cdev:	pointer to &thermal_cooling_device
> > > > > + * @power:	the power in milliwatts
> > > > > + *
> > > > > + * Set the cooling device to consume at most @power milliwatts.
> > > > > + *
> > > > > + * Returns: 0 on success, -EINVAL if the cooling device does not
> > > > > + * implement the power actor API or -E* for other failures.
> > > > > + */
> > > > > +int power_actor_set_power(struct thermal_cooling_device *cdev, u32 power)
> > > > > +{
> > > > > +	unsigned long state;
> > > > > +
> > > > > +	if (!cdev_is_power_actor(cdev))
> > > > > +		return -EINVAL;
> > > > > +
> > > > > +	state = cdev->ops->power2state(cdev, power);
> > > > > +
> > > > > +	return cdev->ops->set_cur_state(cdev, state);
> > > > > +}
> > > > > +
> > > > >  static DEVICE_ATTR(type, 0444, type_show, NULL);
> > > > >  static DEVICE_ATTR(temp, 0444, temp_show, NULL);
> > > > >  static DEVICE_ATTR(mode, 0644, mode_show, mode_store);
> > > > > diff --git a/include/linux/thermal.h b/include/linux/thermal.h
> > > > > index 2c14ab1f5c0d..1155457caf52 100644
> > > > > --- a/include/linux/thermal.h
> > > > > +++ b/include/linux/thermal.h
> > > > > @@ -142,6 +142,9 @@ struct thermal_cooling_device_ops {
> > > > >  	int (*get_max_state) (struct thermal_cooling_device *, unsigned long *);
> > > > >  	int (*get_cur_state) (struct thermal_cooling_device *, unsigned long *);
> > > > >  	int (*set_cur_state) (struct thermal_cooling_device *, unsigned long);
> > > > > +	u32 (*get_actual_power) (struct thermal_cooling_device *);
> > > > > +	u32 (*state2power) (struct thermal_cooling_device *, unsigned long);
> > > > > +	unsigned long (*power2state) (struct thermal_cooling_device *, u32);
> > > > >  };
> > > > >  
> > > > >  struct thermal_cooling_device {
> > > > > @@ -322,6 +325,15 @@ void thermal_zone_of_sensor_unregister(struct device *dev,
> > > > >  }
> > > > >  
> > > > >  #endif
> > > > > +
> > > > > +static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev)
> > > > > +{
> > > > 
> > > > What would happen if one pass cdev == NULL?
> > > 
> > > Is it really worth checking it here instead of just making the caller
> > > pass a valid cdev?  There are a number of functions in the thermal
> > > framework that don't check for valid cdevs or thermal zone pointers
> > > and I don't see why this one is different.
> > 
> > I don't know, maybe we should fix those misbehaving functions then? Can
> > you please point them out?
> 
> This is not an exhaustive list but a quick grep only in thermal_core.c
> yields:
>   - get_tz_trend() doesn't check tz
>   - get_thermal_instance() doesn't check tz or cdev
>   - thermal_zone_device_update() doesn't check tz
>   - thermal_zone_bind_cooling_device() doesn't check tz or cdev
>   - thermal_zone_unbind_cooling_device() doesn't check tz or cdev
>   - thermal_cdev_update() doesn't check cdev


Fair enough, I think your argument holds :-). We should keep the same
pattern. That means, for now we should make sure the callers pass
correct argument, as you suggested. You may keep the function the way it
is.

> 
> > > > > +	return cdev->ops->get_actual_power && cdev->ops->state2power &&
> > > > > +		cdev->ops->power2state;
> > > > > +}
> > > > > +
> > > > > +u32 power_actor_get_max_power(struct thermal_cooling_device *);
> > > > > +int power_actor_set_power(struct thermal_cooling_device *, u32);
> > > > >  struct thermal_zone_device *thermal_zone_device_register(const char *, int, int,
> > > > >  		void *, struct thermal_zone_device_ops *,
> > > > >  		const struct thermal_zone_params *, int, int);
> > > > 
> > > > I am assuming the above two new functions are expected to be used also
> > > > outside thermal core, right? If yes, I'd suggest exporting them. 
> > > 
> > > I don't expect it for the time being.  Wouldn't it be preferable to
> > > export them when its needed instead?
> > 
> > I believe functions in this header are exported. For functions used
> > inside thermal core and code that goes builtin always, they go into
> > drivers/thermal/thermal_core.h, and are not exported.
> 
> Ok, I'll move them to drivers/thermal/thermal_core.h .
> 

OK.

> Cheers,
> Javi

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 9/9] of: thermal: Introduce sustainable power for a thermal zone
  2015-01-06  9:42     ` Javi Merino
@ 2015-01-06 13:13       ` Eduardo Valentin
  2015-01-06 13:29         ` Javi Merino
  0 siblings, 1 reply; 48+ messages in thread
From: Eduardo Valentin @ 2015-01-06 13:13 UTC (permalink / raw)
  To: Javi Merino; +Cc: linux-pm, linux-kernel, Punit Agrawal, broonie, Zhang Rui

[-- Attachment #1: Type: text/plain, Size: 2117 bytes --]

On Tue, Jan 06, 2015 at 09:42:15AM +0000, Javi Merino wrote:
> On Fri, Jan 02, 2015 at 03:53:00PM +0000, Eduardo Valentin wrote:
> > On Fri, Dec 05, 2014 at 07:04:20PM +0000, Javi Merino wrote:
> > > From: Punit Agrawal <punit.agrawal@arm.com>
> > > 
> > > Introduce an optional property called, sustainable-power, which
> > > represents the power (in mW) which the thermal zone can safely
> > > dissipate.
> > > 
> > > If provided the property is parsed and associated with the thermal
> > > zone via the thermal zone parameters.
> > > 
> > > Cc: Zhang Rui <rui.zhang@intel.com>
> > > Cc: Eduardo Valentin <edubezval@gmail.com>
> > > Signed-off-by: Punit Agrawal <punit.agrawal@arm.com>
> > > ---
> > >  Documentation/devicetree/bindings/thermal/thermal.txt | 4 ++++
> > >  drivers/thermal/of-thermal.c                          | 4 ++++
> > >  2 files changed, 8 insertions(+)
> > > 
> > > diff --git a/Documentation/devicetree/bindings/thermal/thermal.txt b/Documentation/devicetree/bindings/thermal/thermal.txt
> > > index f5db6b72a36f..c6eb9a8d2aed 100644
> > > --- a/Documentation/devicetree/bindings/thermal/thermal.txt
> > > +++ b/Documentation/devicetree/bindings/thermal/thermal.txt
> > > @@ -167,6 +167,10 @@ Optional property:
> > >  			by means of sensor ID. Additional coefficients are
> > >  			interpreted as constant offset.
> > >  
> > > +- sustainable-power:	An estimate of the sustainable power (in mW) that the
> > > +  Type: unsigned	thermal zone can dissipate.
> > > +  Size: one cell
> > > +
> > 
> > Please, include examples of this property, as you mentioned in the
> > governor documentation.
> 
> I'd rather put a pointer to the documentation instead of repeating the
> same thing here.  What do you think?

The point is that device tree and Linux are supposed to be independent
entities. I would prefer if you could extend the explanation of what is
'sustainable power' in the above entry. On top of that, pick one of the
existing examples and extend it to include the 'sustainable-power' property,
with a comment explaining it, for instance.


[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 7/9] thermal: introduce the Power Allocator governor
  2015-01-02 15:46   ` Eduardo Valentin
@ 2015-01-06 13:23     ` Javi Merino
  2015-01-06 14:18       ` Eduardo Valentin
  0 siblings, 1 reply; 48+ messages in thread
From: Javi Merino @ 2015-01-06 13:23 UTC (permalink / raw)
  To: Eduardo Valentin
  Cc: linux-pm, linux-kernel, Punit Agrawal, broonie, Zhang Rui

Hi Eduardo,

On Fri, Jan 02, 2015 at 03:46:24PM +0000, Eduardo Valentin wrote:
> On Fri, Dec 05, 2014 at 07:04:18PM +0000, Javi Merino wrote:
> > The power allocator governor is a thermal governor that controls system
> > and device power allocation to control temperature.  Conceptually, the
> > implementation divides the sustainable power of a thermal zone among
> > all the heat sources in that zone.
> > 
> > This governor relies on "power actors", entities that represent heat
> > sources.  They can report current and maximum power consumption and
> > can set a given maximum power consumption, usually via a cooling
> > device.
> > 
> > The governor uses a Proportional Integral Derivative (PID) controller
> > driven by the temperature of the thermal zone.  The output of the
> > controller is a power budget that is then allocated to each power
> > actor that can have bearing on the temperature we are trying to
> > control.  It decides how much power to give each cooling device based
> > on the performance they are requesting.  The PID controller ensures
> > that the total power budget does not exceed the control temperature.
> > 
> > Cc: Zhang Rui <rui.zhang@intel.com>
> > Cc: Eduardo Valentin <edubezval@gmail.com>
> > Signed-off-by: Punit Agrawal <punit.agrawal@arm.com>
> > Signed-off-by: Javi Merino <javi.merino@arm.com>
> > ---
> >  Documentation/thermal/power_allocator.txt | 196 ++++++++++++
> >  drivers/thermal/Kconfig                   |  15 +
> >  drivers/thermal/Makefile                  |   1 +
> >  drivers/thermal/power_allocator.c         | 511 ++++++++++++++++++++++++++++++
> >  drivers/thermal/thermal_core.c            |   7 +-
> >  drivers/thermal/thermal_core.h            |   8 +
> >  include/linux/thermal.h                   |  40 ++-
> >  7 files changed, 774 insertions(+), 4 deletions(-)
> >  create mode 100644 drivers/thermal/power_allocator.c
> > 
> > diff --git a/Documentation/thermal/power_allocator.txt b/Documentation/thermal/power_allocator.txt
> > index d3bb79050c27..23b684afdc75 100644
> > --- a/Documentation/thermal/power_allocator.txt
> > +++ b/Documentation/thermal/power_allocator.txt
> > @@ -1,3 +1,172 @@
> > +Power allocator governor tunables
> > +=================================
> > +
> > +Trip points
> > +-----------
> > +
> > +The governor requires the following two passive trip points:
> > +
> > +1.  "switch on" trip point: temperature above which the governor
> > +    control loop starts operating.
> > +2.  "desired temperature" trip point: it should be higher than the
> > +    "switch on" trip point. It is the target temperature the governor
> > +    is controlling for.
> > +
> > +PID Controller
> > +--------------
> > +
> > +The power allocator governor implements a
> > +Proportional-Integral-Derivative controller (PID controller) with
> > +temperature as the control input and power as the controlled output:
> > +
> > +    P_max = k_p * e + k_i * err_integral + k_d * diff_err + sustainable_power
> > +
> > +where
> > +    e = desired_temperature - current_temperature
> > +    err_integral is the sum of previous errors
> > +    diff_err = e - previous_error
> > +
> > +It is similar to the one depicted below:
> > +
> > +                                      k_d
> > +                                       |
> > +current_temp                           |
> > +     |                                 v
> > +     |                +----------+   +---+
> > +     |         +----->| diff_err |-->| X |------+
> > +     |         |      +----------+   +---+      |
> > +     |         |                                |      tdp        actor
> > +     |         |                      k_i       |       |    get_actual_power()
> > +     |         |                       |        |       |        |     |
> > +     |         |                       |        |       |        |     | ...
> > +     v         |                       v        v       v        v     v
> > +   +---+       |      +-------+      +---+    +---+   +---+   +----------+
> > +   | S |-------+----->| sum e |----->| X |--->| S |-->| S |-->|power     |
> > +   +---+       |      +-------+      +---+    +---+   +---+   |allocation|
> > +     ^         |                                ^             +----------+
> > +     |         |                                |                |     |
> > +     |         |        +---+                   |                |     |
> > +     |         +------->| X |-------------------+                v     v
> > +     |                  +---+                               granted performance
> > +desired_temperature       ^
> > +                          |
> > +                          |
> > +                      k_po/k_pu
> > +
> > +Sustainable power
> > +-----------------
> > +
> > +An estimate of the sustainable dissipatable power (in mW) should be
> > +provided while registering the thermal zone.  This estimates the
> > +sustained power that can be dissipated at the desired control
> > +temperature.  This is the maximum sustained power for allocation at
> > +the desired maximum temperature.  The actual sustained power can vary
> > +for a number of reasons.  The closed loop controller will take care of
> > +variations such as environmental conditions, and some factors related
> > +to the speed-grade of the silicon.  `sustainable_power` is therefore
> > +simply an estimate, and may be tuned to affect the aggressiveness of
> > +the thermal ramp.  For reference, this is 2000mW - 4500mW depending on
> > +screen size (4" phone - 10" tablet).
> 
> I would rephrase the example as:
> 'For reference, the sustainable power of a 4" phone is typically 2000mW,
> while on a 10" table is around 4500mW (may vary depending on screen
> size).

Ok

> > +
> > +If you are using device tree, do add it as a property of the
> > +thermal-zone.  For example:
> > +
> > +	thermal-zones {
> > +		soc_thermal {
> > +			polling-delay = <1000>;
> > +			polling-delay-passive = <100>;
> > +			sustainable-power = <2500>;
> > +			...
> > +
> > +If you use platform code to register your thermal zone instead, pass a
> > +`thermal_zone_params` that has a `sustainable_power`.  If you weren't
> > +passing any `thermal_zone_params`, then something like this will do:
> > +
> > +	static const struct thermal_zone_params tz_params = {
> > +		.sustainable_power = 3500,
> > +	};
> > +
> > +and then pass `tz_params` as the 5th parameter to
> > +`thermal_zone_device_register()`
> > +
> > +k_po and k_pu
> > +-------------
> > +
> > +The implementation of the PID controller in the power allocator
> > +thermal governor allows the configuration of two proportional term
> > +constants: `k_po` and `k_pu`.  `k_po` is the proportional term
> > +constant during temperature overshoot periods (current temperature is
> > +above "desired temperature" trip point).  Conversely, `k_pu` is the
> > +proportional term constant during temperature undershoot periods
> > +(current temperature below "desired temperature" trip point).
> > +
> > +These controls are intended as the primary mechanism for configuring
> > +the permitted thermal "ramp" of the system.  For instance, a lower
> > +`k_pu` value will provide a slower ramp, at the cost of capping
> > +available capacity at a low temperature.  On the other hand, a high
> > +value of `k_pu` will result in the governor granting very high power
> > +whilst temperature is low, and may lead to temperature overshooting.
> > +
> > +The default value for `k_pu` is:
> > +
> > +    2 * sustainable_power / (desired_temperature - switch_on_temp)
> > +
> > +This means that at `switch_on_temp` the output of the controller's
> > +proportional term will be 2 * `sustainable_power`.  The default value
> > +for `k_po` is:
> > +
> > +    sustainable_power / (desired_temperature - switch_on_temp)
> > +
> > +Focusing on the proportional and feed forward values of the PID
> > +controller equation we have:
> > +
> > +    P_max = k_p * e + sustainable_power
> > +
> > +The proportional term is proportional to the difference between the
> > +desired temperature and the current one.  When the current temperature
> > +is the desired one, then the proportional component is zero and
> > +`P_max` = `sustainable_power`.  That is, the system should operate in
> > +thermal equilibrium under constant load.  `sustainable_power` is only
> > +an estimate, which is the reason for closed-loop control such as this.
> > +
> > +Expanding `k_pu` we get:
> > +    P_max = 2 * sustainable_power * (T_set - T) / (T_set - T_on) +
> > +        sustainable_power
> > +
> > +where
> > +    T_set is the desired temperature
> > +    T is the current temperature
> > +    T_on is the switch on temperature
> > +
> > +When the current temperature is the switch_on temperature, the above
> > +formula becomes:
> > +
> > +    P_max = 2 * sustainable_power * (T_set - T_on) / (T_set - T_on) +
> > +        sustainable_power = 2 * sustainable_power + sustainable_power = 
> > +        3 * sustainable_power
> > +
> > +Therefore, the proportional term alone linearly decreases power from
> > +3 * `sustainable_power` to `sustainable_power` as the temperature
> > +rises from the switch on temperature to the desired temperature.
> > +
> > +k_i and integral_cutoff
> > +-----------------------
> > +
> > +`k_i` configures the PID loop's integral term constant.  This term
> > +allows the PID controller to compensate for long term drift and for
> > +the quantized nature of the output control: cooling devices can't set
> > +the exact power that the governor requests.  When the temperature
> > +error is below `integral_cutoff`, errors are accumulated in the
> > +integral term.  This term is then multiplied by `k_i` and the result
> > +added to the output of the controller.  Typically `k_i` is set low (1
> > +or 2) and `integral_cutoff` is 0.
> > +
> > +k_d
> > +---
> 
> k_d may be conflicted with Kd  (capacitance) of the cooling device power API.
> 
> Is it possible to change / rename either one of them?

You're right, I'll rename the one in the formula.

> > +
> > +`k_d` configures the PID loop's derivative term constant.  It's
> > +recommended to leave it as the default: 0.
> > +
> >  Cooling device power API
> >  ========================
> >  
> > @@ -25,3 +194,30 @@ milliwatts.
> >  
> >  Calculate a cooling device state that would make the device consume at
> >  most @power mW.
> > +
> > +Cooling device weights
> > +----------------------
> > +
> > +Weights are a mechanism to bias the allocation between cooling
> > +devices.  They express the relative power efficiency of different
> > +cooling devices.  Higher weight can be used to express higher power
> > +efficiency.  Weighting is relative such that if each cooling device
> > +has a weight of one they are considered equal.  This is particularly
> > +useful in heterogeneous systems where two cooling devices may perform
> > +the same kind of compute, but with different efficiency.  For example,
> > +a system with two different types of processors.
> > +
> > +Weights shall be passed as part of the thermal zone's
> > +`thermal_bind_parameters`.
> > +
> > +Limitations of the power allocator governor
> > +===========================================
> > +
> > +The power allocator governor's PID controller works best if there is a
> > +periodic tick.  If you have a driver that calls
> > +`thermal_zone_device_update()` (or anything that ends up calling the
> > +governor's `throttle()` function) repetitively, the governor response
> > +won't be very good.  Note that this is not particular to this
> > +governor, step-wise will also misbehave if you call its throttle()
> > +faster than the normal thermal framework tick (due to interrupts for
> > +example) as it will overreact.
> > diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> > index f554d25b4399..4496fa5e4a33 100644
> > --- a/drivers/thermal/Kconfig
> > +++ b/drivers/thermal/Kconfig
> > @@ -71,6 +71,14 @@ config THERMAL_DEFAULT_GOV_USER_SPACE
> >  	  Select this if you want to let the user space manage the
> >  	  platform thermals.
> >  
> > +config THERMAL_DEFAULT_GOV_POWER_ALLOCATOR
> > +	bool "power_allocator"
> > +	select THERMAL_GOV_POWER_ALLOCATOR
> > +	help
> > +	  Select this if you want to control temperature based on
> > +	  system and device power allocation. This governor relies on
> > +	  power actors to operate.
> > +
> >  endchoice
> >  
> >  config THERMAL_GOV_FAIR_SHARE
> > @@ -99,6 +107,13 @@ config THERMAL_GOV_USER_SPACE
> >  	help
> >  	  Enable this to let the user space manage the platform thermals.
> >  
> > +config THERMAL_GOV_POWER_ALLOCATOR
> > +	bool "Power allocator thermal governor"
> > +	select THERMAL_POWER_ACTOR
> > +	help
> > +	  Enable this to manage platform thermals by dynamically
> > +	  allocating and limiting power to devices.
> 
> I think the config entry deserves a better description, don't you
> agree?

Given that the entry for fair-share is "Enable this to manage platform
thermals using fair-share governor." and the entry for step wise is
"Enable this to manage platform thermals using a simple linear
governor." this is actually pretty good ;)  

Instead of repeating in the config entry what is in the documentation,
I think I'll put a pointer to
Documentation/thermal/power_allocator.txt

> > +
> >  config CPU_THERMAL
> >  	bool "generic cpu cooling support"
> >  	depends on CPU_FREQ
> > diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> > index 39c4fe87da2f..c33904848c45 100644
> > --- a/drivers/thermal/Makefile
> > +++ b/drivers/thermal/Makefile
> > @@ -14,6 +14,7 @@ thermal_sys-$(CONFIG_THERMAL_GOV_FAIR_SHARE)	+= fair_share.o
> >  thermal_sys-$(CONFIG_THERMAL_GOV_BANG_BANG)	+= gov_bang_bang.o
> >  thermal_sys-$(CONFIG_THERMAL_GOV_STEP_WISE)	+= step_wise.o
> >  thermal_sys-$(CONFIG_THERMAL_GOV_USER_SPACE)	+= user_space.o
> > +thermal_sys-$(CONFIG_THERMAL_GOV_POWER_ALLOCATOR)	+= power_allocator.o
> >  
> >  # cpufreq cooling
> >  thermal_sys-$(CONFIG_CPU_THERMAL)	+= cpu_cooling.o
> > diff --git a/drivers/thermal/power_allocator.c b/drivers/thermal/power_allocator.c
> > new file mode 100644
> > index 000000000000..09e98991efbb
> > --- /dev/null
> > +++ b/drivers/thermal/power_allocator.c
> > @@ -0,0 +1,511 @@
> > +/*
> > + * A power allocator to manage temperature
> > + *
> > + * Copyright (C) 2014 ARM Ltd.
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + *
> > + * This program is distributed "as is" WITHOUT ANY WARRANTY of any
> > + * kind, whether express or implied; without even the implied warranty
> > + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +#define pr_fmt(fmt) "Power allocator: " fmt
> > +
> > +#include <linux/rculist.h>
> > +#include <linux/slab.h>
> > +#include <linux/thermal.h>
> > +
> > +#include "thermal_core.h"
> > +
> > +#define FRAC_BITS 10
> > +#define int_to_frac(x) ((x) << FRAC_BITS)
> > +#define frac_to_int(x) ((x) >> FRAC_BITS)
> > +
> > +/**
> > + * mul_frac() - multiply two fixed-point numbers
> > + * @x:	first multiplicand
> > + * @y:	second multiplicand
> > + *
> 
> If it is a kernel doc, needs a description.

Other parts of the kernel are more liberal in this regard,
specially fro trivial functions like this.  Also, kernel-doc creates a
documentation just fine:

$ scripts/kernel-doc -function mul_frac drivers/thermal/power_allocator.c | nroff -man
mul_frac(9)                 Kernel Hacker's Manual                 mul_frac(9)



NAME
       mul_frac - multiply two fixed-point numbers

SYNOPSIS
       s64 mul_frac (s64 x, s64 y);

ARGUMENTS
       x           first multiplicand

       y           second multiplicand

RETURN
       the  result of multiplying two fixed-point numbers.  The result is also
       a fixed-point number.



January 2015                       mul_frac                        mul_frac(9)


I'll add the long description if you want to, but this is not a
warning.

> > + * Return: the result of multiplying two fixed-point numbers.  The
> > + * result is also a fixed-point number.
> > + */
> > +static inline s64 mul_frac(s64 x, s64 y)
> > +{
> > +	return (x * y) >> FRAC_BITS;
> > +}
> > +
> > +enum power_allocator_trip_levels {
> > +	TRIP_SWITCH_ON = 0,	/* Switch on PID controller */
> > +	TRIP_MAX_DESIRED_TEMPERATURE, /* Temperature we are controlling for */
> > +
> > +	THERMAL_TRIP_NUM,
> > +};
> > +
> > +/**
> > + * struct power_allocator_params - parameters for the power allocator governor
> > + * @k_po:	Proportional parameter of the PID controller when overshooting
> > + *		(i.e., when temperature is below the target)
> > + * @k_pu:	Proportional parameter of the PID controller when undershooting
> > + * @k_i:	Integral parameter of the PID controller
> > + * @k_d:	Derivative parameter of the PID controller
> > + * @integral_cutoff:	threshold below which the error is no longer accumulated
> > +			in the PID controller
> > + * @err_integral:	accumulated error in the PID controller.
> > + * @prev_err:	error in the previous iteration of the PID controller.
> > + *		Used to calculate the derivative term.
> > + */
> > +struct power_allocator_params {
> > +	s32 k_po;
> > +	s32 k_pu;
> > +	s32 k_i;
> > +	s32 k_d;
> > +	s32 integral_cutoff;
> > +	s64 err_integral;
> > +	s32 prev_err;
> > +};
> > +
> > +/**
> > + * get_actor_weight() - get the weight for the power actor
> > + * @tz:		thermal zone we are operating in
> > + * @actor:	the power actor
> > + *
> 
> 
> ditto
> 
> > + * Returns: The weight inside the thermal binding parameters of the
> 
> s/Returns:/Return:/g

Yep.

> Please run the kernel doc script on your patches and avoid adding
> warnings / errors.

Actually, kernel-doc doesn't complain about Returns vs Return and it
doesn't really care if there is no description in a function as I said
before.

Is there a better way than running "scripts/kernel-doc -function
$FUNCTION $FILE" ?  It would be great if scripts/check-patch.pl
checked this as well.

> > + * thermal zone.  If it could not be found, a default weight of 1 is
> > + * assumed.  Weights are expressed as a FRAC_BITS (currently 10-bit)
> > + * fixed point integer.
> > + */
> > +static int get_actor_weight(struct thermal_zone_device *tz,
> > +			struct thermal_cooling_device *cdev)
> > +{
> > +	int i;
> > +
> > +	for (i = 0; i < tz->tzp->num_tbps; i++)
> > +		if (tz->tzp->tbp[i].cdev == cdev)
> > +			return tz->tzp->tbp[i].weight;
> > +
> > +	return int_to_frac(1);
> > +}
> > +
> > +/**
> > + * pid_controller() - PID controller
> > + * @tz:	thermal zone we are operating in
> > + * @current_temp:	the current temperature in millicelsius
> > + * @control_temp:	the target temperature in millicelsius
> > + * @max_allocatable_power:	maximum allocatable power for this thermal zone
> > + *
> > + * This PID controller increases the available power budget so that the
> > + * temperature of the thermal zone gets as close as possible to
> > + * @control_temp and limits the power if it exceeds it.  k_po is the
> > + * proportional term when we are overshooting, k_pu is the
> > + * proportional term when we are undershooting.  integral_cutoff is a
> > + * threshold below which we stop accumulating the error.  The
> > + * accumulated error is only valid if the requested power will make
> > + * the system warmer.  If the system is mostly idle, there's no point
> > + * in accumulating positive error.
> > + *
> > + * Return: The power budget for the next period.
> > + */
> > +static u32 pid_controller(struct thermal_zone_device *tz,
> > +			unsigned long current_temp, unsigned long control_temp,
> > +			u32 max_allocatable_power)
> > +{
> > +	s64 p, i, d, power_range;
> > +	s32 err, max_power_frac;
> > +	struct power_allocator_params *params = tz->governor_data;
> > +
> > +	max_power_frac = int_to_frac(max_allocatable_power);
> > +
> > +	err = ((s32)control_temp - (s32)current_temp);
> > +	err = int_to_frac(err);
> > +
> > +	/* Calculate the proportional term */
> > +	p = mul_frac(err < 0 ? params->k_po : params->k_pu, err);
> > +
> > +	/*
> > +	 * Calculate the integral term
> > +	 *
> > +	 * if the error is less than cut off allow integration (but
> > +	 * the integral is limited to max power)
> > +	 */
> > +	i = mul_frac(params->k_i, params->err_integral);
> > +
> > +	if (err < int_to_frac(params->integral_cutoff)) {
> > +		s64 i_next = i + mul_frac(params->k_i, err);
> > +
> > +		if (abs64(i_next) < max_power_frac) {
> > +			i = i_next;
> > +			params->err_integral += err;
> > +		}
> > +	}
> > +
> > +	/*
> > +	 * Calculate the derivative term
> > +	 *
> > +	 * We do err - prev_err, so with a positive k_d, a decreasing
> > +	 * error (i.e. driving closer to the line) results in less
> > +	 * power being applied, slowing down the controller)
> > +	 */
> > +	d = mul_frac(params->k_d, err - params->prev_err);
> > +	params->prev_err = err;
> > +
> > +	power_range = p + i + d;
> > +
> > +	/* feed-forward the known sustainable dissipatable power */
> > +	power_range = tz->tzp->sustainable_power + frac_to_int(power_range);
> > +
> > +	return clamp(power_range, (s64)0, (s64)max_allocatable_power);
> > +}
> > +
> > +/**
> > + * divvy_up_power() - divvy the allocated power between the actors
> > + * @req_power:	each actor's requested power
> > + * @max_power:	each actor's maximum available power
> > + * @num_actors:	size of the @req_power, @max_power and @granted_power's array
> > + * @total_req_power: sum of @req_power
> > + * @power_range:	total allocated power
> > + * @granted_power:	output array: each actor's granted power
> > + *
> > + * This function divides the total allocated power (@power_range)
> > + * fairly between the actors.  It first tries to give each actor a
> > + * share of the @power_range according to how much power it requested
> > + * compared to the rest of the actors.  For example, if only one actor
> > + * requests power, then it receives all the @power_range.  If
> > + * three actors each requests 1mW, each receives a third of the
> > + * @power_range.
> > + *
> > + * If any actor received more than their maximum power, then that
> > + * surplus is re-divvied among the actors based on how far they are
> > + * from their respective maximums.
> > + *
> > + * Granted power for each actor is written to @granted_power, which
> > + * should've been allocated by the calling function.
> > + */
> > +static void divvy_up_power(u32 *req_power, u32 *max_power, int num_actors,
> > +			u32 total_req_power, u32 power_range,
> > +			u32 *granted_power)
> > +{
> > +	u32 extra_power, capped_extra_power, extra_actor_power[num_actors];
> > +	int i;
> > +
> > +	if (!total_req_power) {
> > +		/*
> > +		 * Nobody requested anything, so just give everybody
> > +		 * the maximum power
> > +		 */
> > +		for (i = 0; i < num_actors; i++)
> > +			granted_power[i] = max_power[i];
> > +
> > +		return;
> > +	}
> > +
> > +	capped_extra_power = 0;
> > +	extra_power = 0;
> > +	for (i = 0; i < num_actors; i++) {
> > +		u64 req_range = req_power[i] * power_range;
> > +
> > +		granted_power[i] = div_u64(req_range, total_req_power);
> > +
> > +		if (granted_power[i] > max_power[i]) {
> > +			extra_power += granted_power[i] - max_power[i];
> > +			granted_power[i] = max_power[i];
> 
> shouldn't we continue here?

No, you would leave the extra_actor_power[i] uninitialized.

> > +		}
> > +
> > +		extra_actor_power[i] = max_power[i] - granted_power[i];
> 
> Do we care when max_power[i] < granted_power[i]?

That can't happen, the above "if" prevents it.

> What happens to (overflowed) extra_actor_power[i]?

extra_actor_power[i] can't overflow, it's a u32 and is asigned the
result of substracting two u32s.  The code make sure that the minuend is
bigger or equal than the sustraend.

> > +		capped_extra_power += extra_actor_power[i];
> > +	}
> > +
> > +	if (!extra_power)
> > +		return;
> > +
> > +	/*
> > +	 * Re-divvy the reclaimed extra among actors based on
> > +	 * how far they are from the max
> > +	 */
> > +	extra_power = min(extra_power, capped_extra_power);
> > +	if (capped_extra_power > 0)
> > +		for (i = 0; i < num_actors; i++)
> > +			granted_power[i] += (extra_actor_power[i] *
> > +					extra_power) / capped_extra_power;
> > +}
> > +
> > +static int allocate_power(struct thermal_zone_device *tz,
> > +			unsigned long current_temp, unsigned long control_temp)
> > +{
> > +	struct thermal_instance *instance;
> > +	u32 *req_power, *max_power, *granted_power;
> > +	u32 total_req_power, max_allocatable_power;
> > +	u32 power_range;
> > +	int i, num_actors, ret = 0;
> > +
> > +	mutex_lock(&tz->lock);
> > +
> > +	num_actors = 0;
> > +	list_for_each_entry(instance, &tz->thermal_instances, tz_node)
> > +		if ((instance->trip == TRIP_MAX_DESIRED_TEMPERATURE) &&
> > +			cdev_is_power_actor(instance->cdev))
> > +			num_actors++;
> > +
> > +	req_power = devm_kcalloc(&tz->device, num_actors, sizeof(*req_power),
> > +				GFP_KERNEL);
> > +	if (!req_power) {
> > +		ret = -ENOMEM;
> > +		goto unlock;
> > +	}
> > +
> > +	max_power = devm_kcalloc(&tz->device, num_actors, sizeof(*max_power),
> > +				GFP_KERNEL);
> > +	if (!max_power) {
> > +		ret = -ENOMEM;
> > +		goto free_req_power;
> > +	}
> > +
> > +	granted_power = devm_kcalloc(&tz->device, num_actors,
> > +				sizeof(*granted_power), GFP_KERNEL);
> > +	if (!granted_power) {
> > +		ret = -ENOMEM;
> > +		goto free_max_power;
> > +	}
> > +
> > +	i = 0;
> > +	total_req_power = 0;
> > +	max_allocatable_power = 0;
> > +
> > +	list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
> > +		int weight;
> > +		struct thermal_cooling_device *cdev = instance->cdev;
> > +
> > +		if (instance->trip != TRIP_MAX_DESIRED_TEMPERATURE)
> > +			continue;
> > +
> > +		if (!cdev_is_power_actor(cdev))
> > +			continue;
> > +
> > +		req_power[i] = cdev->ops->get_actual_power(cdev);
> 
> Is this req_power (I read as 'requested power') or actual_power? I would
> use the later naming to avoid confusions.

We have had a lot of discussions internally about whether this should
be the power that the device wants to consume or the power that is
currently consuming.  The current implementation in the cpu cooling
device uses the power that the cpu is currently drawing because it's
very hard to get the frequency that cpufreq would assign if there were
no limits.

I understand that it's confusing as it is, but I think it's better to
rename get_actual_power() to get_requested_power() and explain this
limitation in the kerneldoc for cpufreq_get_requested_power()

> > +		weight = get_actor_weight(tz, cdev);
> > +		req_power[i] = frac_to_int(weight * req_power[i]);
> > +		total_req_power += req_power[i];
> 
> ditto for total_req_power.
> 
> > +
> > +		max_power[i] = power_actor_get_max_power(cdev);
> > +		max_allocatable_power += max_power[i];
> > +
> > +		i++;
> > +	}
> > +
> > +	power_range = pid_controller(tz, current_temp, control_temp,
> > +				max_allocatable_power);
> > +
> > +	divvy_up_power(req_power, max_power, num_actors, total_req_power,
> > +		power_range, granted_power);
> > +
> > +	i = 0;
> > +	list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
> > +		if (instance->trip != TRIP_MAX_DESIRED_TEMPERATURE)
> > +			continue;
> > +
> > +		if (!cdev_is_power_actor(instance->cdev))
> > +			continue;
> > +
> > +		power_actor_set_power(instance->cdev, granted_power[i]);
> > +
> > +		i++;
> > +	}
> > +
> > +	devm_kfree(&tz->device, granted_power);
> > +free_max_power:
> > +	devm_kfree(&tz->device, max_power);
> > +free_req_power:
> > +	devm_kfree(&tz->device, req_power);
> > +unlock:
> > +	mutex_unlock(&tz->lock);
> > +
> > +	return ret;
> > +}
> > +
> > +static int check_trips(struct thermal_zone_device *tz)
> > +{
> > +	int ret;
> > +	enum thermal_trip_type type;
> > +
> > +	if (tz->trips < THERMAL_TRIP_NUM)
> > +		return -EINVAL;
> > +
> > +	ret = tz->ops->get_trip_type(tz, TRIP_SWITCH_ON, &type);
> > +	if (ret)
> > +		return ret;
> > +
> > +	if (type != THERMAL_TRIP_PASSIVE)
> > +		return -EINVAL;
> > +
> > +	ret = tz->ops->get_trip_type(tz, TRIP_MAX_DESIRED_TEMPERATURE, &type);
> > +	if (ret)
> > +		return ret;
> > +
> > +	if (type != THERMAL_TRIP_PASSIVE)
> > +		return -EINVAL;
> > +
> > +	return ret;
> > +}
> > +
> > +static void reset_pid_controller(struct power_allocator_params *params)
> > +{
> > +	params->err_integral = 0;
> > +	params->prev_err = 0;
> > +}
> > +
> > +static void allow_maximum_power(struct thermal_zone_device *tz)
> > +{
> > +	struct thermal_instance *instance;
> > +
> > +	list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
> > +		u32 max_power;
> > +
> > +		if ((instance->trip != TRIP_MAX_DESIRED_TEMPERATURE) ||
> > +			(!cdev_is_power_actor(instance->cdev)))
> > +			continue;
> > +
> > +		max_power = power_actor_get_max_power(instance->cdev);
> > +		power_actor_set_power(instance->cdev, max_power);
> > +	}
> > +}
> > +
> > +/**
> > + * power_allocator_bind() - bind the power_allocator governor to a thermal zone
> > + * @tz:	thermal zone to bind it to
> > + *
> > + * Check that the thermal zone is valid for this governor, that is, it
> > + * has two thermal trips.  If so, initialize the PID controller
> > + * parameters and bind it to the thermal zone.
> > + *
> > + * Return: 0 on success, -EINVAL if the trips were invalid or -ENOMEM
> > + * if we ran out of memory.
> > + */
> > +static int power_allocator_bind(struct thermal_zone_device *tz)
> > +{
> > +	int ret;
> > +	struct power_allocator_params *params;
> > +	unsigned long switch_on_temp, control_temp;
> > +	u32 temperature_threshold;
> > +
> > +	ret = check_trips(tz);
> > +	if (ret) {
> > +		dev_err(&tz->device,
> > +			"thermal zone %s has the wrong number of trips for this governor\n",
> 
> I would be more specific:
> +			"thermal zone %s has wrong trip setup for power allocator\n",
> 
> 
> Besides, in 'check_trips' you check more than number of trips.

Correct, I'll change the error message.

> > +			tz->type);
> > +		return ret;
> > +	}
> > +
> > +	if (!tz->tzp || !tz->tzp->sustainable_power) {
> > +		dev_err(&tz->device,
> > +			"power_allocator: missing sustainable_power\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	params = devm_kzalloc(&tz->device, sizeof(*params), GFP_KERNEL);
> > +	if (!params)
> > +		return -ENOMEM;
> > +
> > +	ret = tz->ops->get_trip_temp(tz, TRIP_SWITCH_ON, &switch_on_temp);
> > +	if (ret)
> > +		goto free;
> > +
> > +	ret = tz->ops->get_trip_temp(tz, TRIP_MAX_DESIRED_TEMPERATURE,
> > +				&control_temp);
> > +	if (ret)
> > +		goto free;
> > +
> > +	temperature_threshold = control_temp - switch_on_temp;
> > +
> > +	params->k_po = tz->tzp->k_po ?:
> > +		int_to_frac(tz->tzp->sustainable_power) / temperature_threshold;
> > +	params->k_pu = tz->tzp->k_pu ?:
> > +		int_to_frac(2 * tz->tzp->sustainable_power) /
> > +		temperature_threshold;
> > +	params->k_i = tz->tzp->k_i ?: int_to_frac(10) / 1000;
> > +	params->k_d = tz->tzp->k_d ?: int_to_frac(0);
> > +	params->integral_cutoff = tz->tzp->integral_cutoff ?: 0;
> > +
> > +	reset_pid_controller(params);
> > +
> > +	tz->governor_data = params;
> > +
> > +	return 0;
> > +
> > +free:
> > +	devm_kfree(&tz->device, params);
> > +	return ret;
> > +}
> > +
> > +static void power_allocator_unbind(struct thermal_zone_device *tz)
> > +{
> > +	dev_dbg(&tz->device, "Unbinding from thermal zone %d\n", tz->id);
> > +	devm_kfree(&tz->device, tz->governor_data);
> > +	tz->governor_data = NULL;
> > +}
> > +
> > +static int power_allocator_throttle(struct thermal_zone_device *tz, int trip)
> > +{
> > +	int ret;
> > +	unsigned long switch_on_temp, control_temp, current_temp;
> > +	struct power_allocator_params *params = tz->governor_data;
> > +
> > +	/*
> > +	 * We get called for every trip point but we only need to do
> > +	 * our calculations once
> > +	 */
> > +	if (trip != TRIP_MAX_DESIRED_TEMPERATURE)
> > +		return 0;
> > +
> > +	ret = thermal_zone_get_temp(tz, &current_temp);
> > +	if (ret) {
> > +		dev_warn(&tz->device, "Failed to get temperature: %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	ret = tz->ops->get_trip_temp(tz, TRIP_SWITCH_ON, &switch_on_temp);
> > +	if (ret) {
> > +		dev_warn(&tz->device,
> > +			"Failed to get switch on temperature: %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	if (current_temp < switch_on_temp) {
> > +		tz->passive = 0;
> > +		reset_pid_controller(params);
> > +		allow_maximum_power(tz);
> > +		return 0;
> > +	}
> > +
> > +	tz->passive = 1;
> > +
> > +	ret = tz->ops->get_trip_temp(tz, TRIP_MAX_DESIRED_TEMPERATURE,
> > +				&control_temp);
> > +	if (ret) {
> > +		dev_warn(&tz->device,
> > +			"Failed to get the maximum desired temperature: %d\n",
> > +			ret);
> > +		return ret;
> > +	}
> > +
> > +	return allocate_power(tz, current_temp, control_temp);
> > +}
> > +
> > +static struct thermal_governor thermal_gov_power_allocator = {
> > +	.name		= "power_allocator",
> > +	.bind_to_tz	= power_allocator_bind,
> > +	.unbind_from_tz	= power_allocator_unbind,
> > +	.throttle	= power_allocator_throttle,
> > +};
> > +
> > +int thermal_gov_power_allocator_register(void)
> > +{
> > +	return thermal_register_governor(&thermal_gov_power_allocator);
> > +}
> > +
> > +void thermal_gov_power_allocator_unregister(void)
> > +{
> > +	thermal_unregister_governor(&thermal_gov_power_allocator);
> > +}
> > diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
> > index c490f262ea7f..4921e084c20b 100644
> > --- a/drivers/thermal/thermal_core.c
> > +++ b/drivers/thermal/thermal_core.c
> > @@ -1905,7 +1905,11 @@ static int __init thermal_register_governors(void)
> >  	if (result)
> >  		return result;
> >  
> > -	return thermal_gov_user_space_register();
> > +	result = thermal_gov_user_space_register();
> > +	if (result)
> > +		return result;
> > +
> > +	return thermal_gov_power_allocator_register();
> >  }
> >  
> >  static void thermal_unregister_governors(void)
> > @@ -1914,6 +1918,7 @@ static void thermal_unregister_governors(void)
> >  	thermal_gov_fair_share_unregister();
> >  	thermal_gov_bang_bang_unregister();
> >  	thermal_gov_user_space_unregister();
> > +	thermal_gov_power_allocator_unregister();
> >  }
> >  
> >  static int __init thermal_init(void)
> > diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h
> > index d15d243de27a..b907be823527 100644
> > --- a/drivers/thermal/thermal_core.h
> > +++ b/drivers/thermal/thermal_core.h
> > @@ -85,6 +85,14 @@ static inline int thermal_gov_user_space_register(void) { return 0; }
> >  static inline void thermal_gov_user_space_unregister(void) {}
> >  #endif /* CONFIG_THERMAL_GOV_USER_SPACE */
> >  
> > +#ifdef CONFIG_THERMAL_GOV_POWER_ALLOCATOR
> > +int thermal_gov_power_allocator_register(void);
> > +void thermal_gov_power_allocator_unregister(void);
> > +#else
> > +static inline int thermal_gov_power_allocator_register(void) { return 0; }
> > +static inline void thermal_gov_power_allocator_unregister(void) {}
> > +#endif /* CONFIG_THERMAL_GOV_POWER_ALLOCATOR */
> > +
> >  /* device tree support */
> >  #ifdef CONFIG_THERMAL_OF
> >  int of_parse_thermal_zones(void);
> > diff --git a/include/linux/thermal.h b/include/linux/thermal.h
> > index 1155457caf52..b23e019b1761 100644
> > --- a/include/linux/thermal.h
> > +++ b/include/linux/thermal.h
> > @@ -61,6 +61,8 @@
> >  #define DEFAULT_THERMAL_GOVERNOR       "fair_share"
> >  #elif defined(CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE)
> >  #define DEFAULT_THERMAL_GOVERNOR       "user_space"
> > +#elif defined(CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR)
> > +#define DEFAULT_THERMAL_GOVERNOR       "power_allocator"
> >  #endif
> >  
> >  struct thermal_zone_device;
> > @@ -255,9 +257,14 @@ struct thermal_bind_params {
> >  
> >  	/*
> >  	 * This is a measure of 'how effectively these devices can
> > -	 * cool 'this' thermal zone. The shall be determined by platform
> > -	 * characterization. This is on a 'percentage' scale.
> > -	 * See Documentation/thermal/sysfs-api.txt for more information.
> > +	 * cool 'this' thermal zone. The shall be determined by
> > +	 * platform characterization. For the fair-share governor,
> > +	 * this is on a 'percentage' scale.  See
> > +	 * Documentation/thermal/sysfs-api.txt for more
> > +	 * information. For the power_allocator governor, they are
> > +	 * relative to each other, see
> > +	 * Documentation/thermal/power_allocator.txt for more
> > +	 * information.
> 
> What happens if we register a thermal zone with relative weights, at
> fist the user uses power allocator, but then wants to, for some reason,
> use fair share? (or vice-versa).
> 
> Can't power allocator use percentages too?

The problem with percentages is that they are hard to use as they
depend on each other.  For example, you need to know how many cooling
devices there are and that number needs to remain fixed.  You can't
add a new cooling device without changing the weights of all the other
existing cooling devices.  If the thermal zone is already created it's
hard to change the weights so you really can't add or remove cooling
devices.

We were talking in another thread of ignoring a cooling device on some
error.  How do you do that if the percentages must add to a hundred?

I thought that we could reuse the "weight" parameter but if you think
we are abusing by making it mean different things for different
governors we can create a new one instead.

Cheers,
Javi


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 9/9] of: thermal: Introduce sustainable power for a thermal zone
  2015-01-06 13:13       ` Eduardo Valentin
@ 2015-01-06 13:29         ` Javi Merino
  0 siblings, 0 replies; 48+ messages in thread
From: Javi Merino @ 2015-01-06 13:29 UTC (permalink / raw)
  To: Eduardo Valentin
  Cc: linux-pm, linux-kernel, Punit Agrawal, broonie, Zhang Rui

On Tue, Jan 06, 2015 at 01:13:03PM +0000, Eduardo Valentin wrote:
> On Tue, Jan 06, 2015 at 09:42:15AM +0000, Javi Merino wrote:
> > On Fri, Jan 02, 2015 at 03:53:00PM +0000, Eduardo Valentin wrote:
> > > On Fri, Dec 05, 2014 at 07:04:20PM +0000, Javi Merino wrote:
> > > > From: Punit Agrawal <punit.agrawal@arm.com>
> > > > 
> > > > Introduce an optional property called, sustainable-power, which
> > > > represents the power (in mW) which the thermal zone can safely
> > > > dissipate.
> > > > 
> > > > If provided the property is parsed and associated with the thermal
> > > > zone via the thermal zone parameters.
> > > > 
> > > > Cc: Zhang Rui <rui.zhang@intel.com>
> > > > Cc: Eduardo Valentin <edubezval@gmail.com>
> > > > Signed-off-by: Punit Agrawal <punit.agrawal@arm.com>
> > > > ---
> > > >  Documentation/devicetree/bindings/thermal/thermal.txt | 4 ++++
> > > >  drivers/thermal/of-thermal.c                          | 4 ++++
> > > >  2 files changed, 8 insertions(+)
> > > > 
> > > > diff --git a/Documentation/devicetree/bindings/thermal/thermal.txt b/Documentation/devicetree/bindings/thermal/thermal.txt
> > > > index f5db6b72a36f..c6eb9a8d2aed 100644
> > > > --- a/Documentation/devicetree/bindings/thermal/thermal.txt
> > > > +++ b/Documentation/devicetree/bindings/thermal/thermal.txt
> > > > @@ -167,6 +167,10 @@ Optional property:
> > > >  			by means of sensor ID. Additional coefficients are
> > > >  			interpreted as constant offset.
> > > >  
> > > > +- sustainable-power:	An estimate of the sustainable power (in mW) that the
> > > > +  Type: unsigned	thermal zone can dissipate.
> > > > +  Size: one cell
> > > > +
> > > 
> > > Please, include examples of this property, as you mentioned in the
> > > governor documentation.
> > 
> > I'd rather put a pointer to the documentation instead of repeating the
> > same thing here.  What do you think?
> 
> The point is that device tree and Linux are supposed to be independent
> entities. I would prefer if you could extend the explanation of what is
> 'sustainable power' in the above entry. On top of that, pick one of the
> existing examples and extend it to include the 'sustainable-power' property,
> with a comment explaining it, for instance.

Ok, I'll do that.

Cheers,
Javi

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 7/9] thermal: introduce the Power Allocator governor
  2015-01-06 13:23     ` Javi Merino
@ 2015-01-06 14:18       ` Eduardo Valentin
  2015-01-06 14:50         ` Javi Merino
  0 siblings, 1 reply; 48+ messages in thread
From: Eduardo Valentin @ 2015-01-06 14:18 UTC (permalink / raw)
  To: Javi Merino; +Cc: linux-pm, linux-kernel, Punit Agrawal, broonie, Zhang Rui

[-- Attachment #1: Type: text/plain, Size: 41646 bytes --]

On Tue, Jan 06, 2015 at 01:23:42PM +0000, Javi Merino wrote:
> Hi Eduardo,
> 
> On Fri, Jan 02, 2015 at 03:46:24PM +0000, Eduardo Valentin wrote:
> > On Fri, Dec 05, 2014 at 07:04:18PM +0000, Javi Merino wrote:
> > > The power allocator governor is a thermal governor that controls system
> > > and device power allocation to control temperature.  Conceptually, the
> > > implementation divides the sustainable power of a thermal zone among
> > > all the heat sources in that zone.
> > > 
> > > This governor relies on "power actors", entities that represent heat
> > > sources.  They can report current and maximum power consumption and
> > > can set a given maximum power consumption, usually via a cooling
> > > device.
> > > 
> > > The governor uses a Proportional Integral Derivative (PID) controller
> > > driven by the temperature of the thermal zone.  The output of the
> > > controller is a power budget that is then allocated to each power
> > > actor that can have bearing on the temperature we are trying to
> > > control.  It decides how much power to give each cooling device based
> > > on the performance they are requesting.  The PID controller ensures
> > > that the total power budget does not exceed the control temperature.
> > > 
> > > Cc: Zhang Rui <rui.zhang@intel.com>
> > > Cc: Eduardo Valentin <edubezval@gmail.com>
> > > Signed-off-by: Punit Agrawal <punit.agrawal@arm.com>
> > > Signed-off-by: Javi Merino <javi.merino@arm.com>
> > > ---
> > >  Documentation/thermal/power_allocator.txt | 196 ++++++++++++
> > >  drivers/thermal/Kconfig                   |  15 +
> > >  drivers/thermal/Makefile                  |   1 +
> > >  drivers/thermal/power_allocator.c         | 511 ++++++++++++++++++++++++++++++
> > >  drivers/thermal/thermal_core.c            |   7 +-
> > >  drivers/thermal/thermal_core.h            |   8 +
> > >  include/linux/thermal.h                   |  40 ++-
> > >  7 files changed, 774 insertions(+), 4 deletions(-)
> > >  create mode 100644 drivers/thermal/power_allocator.c
> > > 
> > > diff --git a/Documentation/thermal/power_allocator.txt b/Documentation/thermal/power_allocator.txt
> > > index d3bb79050c27..23b684afdc75 100644
> > > --- a/Documentation/thermal/power_allocator.txt
> > > +++ b/Documentation/thermal/power_allocator.txt
> > > @@ -1,3 +1,172 @@
> > > +Power allocator governor tunables
> > > +=================================
> > > +
> > > +Trip points
> > > +-----------
> > > +
> > > +The governor requires the following two passive trip points:
> > > +
> > > +1.  "switch on" trip point: temperature above which the governor
> > > +    control loop starts operating.
> > > +2.  "desired temperature" trip point: it should be higher than the
> > > +    "switch on" trip point. It is the target temperature the governor
> > > +    is controlling for.
> > > +
> > > +PID Controller
> > > +--------------
> > > +
> > > +The power allocator governor implements a
> > > +Proportional-Integral-Derivative controller (PID controller) with
> > > +temperature as the control input and power as the controlled output:
> > > +
> > > +    P_max = k_p * e + k_i * err_integral + k_d * diff_err + sustainable_power
> > > +
> > > +where
> > > +    e = desired_temperature - current_temperature
> > > +    err_integral is the sum of previous errors
> > > +    diff_err = e - previous_error
> > > +
> > > +It is similar to the one depicted below:
> > > +
> > > +                                      k_d
> > > +                                       |
> > > +current_temp                           |
> > > +     |                                 v
> > > +     |                +----------+   +---+
> > > +     |         +----->| diff_err |-->| X |------+
> > > +     |         |      +----------+   +---+      |
> > > +     |         |                                |      tdp        actor
> > > +     |         |                      k_i       |       |    get_actual_power()
> > > +     |         |                       |        |       |        |     |
> > > +     |         |                       |        |       |        |     | ...
> > > +     v         |                       v        v       v        v     v
> > > +   +---+       |      +-------+      +---+    +---+   +---+   +----------+
> > > +   | S |-------+----->| sum e |----->| X |--->| S |-->| S |-->|power     |
> > > +   +---+       |      +-------+      +---+    +---+   +---+   |allocation|
> > > +     ^         |                                ^             +----------+
> > > +     |         |                                |                |     |
> > > +     |         |        +---+                   |                |     |
> > > +     |         +------->| X |-------------------+                v     v
> > > +     |                  +---+                               granted performance
> > > +desired_temperature       ^
> > > +                          |
> > > +                          |
> > > +                      k_po/k_pu
> > > +
> > > +Sustainable power
> > > +-----------------
> > > +
> > > +An estimate of the sustainable dissipatable power (in mW) should be
> > > +provided while registering the thermal zone.  This estimates the
> > > +sustained power that can be dissipated at the desired control
> > > +temperature.  This is the maximum sustained power for allocation at
> > > +the desired maximum temperature.  The actual sustained power can vary
> > > +for a number of reasons.  The closed loop controller will take care of
> > > +variations such as environmental conditions, and some factors related
> > > +to the speed-grade of the silicon.  `sustainable_power` is therefore
> > > +simply an estimate, and may be tuned to affect the aggressiveness of
> > > +the thermal ramp.  For reference, this is 2000mW - 4500mW depending on
> > > +screen size (4" phone - 10" tablet).
> > 
> > I would rephrase the example as:
> > 'For reference, the sustainable power of a 4" phone is typically 2000mW,
> > while on a 10" table is around 4500mW (may vary depending on screen
> > size).
> 
> Ok
> 
> > > +
> > > +If you are using device tree, do add it as a property of the
> > > +thermal-zone.  For example:
> > > +
> > > +	thermal-zones {
> > > +		soc_thermal {
> > > +			polling-delay = <1000>;
> > > +			polling-delay-passive = <100>;
> > > +			sustainable-power = <2500>;
> > > +			...
> > > +
> > > +If you use platform code to register your thermal zone instead, pass a
> > > +`thermal_zone_params` that has a `sustainable_power`.  If you weren't
> > > +passing any `thermal_zone_params`, then something like this will do:
> > > +
> > > +	static const struct thermal_zone_params tz_params = {
> > > +		.sustainable_power = 3500,
> > > +	};
> > > +
> > > +and then pass `tz_params` as the 5th parameter to
> > > +`thermal_zone_device_register()`
> > > +
> > > +k_po and k_pu
> > > +-------------
> > > +
> > > +The implementation of the PID controller in the power allocator
> > > +thermal governor allows the configuration of two proportional term
> > > +constants: `k_po` and `k_pu`.  `k_po` is the proportional term
> > > +constant during temperature overshoot periods (current temperature is
> > > +above "desired temperature" trip point).  Conversely, `k_pu` is the
> > > +proportional term constant during temperature undershoot periods
> > > +(current temperature below "desired temperature" trip point).
> > > +
> > > +These controls are intended as the primary mechanism for configuring
> > > +the permitted thermal "ramp" of the system.  For instance, a lower
> > > +`k_pu` value will provide a slower ramp, at the cost of capping
> > > +available capacity at a low temperature.  On the other hand, a high
> > > +value of `k_pu` will result in the governor granting very high power
> > > +whilst temperature is low, and may lead to temperature overshooting.
> > > +
> > > +The default value for `k_pu` is:
> > > +
> > > +    2 * sustainable_power / (desired_temperature - switch_on_temp)
> > > +
> > > +This means that at `switch_on_temp` the output of the controller's
> > > +proportional term will be 2 * `sustainable_power`.  The default value
> > > +for `k_po` is:
> > > +
> > > +    sustainable_power / (desired_temperature - switch_on_temp)
> > > +
> > > +Focusing on the proportional and feed forward values of the PID
> > > +controller equation we have:
> > > +
> > > +    P_max = k_p * e + sustainable_power
> > > +
> > > +The proportional term is proportional to the difference between the
> > > +desired temperature and the current one.  When the current temperature
> > > +is the desired one, then the proportional component is zero and
> > > +`P_max` = `sustainable_power`.  That is, the system should operate in
> > > +thermal equilibrium under constant load.  `sustainable_power` is only
> > > +an estimate, which is the reason for closed-loop control such as this.
> > > +
> > > +Expanding `k_pu` we get:
> > > +    P_max = 2 * sustainable_power * (T_set - T) / (T_set - T_on) +
> > > +        sustainable_power
> > > +
> > > +where
> > > +    T_set is the desired temperature
> > > +    T is the current temperature
> > > +    T_on is the switch on temperature
> > > +
> > > +When the current temperature is the switch_on temperature, the above
> > > +formula becomes:
> > > +
> > > +    P_max = 2 * sustainable_power * (T_set - T_on) / (T_set - T_on) +
> > > +        sustainable_power = 2 * sustainable_power + sustainable_power = 
> > > +        3 * sustainable_power
> > > +
> > > +Therefore, the proportional term alone linearly decreases power from
> > > +3 * `sustainable_power` to `sustainable_power` as the temperature
> > > +rises from the switch on temperature to the desired temperature.
> > > +
> > > +k_i and integral_cutoff
> > > +-----------------------
> > > +
> > > +`k_i` configures the PID loop's integral term constant.  This term
> > > +allows the PID controller to compensate for long term drift and for
> > > +the quantized nature of the output control: cooling devices can't set
> > > +the exact power that the governor requests.  When the temperature
> > > +error is below `integral_cutoff`, errors are accumulated in the
> > > +integral term.  This term is then multiplied by `k_i` and the result
> > > +added to the output of the controller.  Typically `k_i` is set low (1
> > > +or 2) and `integral_cutoff` is 0.
> > > +
> > > +k_d
> > > +---
> > 
> > k_d may be conflicted with Kd  (capacitance) of the cooling device power API.
> > 
> > Is it possible to change / rename either one of them?
> 
> You're right, I'll rename the one in the formula.
> 
> > > +
> > > +`k_d` configures the PID loop's derivative term constant.  It's
> > > +recommended to leave it as the default: 0.
> > > +
> > >  Cooling device power API
> > >  ========================
> > >  
> > > @@ -25,3 +194,30 @@ milliwatts.
> > >  
> > >  Calculate a cooling device state that would make the device consume at
> > >  most @power mW.
> > > +
> > > +Cooling device weights
> > > +----------------------
> > > +
> > > +Weights are a mechanism to bias the allocation between cooling
> > > +devices.  They express the relative power efficiency of different
> > > +cooling devices.  Higher weight can be used to express higher power
> > > +efficiency.  Weighting is relative such that if each cooling device
> > > +has a weight of one they are considered equal.  This is particularly
> > > +useful in heterogeneous systems where two cooling devices may perform
> > > +the same kind of compute, but with different efficiency.  For example,
> > > +a system with two different types of processors.
> > > +
> > > +Weights shall be passed as part of the thermal zone's
> > > +`thermal_bind_parameters`.
> > > +
> > > +Limitations of the power allocator governor
> > > +===========================================
> > > +
> > > +The power allocator governor's PID controller works best if there is a
> > > +periodic tick.  If you have a driver that calls
> > > +`thermal_zone_device_update()` (or anything that ends up calling the
> > > +governor's `throttle()` function) repetitively, the governor response
> > > +won't be very good.  Note that this is not particular to this
> > > +governor, step-wise will also misbehave if you call its throttle()
> > > +faster than the normal thermal framework tick (due to interrupts for
> > > +example) as it will overreact.
> > > diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> > > index f554d25b4399..4496fa5e4a33 100644
> > > --- a/drivers/thermal/Kconfig
> > > +++ b/drivers/thermal/Kconfig
> > > @@ -71,6 +71,14 @@ config THERMAL_DEFAULT_GOV_USER_SPACE
> > >  	  Select this if you want to let the user space manage the
> > >  	  platform thermals.
> > >  
> > > +config THERMAL_DEFAULT_GOV_POWER_ALLOCATOR
> > > +	bool "power_allocator"
> > > +	select THERMAL_GOV_POWER_ALLOCATOR
> > > +	help
> > > +	  Select this if you want to control temperature based on
> > > +	  system and device power allocation. This governor relies on
> > > +	  power actors to operate.
> > > +
> > >  endchoice
> > >  
> > >  config THERMAL_GOV_FAIR_SHARE
> > > @@ -99,6 +107,13 @@ config THERMAL_GOV_USER_SPACE
> > >  	help
> > >  	  Enable this to let the user space manage the platform thermals.
> > >  
> > > +config THERMAL_GOV_POWER_ALLOCATOR
> > > +	bool "Power allocator thermal governor"
> > > +	select THERMAL_POWER_ACTOR
> > > +	help
> > > +	  Enable this to manage platform thermals by dynamically
> > > +	  allocating and limiting power to devices.
> > 
> > I think the config entry deserves a better description, don't you
> > agree?
> 
> Given that the entry for fair-share is "Enable this to manage platform
> thermals using fair-share governor." and the entry for step wise is
> "Enable this to manage platform thermals using a simple linear
> governor." this is actually pretty good ;)  

hehehe... It does not mean we should follow bad examples right? :-)

> 
> Instead of repeating in the config entry what is in the documentation,
> I think I'll put a pointer to
> Documentation/thermal/power_allocator.txt

Good!

> 
> > > +
> > >  config CPU_THERMAL
> > >  	bool "generic cpu cooling support"
> > >  	depends on CPU_FREQ
> > > diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> > > index 39c4fe87da2f..c33904848c45 100644
> > > --- a/drivers/thermal/Makefile
> > > +++ b/drivers/thermal/Makefile
> > > @@ -14,6 +14,7 @@ thermal_sys-$(CONFIG_THERMAL_GOV_FAIR_SHARE)	+= fair_share.o
> > >  thermal_sys-$(CONFIG_THERMAL_GOV_BANG_BANG)	+= gov_bang_bang.o
> > >  thermal_sys-$(CONFIG_THERMAL_GOV_STEP_WISE)	+= step_wise.o
> > >  thermal_sys-$(CONFIG_THERMAL_GOV_USER_SPACE)	+= user_space.o
> > > +thermal_sys-$(CONFIG_THERMAL_GOV_POWER_ALLOCATOR)	+= power_allocator.o
> > >  
> > >  # cpufreq cooling
> > >  thermal_sys-$(CONFIG_CPU_THERMAL)	+= cpu_cooling.o
> > > diff --git a/drivers/thermal/power_allocator.c b/drivers/thermal/power_allocator.c
> > > new file mode 100644
> > > index 000000000000..09e98991efbb
> > > --- /dev/null
> > > +++ b/drivers/thermal/power_allocator.c
> > > @@ -0,0 +1,511 @@
> > > +/*
> > > + * A power allocator to manage temperature
> > > + *
> > > + * Copyright (C) 2014 ARM Ltd.
> > > + *
> > > + * This program is free software; you can redistribute it and/or modify
> > > + * it under the terms of the GNU General Public License version 2 as
> > > + * published by the Free Software Foundation.
> > > + *
> > > + * This program is distributed "as is" WITHOUT ANY WARRANTY of any
> > > + * kind, whether express or implied; without even the implied warranty
> > > + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > > + * GNU General Public License for more details.
> > > + */
> > > +
> > > +#define pr_fmt(fmt) "Power allocator: " fmt
> > > +
> > > +#include <linux/rculist.h>
> > > +#include <linux/slab.h>
> > > +#include <linux/thermal.h>
> > > +
> > > +#include "thermal_core.h"
> > > +
> > > +#define FRAC_BITS 10
> > > +#define int_to_frac(x) ((x) << FRAC_BITS)
> > > +#define frac_to_int(x) ((x) >> FRAC_BITS)
> > > +
> > > +/**
> > > + * mul_frac() - multiply two fixed-point numbers
> > > + * @x:	first multiplicand
> > > + * @y:	second multiplicand
> > > + *
> > 
> > If it is a kernel doc, needs a description.
> 
> Other parts of the kernel are more liberal in this regard,
> specially fro trivial functions like this.  Also, kernel-doc creates a
> documentation just fine:
> 
> $ scripts/kernel-doc -function mul_frac drivers/thermal/power_allocator.c | nroff -man
> mul_frac(9)                 Kernel Hacker's Manual                 mul_frac(9)
> 
> 
> 
> NAME
>        mul_frac - multiply two fixed-point numbers
> 
> SYNOPSIS
>        s64 mul_frac (s64 x, s64 y);
> 
> ARGUMENTS
>        x           first multiplicand
> 
>        y           second multiplicand
> 
> RETURN
>        the  result of multiplying two fixed-point numbers.  The result is also
>        a fixed-point number.
> 
> 
> 
> January 2015                       mul_frac                        mul_frac(9)
> 
> 
> I'll add the long description if you want to, but this is not a
> warning.
> 


As long as there is no kerneldoc warning/errors, I am fine taking it. I
must confess I haven't run kerneldoc script in your patch as I got it
with encoding scrambled, so I was just pointing the missing entries.

> > > + * Return: the result of multiplying two fixed-point numbers.  The
> > > + * result is also a fixed-point number.
> > > + */
> > > +static inline s64 mul_frac(s64 x, s64 y)
> > > +{
> > > +	return (x * y) >> FRAC_BITS;
> > > +}
> > > +
> > > +enum power_allocator_trip_levels {
> > > +	TRIP_SWITCH_ON = 0,	/* Switch on PID controller */
> > > +	TRIP_MAX_DESIRED_TEMPERATURE, /* Temperature we are controlling for */
> > > +
> > > +	THERMAL_TRIP_NUM,
> > > +};
> > > +
> > > +/**
> > > + * struct power_allocator_params - parameters for the power allocator governor
> > > + * @k_po:	Proportional parameter of the PID controller when overshooting
> > > + *		(i.e., when temperature is below the target)
> > > + * @k_pu:	Proportional parameter of the PID controller when undershooting
> > > + * @k_i:	Integral parameter of the PID controller
> > > + * @k_d:	Derivative parameter of the PID controller
> > > + * @integral_cutoff:	threshold below which the error is no longer accumulated
> > > +			in the PID controller
> > > + * @err_integral:	accumulated error in the PID controller.
> > > + * @prev_err:	error in the previous iteration of the PID controller.
> > > + *		Used to calculate the derivative term.
> > > + */
> > > +struct power_allocator_params {
> > > +	s32 k_po;
> > > +	s32 k_pu;
> > > +	s32 k_i;
> > > +	s32 k_d;
> > > +	s32 integral_cutoff;
> > > +	s64 err_integral;
> > > +	s32 prev_err;
> > > +};
> > > +
> > > +/**
> > > + * get_actor_weight() - get the weight for the power actor
> > > + * @tz:		thermal zone we are operating in
> > > + * @actor:	the power actor
> > > + *
> > 
> > 
> > ditto
> > 
> > > + * Returns: The weight inside the thermal binding parameters of the
> > 
> > s/Returns:/Return:/g
> 
> Yep.
> 
> > Please run the kernel doc script on your patches and avoid adding
> > warnings / errors.
> 
> Actually, kernel-doc doesn't complain about Returns vs Return and it
> doesn't really care if there is no description in a function as I said
> before.
> 
> Is there a better way than running "scripts/kernel-doc -function
> $FUNCTION $FILE" ?  It would be great if scripts/check-patch.pl
> checked this as well.

I usually run 
$ ./scripts/kernel-doc -text -v drivers/thermal/thermal_core.c > /dev/null

to see what it complain.

as for checkpatch.pl, well, I agree. But as it is not there, I have its
execution in my own internal scripts that I run on top of patches I
receive.

> 
> > > + * thermal zone.  If it could not be found, a default weight of 1 is
> > > + * assumed.  Weights are expressed as a FRAC_BITS (currently 10-bit)
> > > + * fixed point integer.
> > > + */
> > > +static int get_actor_weight(struct thermal_zone_device *tz,
> > > +			struct thermal_cooling_device *cdev)
> > > +{
> > > +	int i;
> > > +
> > > +	for (i = 0; i < tz->tzp->num_tbps; i++)
> > > +		if (tz->tzp->tbp[i].cdev == cdev)
> > > +			return tz->tzp->tbp[i].weight;
> > > +
> > > +	return int_to_frac(1);
> > > +}
> > > +
> > > +/**
> > > + * pid_controller() - PID controller
> > > + * @tz:	thermal zone we are operating in
> > > + * @current_temp:	the current temperature in millicelsius
> > > + * @control_temp:	the target temperature in millicelsius
> > > + * @max_allocatable_power:	maximum allocatable power for this thermal zone
> > > + *
> > > + * This PID controller increases the available power budget so that the
> > > + * temperature of the thermal zone gets as close as possible to
> > > + * @control_temp and limits the power if it exceeds it.  k_po is the
> > > + * proportional term when we are overshooting, k_pu is the
> > > + * proportional term when we are undershooting.  integral_cutoff is a
> > > + * threshold below which we stop accumulating the error.  The
> > > + * accumulated error is only valid if the requested power will make
> > > + * the system warmer.  If the system is mostly idle, there's no point
> > > + * in accumulating positive error.
> > > + *
> > > + * Return: The power budget for the next period.
> > > + */
> > > +static u32 pid_controller(struct thermal_zone_device *tz,
> > > +			unsigned long current_temp, unsigned long control_temp,
> > > +			u32 max_allocatable_power)
> > > +{
> > > +	s64 p, i, d, power_range;
> > > +	s32 err, max_power_frac;
> > > +	struct power_allocator_params *params = tz->governor_data;
> > > +
> > > +	max_power_frac = int_to_frac(max_allocatable_power);
> > > +
> > > +	err = ((s32)control_temp - (s32)current_temp);
> > > +	err = int_to_frac(err);
> > > +
> > > +	/* Calculate the proportional term */
> > > +	p = mul_frac(err < 0 ? params->k_po : params->k_pu, err);
> > > +
> > > +	/*
> > > +	 * Calculate the integral term
> > > +	 *
> > > +	 * if the error is less than cut off allow integration (but
> > > +	 * the integral is limited to max power)
> > > +	 */
> > > +	i = mul_frac(params->k_i, params->err_integral);
> > > +
> > > +	if (err < int_to_frac(params->integral_cutoff)) {
> > > +		s64 i_next = i + mul_frac(params->k_i, err);
> > > +
> > > +		if (abs64(i_next) < max_power_frac) {
> > > +			i = i_next;
> > > +			params->err_integral += err;
> > > +		}
> > > +	}
> > > +
> > > +	/*
> > > +	 * Calculate the derivative term
> > > +	 *
> > > +	 * We do err - prev_err, so with a positive k_d, a decreasing
> > > +	 * error (i.e. driving closer to the line) results in less
> > > +	 * power being applied, slowing down the controller)
> > > +	 */
> > > +	d = mul_frac(params->k_d, err - params->prev_err);
> > > +	params->prev_err = err;
> > > +
> > > +	power_range = p + i + d;
> > > +
> > > +	/* feed-forward the known sustainable dissipatable power */
> > > +	power_range = tz->tzp->sustainable_power + frac_to_int(power_range);
> > > +
> > > +	return clamp(power_range, (s64)0, (s64)max_allocatable_power);
> > > +}
> > > +
> > > +/**
> > > + * divvy_up_power() - divvy the allocated power between the actors
> > > + * @req_power:	each actor's requested power
> > > + * @max_power:	each actor's maximum available power
> > > + * @num_actors:	size of the @req_power, @max_power and @granted_power's array
> > > + * @total_req_power: sum of @req_power
> > > + * @power_range:	total allocated power
> > > + * @granted_power:	output array: each actor's granted power
> > > + *
> > > + * This function divides the total allocated power (@power_range)
> > > + * fairly between the actors.  It first tries to give each actor a
> > > + * share of the @power_range according to how much power it requested
> > > + * compared to the rest of the actors.  For example, if only one actor
> > > + * requests power, then it receives all the @power_range.  If
> > > + * three actors each requests 1mW, each receives a third of the
> > > + * @power_range.
> > > + *
> > > + * If any actor received more than their maximum power, then that
> > > + * surplus is re-divvied among the actors based on how far they are
> > > + * from their respective maximums.
> > > + *
> > > + * Granted power for each actor is written to @granted_power, which
> > > + * should've been allocated by the calling function.
> > > + */
> > > +static void divvy_up_power(u32 *req_power, u32 *max_power, int num_actors,
> > > +			u32 total_req_power, u32 power_range,
> > > +			u32 *granted_power)
> > > +{
> > > +	u32 extra_power, capped_extra_power, extra_actor_power[num_actors];
> > > +	int i;
> > > +
> > > +	if (!total_req_power) {
> > > +		/*
> > > +		 * Nobody requested anything, so just give everybody
> > > +		 * the maximum power
> > > +		 */
> > > +		for (i = 0; i < num_actors; i++)
> > > +			granted_power[i] = max_power[i];
> > > +
> > > +		return;
> > > +	}
> > > +
> > > +	capped_extra_power = 0;
> > > +	extra_power = 0;
> > > +	for (i = 0; i < num_actors; i++) {
> > > +		u64 req_range = req_power[i] * power_range;
> > > +
> > > +		granted_power[i] = div_u64(req_range, total_req_power);
> > > +
> > > +		if (granted_power[i] > max_power[i]) {
> > > +			extra_power += granted_power[i] - max_power[i];
> > > +			granted_power[i] = max_power[i];
> > 
> > shouldn't we continue here?
> 
> No, you would leave the extra_actor_power[i] uninitialized.
> 
> > > +		}
> > > +
> > > +		extra_actor_power[i] = max_power[i] - granted_power[i];
> > 
> > Do we care when max_power[i] < granted_power[i]?
> 
> That can't happen, the above "if" prevents it.
> 
> > What happens to (overflowed) extra_actor_power[i]?
> 
> extra_actor_power[i] can't overflow, it's a u32 and is asigned the
> result of substracting two u32s.  The code make sure that the minuend is
> bigger or equal than the sustraend.
> 

I see now. Maybe I need extra coffee :-)


> > > +		capped_extra_power += extra_actor_power[i];
> > > +	}
> > > +
> > > +	if (!extra_power)
> > > +		return;
> > > +
> > > +	/*
> > > +	 * Re-divvy the reclaimed extra among actors based on
> > > +	 * how far they are from the max
> > > +	 */
> > > +	extra_power = min(extra_power, capped_extra_power);
> > > +	if (capped_extra_power > 0)
> > > +		for (i = 0; i < num_actors; i++)
> > > +			granted_power[i] += (extra_actor_power[i] *
> > > +					extra_power) / capped_extra_power;
> > > +}
> > > +
> > > +static int allocate_power(struct thermal_zone_device *tz,
> > > +			unsigned long current_temp, unsigned long control_temp)
> > > +{
> > > +	struct thermal_instance *instance;
> > > +	u32 *req_power, *max_power, *granted_power;
> > > +	u32 total_req_power, max_allocatable_power;
> > > +	u32 power_range;
> > > +	int i, num_actors, ret = 0;
> > > +
> > > +	mutex_lock(&tz->lock);
> > > +
> > > +	num_actors = 0;
> > > +	list_for_each_entry(instance, &tz->thermal_instances, tz_node)
> > > +		if ((instance->trip == TRIP_MAX_DESIRED_TEMPERATURE) &&
> > > +			cdev_is_power_actor(instance->cdev))
> > > +			num_actors++;
> > > +
> > > +	req_power = devm_kcalloc(&tz->device, num_actors, sizeof(*req_power),
> > > +				GFP_KERNEL);
> > > +	if (!req_power) {
> > > +		ret = -ENOMEM;
> > > +		goto unlock;
> > > +	}
> > > +
> > > +	max_power = devm_kcalloc(&tz->device, num_actors, sizeof(*max_power),
> > > +				GFP_KERNEL);
> > > +	if (!max_power) {
> > > +		ret = -ENOMEM;
> > > +		goto free_req_power;
> > > +	}
> > > +
> > > +	granted_power = devm_kcalloc(&tz->device, num_actors,
> > > +				sizeof(*granted_power), GFP_KERNEL);
> > > +	if (!granted_power) {
> > > +		ret = -ENOMEM;
> > > +		goto free_max_power;
> > > +	}
> > > +
> > > +	i = 0;
> > > +	total_req_power = 0;
> > > +	max_allocatable_power = 0;
> > > +
> > > +	list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
> > > +		int weight;
> > > +		struct thermal_cooling_device *cdev = instance->cdev;
> > > +
> > > +		if (instance->trip != TRIP_MAX_DESIRED_TEMPERATURE)
> > > +			continue;
> > > +
> > > +		if (!cdev_is_power_actor(cdev))
> > > +			continue;
> > > +
> > > +		req_power[i] = cdev->ops->get_actual_power(cdev);
> > 
> > Is this req_power (I read as 'requested power') or actual_power? I would
> > use the later naming to avoid confusions.
> 
> We have had a lot of discussions internally about whether this should
> be the power that the device wants to consume or the power that is
> currently consuming.  The current implementation in the cpu cooling
> device uses the power that the cpu is currently drawing because it's
> very hard to get the frequency that cpufreq would assign if there were
> no limits.
> 
> I understand that it's confusing as it is, but I think it's better to
> rename get_actual_power() to get_requested_power() and explain this
> limitation in the kerneldoc for cpufreq_get_requested_power()
> 

ok.

> > > +		weight = get_actor_weight(tz, cdev);
> > > +		req_power[i] = frac_to_int(weight * req_power[i]);
> > > +		total_req_power += req_power[i];
> > 
> > ditto for total_req_power.
> > 
> > > +
> > > +		max_power[i] = power_actor_get_max_power(cdev);
> > > +		max_allocatable_power += max_power[i];
> > > +
> > > +		i++;
> > > +	}
> > > +
> > > +	power_range = pid_controller(tz, current_temp, control_temp,
> > > +				max_allocatable_power);
> > > +
> > > +	divvy_up_power(req_power, max_power, num_actors, total_req_power,
> > > +		power_range, granted_power);
> > > +
> > > +	i = 0;
> > > +	list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
> > > +		if (instance->trip != TRIP_MAX_DESIRED_TEMPERATURE)
> > > +			continue;
> > > +
> > > +		if (!cdev_is_power_actor(instance->cdev))
> > > +			continue;
> > > +
> > > +		power_actor_set_power(instance->cdev, granted_power[i]);
> > > +
> > > +		i++;
> > > +	}
> > > +
> > > +	devm_kfree(&tz->device, granted_power);
> > > +free_max_power:
> > > +	devm_kfree(&tz->device, max_power);
> > > +free_req_power:
> > > +	devm_kfree(&tz->device, req_power);
> > > +unlock:
> > > +	mutex_unlock(&tz->lock);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static int check_trips(struct thermal_zone_device *tz)
> > > +{
> > > +	int ret;
> > > +	enum thermal_trip_type type;
> > > +
> > > +	if (tz->trips < THERMAL_TRIP_NUM)
> > > +		return -EINVAL;
> > > +
> > > +	ret = tz->ops->get_trip_type(tz, TRIP_SWITCH_ON, &type);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	if (type != THERMAL_TRIP_PASSIVE)
> > > +		return -EINVAL;
> > > +
> > > +	ret = tz->ops->get_trip_type(tz, TRIP_MAX_DESIRED_TEMPERATURE, &type);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	if (type != THERMAL_TRIP_PASSIVE)
> > > +		return -EINVAL;
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static void reset_pid_controller(struct power_allocator_params *params)
> > > +{
> > > +	params->err_integral = 0;
> > > +	params->prev_err = 0;
> > > +}
> > > +
> > > +static void allow_maximum_power(struct thermal_zone_device *tz)
> > > +{
> > > +	struct thermal_instance *instance;
> > > +
> > > +	list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
> > > +		u32 max_power;
> > > +
> > > +		if ((instance->trip != TRIP_MAX_DESIRED_TEMPERATURE) ||
> > > +			(!cdev_is_power_actor(instance->cdev)))
> > > +			continue;
> > > +
> > > +		max_power = power_actor_get_max_power(instance->cdev);
> > > +		power_actor_set_power(instance->cdev, max_power);
> > > +	}
> > > +}
> > > +
> > > +/**
> > > + * power_allocator_bind() - bind the power_allocator governor to a thermal zone
> > > + * @tz:	thermal zone to bind it to
> > > + *
> > > + * Check that the thermal zone is valid for this governor, that is, it
> > > + * has two thermal trips.  If so, initialize the PID controller
> > > + * parameters and bind it to the thermal zone.
> > > + *
> > > + * Return: 0 on success, -EINVAL if the trips were invalid or -ENOMEM
> > > + * if we ran out of memory.
> > > + */
> > > +static int power_allocator_bind(struct thermal_zone_device *tz)
> > > +{
> > > +	int ret;
> > > +	struct power_allocator_params *params;
> > > +	unsigned long switch_on_temp, control_temp;
> > > +	u32 temperature_threshold;
> > > +
> > > +	ret = check_trips(tz);
> > > +	if (ret) {
> > > +		dev_err(&tz->device,
> > > +			"thermal zone %s has the wrong number of trips for this governor\n",
> > 
> > I would be more specific:
> > +			"thermal zone %s has wrong trip setup for power allocator\n",
> > 
> > 
> > Besides, in 'check_trips' you check more than number of trips.
> 
> Correct, I'll change the error message.
> 
> > > +			tz->type);
> > > +		return ret;
> > > +	}
> > > +
> > > +	if (!tz->tzp || !tz->tzp->sustainable_power) {
> > > +		dev_err(&tz->device,
> > > +			"power_allocator: missing sustainable_power\n");
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	params = devm_kzalloc(&tz->device, sizeof(*params), GFP_KERNEL);
> > > +	if (!params)
> > > +		return -ENOMEM;
> > > +
> > > +	ret = tz->ops->get_trip_temp(tz, TRIP_SWITCH_ON, &switch_on_temp);
> > > +	if (ret)
> > > +		goto free;
> > > +
> > > +	ret = tz->ops->get_trip_temp(tz, TRIP_MAX_DESIRED_TEMPERATURE,
> > > +				&control_temp);
> > > +	if (ret)
> > > +		goto free;
> > > +
> > > +	temperature_threshold = control_temp - switch_on_temp;
> > > +
> > > +	params->k_po = tz->tzp->k_po ?:
> > > +		int_to_frac(tz->tzp->sustainable_power) / temperature_threshold;
> > > +	params->k_pu = tz->tzp->k_pu ?:
> > > +		int_to_frac(2 * tz->tzp->sustainable_power) /
> > > +		temperature_threshold;
> > > +	params->k_i = tz->tzp->k_i ?: int_to_frac(10) / 1000;
> > > +	params->k_d = tz->tzp->k_d ?: int_to_frac(0);
> > > +	params->integral_cutoff = tz->tzp->integral_cutoff ?: 0;
> > > +
> > > +	reset_pid_controller(params);
> > > +
> > > +	tz->governor_data = params;
> > > +
> > > +	return 0;
> > > +
> > > +free:
> > > +	devm_kfree(&tz->device, params);
> > > +	return ret;
> > > +}
> > > +
> > > +static void power_allocator_unbind(struct thermal_zone_device *tz)
> > > +{
> > > +	dev_dbg(&tz->device, "Unbinding from thermal zone %d\n", tz->id);
> > > +	devm_kfree(&tz->device, tz->governor_data);
> > > +	tz->governor_data = NULL;
> > > +}
> > > +
> > > +static int power_allocator_throttle(struct thermal_zone_device *tz, int trip)
> > > +{
> > > +	int ret;
> > > +	unsigned long switch_on_temp, control_temp, current_temp;
> > > +	struct power_allocator_params *params = tz->governor_data;
> > > +
> > > +	/*
> > > +	 * We get called for every trip point but we only need to do
> > > +	 * our calculations once
> > > +	 */
> > > +	if (trip != TRIP_MAX_DESIRED_TEMPERATURE)
> > > +		return 0;
> > > +
> > > +	ret = thermal_zone_get_temp(tz, &current_temp);
> > > +	if (ret) {
> > > +		dev_warn(&tz->device, "Failed to get temperature: %d\n", ret);
> > > +		return ret;
> > > +	}
> > > +
> > > +	ret = tz->ops->get_trip_temp(tz, TRIP_SWITCH_ON, &switch_on_temp);
> > > +	if (ret) {
> > > +		dev_warn(&tz->device,
> > > +			"Failed to get switch on temperature: %d\n", ret);
> > > +		return ret;
> > > +	}
> > > +
> > > +	if (current_temp < switch_on_temp) {
> > > +		tz->passive = 0;
> > > +		reset_pid_controller(params);
> > > +		allow_maximum_power(tz);
> > > +		return 0;
> > > +	}
> > > +
> > > +	tz->passive = 1;
> > > +
> > > +	ret = tz->ops->get_trip_temp(tz, TRIP_MAX_DESIRED_TEMPERATURE,
> > > +				&control_temp);
> > > +	if (ret) {
> > > +		dev_warn(&tz->device,
> > > +			"Failed to get the maximum desired temperature: %d\n",
> > > +			ret);
> > > +		return ret;
> > > +	}
> > > +
> > > +	return allocate_power(tz, current_temp, control_temp);
> > > +}
> > > +
> > > +static struct thermal_governor thermal_gov_power_allocator = {
> > > +	.name		= "power_allocator",
> > > +	.bind_to_tz	= power_allocator_bind,
> > > +	.unbind_from_tz	= power_allocator_unbind,
> > > +	.throttle	= power_allocator_throttle,
> > > +};
> > > +
> > > +int thermal_gov_power_allocator_register(void)
> > > +{
> > > +	return thermal_register_governor(&thermal_gov_power_allocator);
> > > +}
> > > +
> > > +void thermal_gov_power_allocator_unregister(void)
> > > +{
> > > +	thermal_unregister_governor(&thermal_gov_power_allocator);
> > > +}
> > > diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
> > > index c490f262ea7f..4921e084c20b 100644
> > > --- a/drivers/thermal/thermal_core.c
> > > +++ b/drivers/thermal/thermal_core.c
> > > @@ -1905,7 +1905,11 @@ static int __init thermal_register_governors(void)
> > >  	if (result)
> > >  		return result;
> > >  
> > > -	return thermal_gov_user_space_register();
> > > +	result = thermal_gov_user_space_register();
> > > +	if (result)
> > > +		return result;
> > > +
> > > +	return thermal_gov_power_allocator_register();
> > >  }
> > >  
> > >  static void thermal_unregister_governors(void)
> > > @@ -1914,6 +1918,7 @@ static void thermal_unregister_governors(void)
> > >  	thermal_gov_fair_share_unregister();
> > >  	thermal_gov_bang_bang_unregister();
> > >  	thermal_gov_user_space_unregister();
> > > +	thermal_gov_power_allocator_unregister();
> > >  }
> > >  
> > >  static int __init thermal_init(void)
> > > diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h
> > > index d15d243de27a..b907be823527 100644
> > > --- a/drivers/thermal/thermal_core.h
> > > +++ b/drivers/thermal/thermal_core.h
> > > @@ -85,6 +85,14 @@ static inline int thermal_gov_user_space_register(void) { return 0; }
> > >  static inline void thermal_gov_user_space_unregister(void) {}
> > >  #endif /* CONFIG_THERMAL_GOV_USER_SPACE */
> > >  
> > > +#ifdef CONFIG_THERMAL_GOV_POWER_ALLOCATOR
> > > +int thermal_gov_power_allocator_register(void);
> > > +void thermal_gov_power_allocator_unregister(void);
> > > +#else
> > > +static inline int thermal_gov_power_allocator_register(void) { return 0; }
> > > +static inline void thermal_gov_power_allocator_unregister(void) {}
> > > +#endif /* CONFIG_THERMAL_GOV_POWER_ALLOCATOR */
> > > +
> > >  /* device tree support */
> > >  #ifdef CONFIG_THERMAL_OF
> > >  int of_parse_thermal_zones(void);
> > > diff --git a/include/linux/thermal.h b/include/linux/thermal.h
> > > index 1155457caf52..b23e019b1761 100644
> > > --- a/include/linux/thermal.h
> > > +++ b/include/linux/thermal.h
> > > @@ -61,6 +61,8 @@
> > >  #define DEFAULT_THERMAL_GOVERNOR       "fair_share"
> > >  #elif defined(CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE)
> > >  #define DEFAULT_THERMAL_GOVERNOR       "user_space"
> > > +#elif defined(CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR)
> > > +#define DEFAULT_THERMAL_GOVERNOR       "power_allocator"
> > >  #endif
> > >  
> > >  struct thermal_zone_device;
> > > @@ -255,9 +257,14 @@ struct thermal_bind_params {
> > >  
> > >  	/*
> > >  	 * This is a measure of 'how effectively these devices can
> > > -	 * cool 'this' thermal zone. The shall be determined by platform
> > > -	 * characterization. This is on a 'percentage' scale.
> > > -	 * See Documentation/thermal/sysfs-api.txt for more information.
> > > +	 * cool 'this' thermal zone. The shall be determined by
> > > +	 * platform characterization. For the fair-share governor,
> > > +	 * this is on a 'percentage' scale.  See
> > > +	 * Documentation/thermal/sysfs-api.txt for more
> > > +	 * information. For the power_allocator governor, they are
> > > +	 * relative to each other, see
> > > +	 * Documentation/thermal/power_allocator.txt for more
> > > +	 * information.
> > 
> > What happens if we register a thermal zone with relative weights, at
> > fist the user uses power allocator, but then wants to, for some reason,
> > use fair share? (or vice-versa).
> > 
> > Can't power allocator use percentages too?
> 
> The problem with percentages is that they are hard to use as they
> depend on each other.  For example, you need to know how many cooling
> devices there are and that number needs to remain fixed.  You can't
> add a new cooling device without changing the weights of all the other
> existing cooling devices.  If the thermal zone is already created it's
> hard to change the weights so you really can't add or remove cooling
> devices.
> 
> We were talking in another thread of ignoring a cooling device on some
> error.  How do you do that if the percentages must add to a hundred?
> 
> I thought that we could reuse the "weight" parameter but if you think
> we are abusing by making it mean different things for different
> governors we can create a new one instead.
> 

yeah, I am concerned about the inconsistence. If you can make both
governor to use the data standardized with same unit (percentage or
relative values), then I am fine with that. Either way: (a) make power
allocator to use percentage; or (b) make fair share to use relative
values.

> Cheers,
> Javi
> 

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 7/9] thermal: introduce the Power Allocator governor
  2015-01-06 14:18       ` Eduardo Valentin
@ 2015-01-06 14:50         ` Javi Merino
  0 siblings, 0 replies; 48+ messages in thread
From: Javi Merino @ 2015-01-06 14:50 UTC (permalink / raw)
  To: Eduardo Valentin
  Cc: linux-pm, linux-kernel, Punit Agrawal, broonie, Zhang Rui

Hi Eduardo,

On Tue, Jan 06, 2015 at 02:18:36PM +0000, Eduardo Valentin wrote:
> On Tue, Jan 06, 2015 at 01:23:42PM +0000, Javi Merino wrote:
> > On Fri, Jan 02, 2015 at 03:46:24PM +0000, Eduardo Valentin wrote:
> > > On Fri, Dec 05, 2014 at 07:04:18PM +0000, Javi Merino wrote:
> > > > diff --git a/drivers/thermal/power_allocator.c b/drivers/thermal/power_allocator.c
> > > > new file mode 100644
> > > > index 000000000000..09e98991efbb
> > > > --- /dev/null
> > > > +++ b/drivers/thermal/power_allocator.c
> > > > @@ -0,0 +1,511 @@
> > > > +/*
> > > > + * A power allocator to manage temperature
> > > > + *
> > > > + * Copyright (C) 2014 ARM Ltd.
> > > > + *
> > > > + * This program is free software; you can redistribute it and/or modify
> > > > + * it under the terms of the GNU General Public License version 2 as
> > > > + * published by the Free Software Foundation.
> > > > + *
> > > > + * This program is distributed "as is" WITHOUT ANY WARRANTY of any
> > > > + * kind, whether express or implied; without even the implied warranty
> > > > + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > > > + * GNU General Public License for more details.
> > > > + */
> > > > +
> > > > +#define pr_fmt(fmt) "Power allocator: " fmt
> > > > +
> > > > +#include <linux/rculist.h>
> > > > +#include <linux/slab.h>
> > > > +#include <linux/thermal.h>
> > > > +
> > > > +#include "thermal_core.h"
> > > > +
> > > > +#define FRAC_BITS 10
> > > > +#define int_to_frac(x) ((x) << FRAC_BITS)
> > > > +#define frac_to_int(x) ((x) >> FRAC_BITS)
> > > > +
> > > > +/**
> > > > + * mul_frac() - multiply two fixed-point numbers
> > > > + * @x:	first multiplicand
> > > > + * @y:	second multiplicand
> > > > + *
> > > 
> > > If it is a kernel doc, needs a description.
> > 
> > Other parts of the kernel are more liberal in this regard,
> > specially fro trivial functions like this.  Also, kernel-doc creates a
> > documentation just fine:
> > 
> > $ scripts/kernel-doc -function mul_frac drivers/thermal/power_allocator.c | nroff -man
> > mul_frac(9)                 Kernel Hacker's Manual                 mul_frac(9)
> > 
> > 
> > 
> > NAME
> >        mul_frac - multiply two fixed-point numbers
> > 
> > SYNOPSIS
> >        s64 mul_frac (s64 x, s64 y);
> > 
> > ARGUMENTS
> >        x           first multiplicand
> > 
> >        y           second multiplicand
> > 
> > RETURN
> >        the  result of multiplying two fixed-point numbers.  The result is also
> >        a fixed-point number.
> > 
> > 
> > 
> > January 2015                       mul_frac                        mul_frac(9)
> > 
> > 
> > I'll add the long description if you want to, but this is not a
> > warning.
> > 
> 
> 
> As long as there is no kerneldoc warning/errors, I am fine taking it. I
> must confess I haven't run kerneldoc script in your patch as I got it
> with encoding scrambled, so I was just pointing the missing entries.

Ok, I'll make sure kernel-doc doesn't scream.

> > > > + * Return: the result of multiplying two fixed-point numbers.  The
> > > > + * result is also a fixed-point number.
> > > > + */
> > > > +static inline s64 mul_frac(s64 x, s64 y)
> > > > +{
> > > > +	return (x * y) >> FRAC_BITS;
> > > > +}
> > > > +
> > > > +enum power_allocator_trip_levels {
> > > > +	TRIP_SWITCH_ON = 0,	/* Switch on PID controller */
> > > > +	TRIP_MAX_DESIRED_TEMPERATURE, /* Temperature we are controlling for */
> > > > +
> > > > +	THERMAL_TRIP_NUM,
> > > > +};
> > > > +
> > > > +/**
> > > > + * struct power_allocator_params - parameters for the power allocator governor
> > > > + * @k_po:	Proportional parameter of the PID controller when overshooting
> > > > + *		(i.e., when temperature is below the target)
> > > > + * @k_pu:	Proportional parameter of the PID controller when undershooting
> > > > + * @k_i:	Integral parameter of the PID controller
> > > > + * @k_d:	Derivative parameter of the PID controller
> > > > + * @integral_cutoff:	threshold below which the error is no longer accumulated
> > > > +			in the PID controller
> > > > + * @err_integral:	accumulated error in the PID controller.
> > > > + * @prev_err:	error in the previous iteration of the PID controller.
> > > > + *		Used to calculate the derivative term.
> > > > + */
> > > > +struct power_allocator_params {
> > > > +	s32 k_po;
> > > > +	s32 k_pu;
> > > > +	s32 k_i;
> > > > +	s32 k_d;
> > > > +	s32 integral_cutoff;
> > > > +	s64 err_integral;
> > > > +	s32 prev_err;
> > > > +};
> > > > +
> > > > +/**
> > > > + * get_actor_weight() - get the weight for the power actor
> > > > + * @tz:		thermal zone we are operating in
> > > > + * @actor:	the power actor
> > > > + *
> > > 
> > > 
> > > ditto
> > > 
> > > > + * Returns: The weight inside the thermal binding parameters of the
> > > 
> > > s/Returns:/Return:/g
> > 
> > Yep.
> > 
> > > Please run the kernel doc script on your patches and avoid adding
> > > warnings / errors.
> > 
> > Actually, kernel-doc doesn't complain about Returns vs Return and it
> > doesn't really care if there is no description in a function as I said
> > before.
> > 
> > Is there a better way than running "scripts/kernel-doc -function
> > $FUNCTION $FILE" ?  It would be great if scripts/check-patch.pl
> > checked this as well.
> 
> I usually run 
> $ ./scripts/kernel-doc -text -v drivers/thermal/thermal_core.c > /dev/null
> 
> to see what it complain.

I'll do that from now on.

> as for checkpatch.pl, well, I agree. But as it is not there, I have its
> execution in my own internal scripts that I run on top of patches I
> receive.
> 
> > 
[...]
> > > > +/**
> > > > + * divvy_up_power() - divvy the allocated power between the actors
> > > > + * @req_power:	each actor's requested power
> > > > + * @max_power:	each actor's maximum available power
> > > > + * @num_actors:	size of the @req_power, @max_power and @granted_power's array
> > > > + * @total_req_power: sum of @req_power
> > > > + * @power_range:	total allocated power
> > > > + * @granted_power:	output array: each actor's granted power
> > > > + *
> > > > + * This function divides the total allocated power (@power_range)
> > > > + * fairly between the actors.  It first tries to give each actor a
> > > > + * share of the @power_range according to how much power it requested
> > > > + * compared to the rest of the actors.  For example, if only one actor
> > > > + * requests power, then it receives all the @power_range.  If
> > > > + * three actors each requests 1mW, each receives a third of the
> > > > + * @power_range.
> > > > + *
> > > > + * If any actor received more than their maximum power, then that
> > > > + * surplus is re-divvied among the actors based on how far they are
> > > > + * from their respective maximums.
> > > > + *
> > > > + * Granted power for each actor is written to @granted_power, which
> > > > + * should've been allocated by the calling function.
> > > > + */
> > > > +static void divvy_up_power(u32 *req_power, u32 *max_power, int num_actors,
> > > > +			u32 total_req_power, u32 power_range,
> > > > +			u32 *granted_power)
> > > > +{
> > > > +	u32 extra_power, capped_extra_power, extra_actor_power[num_actors];
> > > > +	int i;
> > > > +
> > > > +	if (!total_req_power) {
> > > > +		/*
> > > > +		 * Nobody requested anything, so just give everybody
> > > > +		 * the maximum power
> > > > +		 */
> > > > +		for (i = 0; i < num_actors; i++)
> > > > +			granted_power[i] = max_power[i];
> > > > +
> > > > +		return;
> > > > +	}
> > > > +
> > > > +	capped_extra_power = 0;
> > > > +	extra_power = 0;
> > > > +	for (i = 0; i < num_actors; i++) {
> > > > +		u64 req_range = req_power[i] * power_range;
> > > > +
> > > > +		granted_power[i] = div_u64(req_range, total_req_power);
> > > > +
> > > > +		if (granted_power[i] > max_power[i]) {
> > > > +			extra_power += granted_power[i] - max_power[i];
> > > > +			granted_power[i] = max_power[i];
> > > 
> > > shouldn't we continue here?
> > 
> > No, you would leave the extra_actor_power[i] uninitialized.
> > 
> > > > +		}
> > > > +
> > > > +		extra_actor_power[i] = max_power[i] - granted_power[i];
> > > 
> > > Do we care when max_power[i] < granted_power[i]?
> > 
> > That can't happen, the above "if" prevents it.
> > 
> > > What happens to (overflowed) extra_actor_power[i]?
> > 
> > extra_actor_power[i] can't overflow, it's a u32 and is asigned the
> > result of substracting two u32s.  The code make sure that the minuend is
> > bigger or equal than the sustraend.
> > 
> 
> I see now. Maybe I need extra coffee :-)

:)

[...]
> > > > diff --git a/include/linux/thermal.h b/include/linux/thermal.h
> > > > index 1155457caf52..b23e019b1761 100644
> > > > --- a/include/linux/thermal.h
> > > > +++ b/include/linux/thermal.h
> > > > @@ -61,6 +61,8 @@
> > > >  #define DEFAULT_THERMAL_GOVERNOR       "fair_share"
> > > >  #elif defined(CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE)
> > > >  #define DEFAULT_THERMAL_GOVERNOR       "user_space"
> > > > +#elif defined(CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR)
> > > > +#define DEFAULT_THERMAL_GOVERNOR       "power_allocator"
> > > >  #endif
> > > >  
> > > >  struct thermal_zone_device;
> > > > @@ -255,9 +257,14 @@ struct thermal_bind_params {
> > > >  
> > > >  	/*
> > > >  	 * This is a measure of 'how effectively these devices can
> > > > -	 * cool 'this' thermal zone. The shall be determined by platform
> > > > -	 * characterization. This is on a 'percentage' scale.
> > > > -	 * See Documentation/thermal/sysfs-api.txt for more information.
> > > > +	 * cool 'this' thermal zone. The shall be determined by
> > > > +	 * platform characterization. For the fair-share governor,
> > > > +	 * this is on a 'percentage' scale.  See
> > > > +	 * Documentation/thermal/sysfs-api.txt for more
> > > > +	 * information. For the power_allocator governor, they are
> > > > +	 * relative to each other, see
> > > > +	 * Documentation/thermal/power_allocator.txt for more
> > > > +	 * information.
> > > 
> > > What happens if we register a thermal zone with relative weights, at
> > > fist the user uses power allocator, but then wants to, for some reason,
> > > use fair share? (or vice-versa).
> > > 
> > > Can't power allocator use percentages too?
> > 
> > The problem with percentages is that they are hard to use as they
> > depend on each other.  For example, you need to know how many cooling
> > devices there are and that number needs to remain fixed.  You can't
> > add a new cooling device without changing the weights of all the other
> > existing cooling devices.  If the thermal zone is already created it's
> > hard to change the weights so you really can't add or remove cooling
> > devices.
> > 
> > We were talking in another thread of ignoring a cooling device on some
> > error.  How do you do that if the percentages must add to a hundred?
> > 
> > I thought that we could reuse the "weight" parameter but if you think
> > we are abusing by making it mean different things for different
> > governors we can create a new one instead.
> > 
> 
> yeah, I am concerned about the inconsistence. If you can make both
> governor to use the data standardized with same unit (percentage or
> relative values), then I am fine with that. Either way: (a) make power
> allocator to use percentage; or (b) make fair share to use relative
> values.

Ok, I'll send a path for (b) then.

Cheers,
Javi

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 4/9] thermal: let governors have private data for each thermal zone
  2014-12-08  4:11   ` Zhang Rui
@ 2015-01-23 14:33     ` Javi Merino
  0 siblings, 0 replies; 48+ messages in thread
From: Javi Merino @ 2015-01-23 14:33 UTC (permalink / raw)
  To: Zhang Rui
  Cc: linux-pm, linux-kernel, Punit Agrawal, broonie, Eduardo Valentin

Hi Rui,

On Mon, Dec 08, 2014 at 04:11:32AM +0000, Zhang Rui wrote:
> On Fri, 2014-12-05 at 19:04 +0000, Javi Merino wrote:
> > A governor may need to store its current state between calls to
> > throttle().  That state depends on the thermal zone, so store it as
> > private data in struct thermal_zone_device.
> > 
> > The governors may have two new ops: bind_to_tz() and unbind_from_tz().
> > When provided, these functions let governors do some initialization
> > and teardown when they are bound/unbound to a tz and possibly store that
> > information in the governor_data field of the struct
> > thermal_zone_device.
> > 
> > Cc: Zhang Rui <rui.zhang@intel.com>
> > Cc: Eduardo Valentin <edubezval@gmail.com>
> > Signed-off-by: Javi Merino <javi.merino@arm.com>
> 
> applied.
> 
> thanks,
> rui

Where can I find it?  Your next branch in git.kernel.org doesn't have
it.  I'm preparing an update of this series and I wanted to based it
on a branch that had this commit applied.  Cheers,
Javi

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 6/9] thermal: cpu_cooling: implement the power cooling device API
  2014-12-05 19:04 ` [RFC PATCH v6 6/9] thermal: cpu_cooling: implement the power cooling device API Javi Merino
  2014-12-08  5:49   ` Viresh Kumar
@ 2015-01-28  5:23   ` Eduardo Valentin
  2015-01-29 11:19     ` Punit Agrawal
  1 sibling, 1 reply; 48+ messages in thread
From: Eduardo Valentin @ 2015-01-28  5:23 UTC (permalink / raw)
  To: Javi Merino; +Cc: linux-pm, linux-kernel, punit.agrawal, broonie, Zhang Rui

[-- Attachment #1: Type: text/plain, Size: 3041 bytes --]


Hello Javi,

On Fri, Dec 05, 2014 at 07:04:17PM +0000, Javi Merino wrote:
> Add a basic power model to the cpu cooling device to implement the
> power cooling device API.  The power model uses the current frequency,
> current load and OPPs for the power calculations.  The cpus must have
> registered their OPPs using the OPP library.
> 
> Cc: Zhang Rui <rui.zhang@intel.com>
> Cc: Eduardo Valentin <edubezval@gmail.com>
> Signed-off-by: Punit Agrawal <punit.agrawal@arm.com>
> Signed-off-by: Javi Merino <javi.merino@arm.com>

<big cut>

> +
> +/**
> + * get_load() - get load for a cpu since last updated
> + * @cpufreq_device:	&struct cpufreq_cooling_device for this cpu
> + * @cpu:	cpu number
> + *
> + * Return: The average load of cpu @cpu in percentage since this
> + * function was last called.
> + */
> +static u32 get_load(struct cpufreq_cooling_device *cpufreq_device, int cpu)
> +{
> +	u32 load;
> +	u64 now, now_idle, delta_time, delta_idle;
> +
> +	now_idle = get_cpu_idle_time(cpu, &now, 0);
> +	delta_idle = now_idle - cpufreq_device->time_in_idle[cpu];
> +	delta_time = now - cpufreq_device->time_in_idle_timestamp[cpu];
> +
> +	if (delta_time <= delta_idle)
> +		load = 0;
> +	else
> +		load = div64_u64(100 * (delta_time - delta_idle), delta_time);
> +
> +	cpufreq_device->time_in_idle[cpu] = now_idle;
> +	cpufreq_device->time_in_idle_timestamp[cpu] = now;
> +
> +	return load;
> +}

<cut>

>  
> +/**
> + * cpufreq_get_actual_power() - get the current power
> + * @cdev:	&thermal_cooling_device pointer
> + *
> + * Return the current power consumption of the cpus in milliwatts.
> + */
> +static u32 cpufreq_get_actual_power(struct thermal_cooling_device *cdev)
> +{
> +	unsigned long freq;
> +	int cpu;
> +	u32 static_power, dynamic_power, total_load = 0;
> +	struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
> +
> +	freq = cpufreq_quick_get(cpumask_any(&cpufreq_device->allowed_cpus));
> +
> +	for_each_cpu(cpu, &cpufreq_device->allowed_cpus) {
> +		u32 load;
> +
> +		if (cpu_online(cpu))
> +			load = get_load(cpufreq_device, cpu);
> +		else
> +			load = 0;
> +
> +		total_load += load;
> +	}
> +
> +	cpufreq_device->last_load = total_load;
> +
> +	static_power = get_static_power(cpufreq_device, freq);
> +	dynamic_power = get_dynamic_power(cpufreq_device, freq);
> +
> +	return static_power + dynamic_power;
> +}

With respect to load computation vs. frequency usage vs. power
estimation, while getting actual power for a given interval T. What if
in interval T, we have used, say, 3 different cpu frequencies, and the
load on the first was 50%, on the second 80%, and on the last frequency,
the load was 60%, what should be the right load value for computing the
actual power? 

I mean, we are using the total idle time for a given interval, but 1 -
idle not always seams to reflect actual load on different opps, if opps
change over time within T time interval window.

BR,


BR,

Eduardo Valentin

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 6/9] thermal: cpu_cooling: implement the power cooling device API
  2015-01-29 11:19     ` Punit Agrawal
@ 2015-01-29  0:15       ` Eduardo Valentin
  2015-01-29 19:06         ` Javi Merino
  0 siblings, 1 reply; 48+ messages in thread
From: Eduardo Valentin @ 2015-01-29  0:15 UTC (permalink / raw)
  To: Punit Agrawal; +Cc: Javi Merino, linux-pm, linux-kernel, broonie, Zhang Rui

:x
> Hi Eduardo,
> 
> Eduardo Valentin <edubezval@gmail.com> writes:
> 
> > Hello Javi,
> >
> > On Fri, Dec 05, 2014 at 07:04:17PM +0000, Javi Merino wrote:
> >> Add a basic power model to the cpu cooling device to implement the
> >> power cooling device API.  The power model uses the current frequency,
> >> current load and OPPs for the power calculations.  The cpus must have
> >> registered their OPPs using the OPP library.
> >> 
> >> Cc: Zhang Rui <rui.zhang@intel.com>
> >> Cc: Eduardo Valentin <edubezval@gmail.com>
> >> Signed-off-by: Punit Agrawal <punit.agrawal@arm.com>
> >> Signed-off-by: Javi Merino <javi.merino@arm.com>
> >
> > <big cut>
> >
> >> +
> >> +/**
> >> + * get_load() - get load for a cpu since last updated
> >> + * @cpufreq_device:	&struct cpufreq_cooling_device for this cpu
> >> + * @cpu:	cpu number
> >> + *
> >> + * Return: The average load of cpu @cpu in percentage since this
> >> + * function was last called.
> >> + */
> >> +static u32 get_load(struct cpufreq_cooling_device *cpufreq_device, int cpu)
> >> +{
> >> +	u32 load;
> >> +	u64 now, now_idle, delta_time, delta_idle;
> >> +
> >> +	now_idle = get_cpu_idle_time(cpu, &now, 0);
> >> +	delta_idle = now_idle - cpufreq_device->time_in_idle[cpu];
> >> +	delta_time = now - cpufreq_device->time_in_idle_timestamp[cpu];
> >> +
> >> +	if (delta_time <= delta_idle)
> >> +		load = 0;
> >> +	else
> >> +		load = div64_u64(100 * (delta_time - delta_idle), delta_time);
> >> +
> >> +	cpufreq_device->time_in_idle[cpu] = now_idle;
> >> +	cpufreq_device->time_in_idle_timestamp[cpu] = now;
> >> +
> >> +	return load;
> >> +}
> >
> > <cut>
> >
> >>  
> >> +/**
> >> + * cpufreq_get_actual_power() - get the current power
> >> + * @cdev:	&thermal_cooling_device pointer
> >> + *
> >> + * Return the current power consumption of the cpus in milliwatts.
> >> + */
> >> +static u32 cpufreq_get_actual_power(struct thermal_cooling_device *cdev)
> >> +{
> >> +	unsigned long freq;
> >> +	int cpu;
> >> +	u32 static_power, dynamic_power, total_load = 0;
> >> +	struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
> >> +
> >> +	freq = cpufreq_quick_get(cpumask_any(&cpufreq_device->allowed_cpus));
> >> +
> >> +	for_each_cpu(cpu, &cpufreq_device->allowed_cpus) {
> >> +		u32 load;
> >> +
> >> +		if (cpu_online(cpu))
> >> +			load = get_load(cpufreq_device, cpu);
> >> +		else
> >> +			load = 0;
> >> +
> >> +		total_load += load;
> >> +	}
> >> +
> >> +	cpufreq_device->last_load = total_load;
> >> +
> >> +	static_power = get_static_power(cpufreq_device, freq);
> >> +	dynamic_power = get_dynamic_power(cpufreq_device, freq);
> >> +
> >> +	return static_power + dynamic_power;
> >> +}
> >
> > With respect to load computation vs. frequency usage vs. power
> > estimation, while getting actual power for a given interval T. What if
> > in interval T, we have used, say, 3 different cpu frequencies, and the
> > load on the first was 50%, on the second 80%, and on the last frequency,
> > the load was 60%, what should be the right load value for computing the
> > actual power? 
> >
> > I mean, we are using the total idle time for a given interval, but 1 -
> > idle not always seams to reflect actual load on different opps, if opps
> > change over time within T time interval window.
> 
> The value returned by cpufreq_get_actual_power is used as a proxy for
> the estimate of the requested power of the actor for the next window
> duration. Even though the frequency might have changed in the previous
> period, the current frequency reflects the latest information about the
> required performance. As it is an estimate, and to avoid making the
> power calculations more complicated, we used utilisation (1 - idle time)
> to calculate the request. The estimate for the T+1 period becomes more
> accurate as the load stabilises.
> 
> In our testing on different workloads using 100ms as the polling period
> for thermal control, we didn't see any problems arising from the use of
> this definition of utilisation.
> 
> Having said that, there are a number of ways to improve the accuracy of
> the power calculations. As part of investigating the effects of
> improving model accuracy and it's effect on thermal control and
> performance, we plan to look at fine-grained frequency and load tracking
> once the initial set of patches are merged.

In this case, I believe we must mark the code at least with a TODO or
REVISIT mark. Can we add the above comments within a REVISIT: mark in
this part of the code?

> 
> Cheers,
> Punit
> 
> >
> > BR,
> >
> >
> > BR,
> >
> > Eduardo Valentin

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 6/9] thermal: cpu_cooling: implement the power cooling device API
  2015-01-28  5:23   ` Eduardo Valentin
@ 2015-01-29 11:19     ` Punit Agrawal
  2015-01-29  0:15       ` Eduardo Valentin
  0 siblings, 1 reply; 48+ messages in thread
From: Punit Agrawal @ 2015-01-29 11:19 UTC (permalink / raw)
  To: Eduardo Valentin; +Cc: Javi Merino, linux-pm, linux-kernel, broonie, Zhang Rui

Hi Eduardo,

Eduardo Valentin <edubezval@gmail.com> writes:

> Hello Javi,
>
> On Fri, Dec 05, 2014 at 07:04:17PM +0000, Javi Merino wrote:
>> Add a basic power model to the cpu cooling device to implement the
>> power cooling device API.  The power model uses the current frequency,
>> current load and OPPs for the power calculations.  The cpus must have
>> registered their OPPs using the OPP library.
>> 
>> Cc: Zhang Rui <rui.zhang@intel.com>
>> Cc: Eduardo Valentin <edubezval@gmail.com>
>> Signed-off-by: Punit Agrawal <punit.agrawal@arm.com>
>> Signed-off-by: Javi Merino <javi.merino@arm.com>
>
> <big cut>
>
>> +
>> +/**
>> + * get_load() - get load for a cpu since last updated
>> + * @cpufreq_device:	&struct cpufreq_cooling_device for this cpu
>> + * @cpu:	cpu number
>> + *
>> + * Return: The average load of cpu @cpu in percentage since this
>> + * function was last called.
>> + */
>> +static u32 get_load(struct cpufreq_cooling_device *cpufreq_device, int cpu)
>> +{
>> +	u32 load;
>> +	u64 now, now_idle, delta_time, delta_idle;
>> +
>> +	now_idle = get_cpu_idle_time(cpu, &now, 0);
>> +	delta_idle = now_idle - cpufreq_device->time_in_idle[cpu];
>> +	delta_time = now - cpufreq_device->time_in_idle_timestamp[cpu];
>> +
>> +	if (delta_time <= delta_idle)
>> +		load = 0;
>> +	else
>> +		load = div64_u64(100 * (delta_time - delta_idle), delta_time);
>> +
>> +	cpufreq_device->time_in_idle[cpu] = now_idle;
>> +	cpufreq_device->time_in_idle_timestamp[cpu] = now;
>> +
>> +	return load;
>> +}
>
> <cut>
>
>>  
>> +/**
>> + * cpufreq_get_actual_power() - get the current power
>> + * @cdev:	&thermal_cooling_device pointer
>> + *
>> + * Return the current power consumption of the cpus in milliwatts.
>> + */
>> +static u32 cpufreq_get_actual_power(struct thermal_cooling_device *cdev)
>> +{
>> +	unsigned long freq;
>> +	int cpu;
>> +	u32 static_power, dynamic_power, total_load = 0;
>> +	struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
>> +
>> +	freq = cpufreq_quick_get(cpumask_any(&cpufreq_device->allowed_cpus));
>> +
>> +	for_each_cpu(cpu, &cpufreq_device->allowed_cpus) {
>> +		u32 load;
>> +
>> +		if (cpu_online(cpu))
>> +			load = get_load(cpufreq_device, cpu);
>> +		else
>> +			load = 0;
>> +
>> +		total_load += load;
>> +	}
>> +
>> +	cpufreq_device->last_load = total_load;
>> +
>> +	static_power = get_static_power(cpufreq_device, freq);
>> +	dynamic_power = get_dynamic_power(cpufreq_device, freq);
>> +
>> +	return static_power + dynamic_power;
>> +}
>
> With respect to load computation vs. frequency usage vs. power
> estimation, while getting actual power for a given interval T. What if
> in interval T, we have used, say, 3 different cpu frequencies, and the
> load on the first was 50%, on the second 80%, and on the last frequency,
> the load was 60%, what should be the right load value for computing the
> actual power? 
>
> I mean, we are using the total idle time for a given interval, but 1 -
> idle not always seams to reflect actual load on different opps, if opps
> change over time within T time interval window.

The value returned by cpufreq_get_actual_power is used as a proxy for
the estimate of the requested power of the actor for the next window
duration. Even though the frequency might have changed in the previous
period, the current frequency reflects the latest information about the
required performance. As it is an estimate, and to avoid making the
power calculations more complicated, we used utilisation (1 - idle time)
to calculate the request. The estimate for the T+1 period becomes more
accurate as the load stabilises.

In our testing on different workloads using 100ms as the polling period
for thermal control, we didn't see any problems arising from the use of
this definition of utilisation.

Having said that, there are a number of ways to improve the accuracy of
the power calculations. As part of investigating the effects of
improving model accuracy and it's effect on thermal control and
performance, we plan to look at fine-grained frequency and load tracking
once the initial set of patches are merged.

Cheers,
Punit

>
> BR,
>
>
> BR,
>
> Eduardo Valentin

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 6/9] thermal: cpu_cooling: implement the power cooling device API
  2015-01-29  0:15       ` Eduardo Valentin
@ 2015-01-29 19:06         ` Javi Merino
  0 siblings, 0 replies; 48+ messages in thread
From: Javi Merino @ 2015-01-29 19:06 UTC (permalink / raw)
  To: Eduardo Valentin
  Cc: Punit Agrawal, linux-pm, linux-kernel, broonie, Zhang Rui

On Thu, Jan 29, 2015 at 12:15:07AM +0000, Eduardo Valentin wrote:
> > Hi Eduardo,
> > 
> > Eduardo Valentin <edubezval@gmail.com> writes:
> > 
> > > Hello Javi,
> > >
> > > On Fri, Dec 05, 2014 at 07:04:17PM +0000, Javi Merino wrote:
> > >> Add a basic power model to the cpu cooling device to implement the
> > >> power cooling device API.  The power model uses the current frequency,
> > >> current load and OPPs for the power calculations.  The cpus must have
> > >> registered their OPPs using the OPP library.
> > >> 
> > >> Cc: Zhang Rui <rui.zhang@intel.com>
> > >> Cc: Eduardo Valentin <edubezval@gmail.com>
> > >> Signed-off-by: Punit Agrawal <punit.agrawal@arm.com>
> > >> Signed-off-by: Javi Merino <javi.merino@arm.com>
> > >
> > > <big cut>
> > >
> > >> +
> > >> +/**
> > >> + * get_load() - get load for a cpu since last updated
> > >> + * @cpufreq_device:	&struct cpufreq_cooling_device for this cpu
> > >> + * @cpu:	cpu number
> > >> + *
> > >> + * Return: The average load of cpu @cpu in percentage since this
> > >> + * function was last called.
> > >> + */
> > >> +static u32 get_load(struct cpufreq_cooling_device *cpufreq_device, int cpu)
> > >> +{
> > >> +	u32 load;
> > >> +	u64 now, now_idle, delta_time, delta_idle;
> > >> +
> > >> +	now_idle = get_cpu_idle_time(cpu, &now, 0);
> > >> +	delta_idle = now_idle - cpufreq_device->time_in_idle[cpu];
> > >> +	delta_time = now - cpufreq_device->time_in_idle_timestamp[cpu];
> > >> +
> > >> +	if (delta_time <= delta_idle)
> > >> +		load = 0;
> > >> +	else
> > >> +		load = div64_u64(100 * (delta_time - delta_idle), delta_time);
> > >> +
> > >> +	cpufreq_device->time_in_idle[cpu] = now_idle;
> > >> +	cpufreq_device->time_in_idle_timestamp[cpu] = now;
> > >> +
> > >> +	return load;
> > >> +}
> > >
> > > <cut>
> > >
> > >>  
> > >> +/**
> > >> + * cpufreq_get_actual_power() - get the current power
> > >> + * @cdev:	&thermal_cooling_device pointer
> > >> + *
> > >> + * Return the current power consumption of the cpus in milliwatts.
> > >> + */
> > >> +static u32 cpufreq_get_actual_power(struct thermal_cooling_device *cdev)
> > >> +{
> > >> +	unsigned long freq;
> > >> +	int cpu;
> > >> +	u32 static_power, dynamic_power, total_load = 0;
> > >> +	struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
> > >> +
> > >> +	freq = cpufreq_quick_get(cpumask_any(&cpufreq_device->allowed_cpus));
> > >> +
> > >> +	for_each_cpu(cpu, &cpufreq_device->allowed_cpus) {
> > >> +		u32 load;
> > >> +
> > >> +		if (cpu_online(cpu))
> > >> +			load = get_load(cpufreq_device, cpu);
> > >> +		else
> > >> +			load = 0;
> > >> +
> > >> +		total_load += load;
> > >> +	}
> > >> +
> > >> +	cpufreq_device->last_load = total_load;
> > >> +
> > >> +	static_power = get_static_power(cpufreq_device, freq);
> > >> +	dynamic_power = get_dynamic_power(cpufreq_device, freq);
> > >> +
> > >> +	return static_power + dynamic_power;
> > >> +}
> > >
> > > With respect to load computation vs. frequency usage vs. power
> > > estimation, while getting actual power for a given interval T. What if
> > > in interval T, we have used, say, 3 different cpu frequencies, and the
> > > load on the first was 50%, on the second 80%, and on the last frequency,
> > > the load was 60%, what should be the right load value for computing the
> > > actual power? 
> > >
> > > I mean, we are using the total idle time for a given interval, but 1 -
> > > idle not always seams to reflect actual load on different opps, if opps
> > > change over time within T time interval window.
> > 
> > The value returned by cpufreq_get_actual_power is used as a proxy for
> > the estimate of the requested power of the actor for the next window
> > duration. Even though the frequency might have changed in the previous
> > period, the current frequency reflects the latest information about the
> > required performance. As it is an estimate, and to avoid making the
> > power calculations more complicated, we used utilisation (1 - idle time)
> > to calculate the request. The estimate for the T+1 period becomes more
> > accurate as the load stabilises.
> > 
> > In our testing on different workloads using 100ms as the polling period
> > for thermal control, we didn't see any problems arising from the use of
> > this definition of utilisation.
> > 
> > Having said that, there are a number of ways to improve the accuracy of
> > the power calculations. As part of investigating the effects of
> > improving model accuracy and it's effect on thermal control and
> > performance, we plan to look at fine-grained frequency and load tracking
> > once the initial set of patches are merged.
> 
> In this case, I believe we must mark the code at least with a TODO or
> REVISIT mark. Can we add the above comments within a REVISIT: mark in
> this part of the code?

Ok, we will add a comment that summarizes this discussion around this
area of code, acknowledging the simplification and hinting that we
will look into improving it.

Cheers,
Javi

^ permalink raw reply	[flat|nested] 48+ messages in thread

end of thread, other threads:[~2015-01-29 19:06 UTC | newest]

Thread overview: 48+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-12-05 19:04 [RFC PATCH v6 0/9] The power allocator thermal governor Javi Merino
2014-12-05 19:04 ` [RFC PATCH v6 1/9] tracing: Add array printing helpers Javi Merino
2014-12-08 14:39   ` Dave P Martin
2014-12-08 15:42   ` Steven Rostedt
2014-12-08 16:04     ` Dave P Martin
2014-12-10 10:52       ` Javi Merino
2014-12-05 19:04 ` [RFC PATCH v6 2/9] tools lib traceevent: Generalize numeric argument Javi Merino
2014-12-05 19:04 ` [RFC PATCH v6 3/9] tools lib traceevent: Add support for __print_u{8,16,32,64}_array() Javi Merino
2014-12-05 19:04 ` [RFC PATCH v6 4/9] thermal: let governors have private data for each thermal zone Javi Merino
2014-12-08  4:11   ` Zhang Rui
2015-01-23 14:33     ` Javi Merino
2014-12-05 19:04 ` [RFC PATCH v6 5/9] thermal: extend the cooling device API to include power information Javi Merino
2014-12-23 15:14   ` Eduardo Valentin
2015-01-05 15:37     ` Javi Merino
2015-01-05 21:04       ` Eduardo Valentin
2015-01-06 10:34         ` Javi Merino
2015-01-06 13:08           ` Eduardo Valentin
2014-12-05 19:04 ` [RFC PATCH v6 6/9] thermal: cpu_cooling: implement the power cooling device API Javi Merino
2014-12-08  5:49   ` Viresh Kumar
2014-12-08 12:50     ` Javi Merino
2014-12-08 13:31       ` Viresh Kumar
2014-12-08 14:22         ` Javi Merino
2014-12-09  1:59           ` Viresh Kumar
2014-12-09 10:32             ` Javi Merino
2014-12-09 10:36               ` Viresh Kumar
2014-12-09 11:00                 ` Javi Merino
2014-12-09 11:06                   ` Viresh Kumar
2014-12-09 11:23                     ` Javi Merino
2015-01-02 14:37                   ` Eduardo Valentin
2015-01-05 16:53                     ` Javi Merino
2015-01-05 20:44                       ` Eduardo Valentin
2015-01-06 11:01                         ` Javi Merino
2015-01-28  5:23   ` Eduardo Valentin
2015-01-29 11:19     ` Punit Agrawal
2015-01-29  0:15       ` Eduardo Valentin
2015-01-29 19:06         ` Javi Merino
2014-12-05 19:04 ` [RFC PATCH v6 7/9] thermal: introduce the Power Allocator governor Javi Merino
2015-01-02 15:46   ` Eduardo Valentin
2015-01-06 13:23     ` Javi Merino
2015-01-06 14:18       ` Eduardo Valentin
2015-01-06 14:50         ` Javi Merino
2015-01-02 15:51   ` Eduardo Valentin
2014-12-05 19:04 ` [RFC PATCH v6 8/9] thermal: add trace events to the power allocator governor Javi Merino
2014-12-05 19:04 ` [RFC PATCH v6 9/9] of: thermal: Introduce sustainable power for a thermal zone Javi Merino
2015-01-02 15:53   ` Eduardo Valentin
2015-01-06  9:42     ` Javi Merino
2015-01-06 13:13       ` Eduardo Valentin
2015-01-06 13:29         ` Javi Merino

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.