diff mbox

[1/2] thermal: IPA: add new divvy-up algorithm

Message ID 1494250802-6663-2-git-send-email-lukasz.luba@arm.com (mailing list archive)
State Not Applicable, archived
Headers show

Commit Message

Lukasz Luba May 8, 2017, 1:40 p.m. UTC
The IPA algorithm grants actors with some extra power when the following
conditions are meet:
- there is at least one actor which got more power than it's max_power
  value,
- there is at least one actor which is capable to receive some more power
  (it's requested power is below max_power value). The IPA grants this
extra power to all capable actors.

Before this commit the algorithm did not take into account the actor's type
(cpu or devfreq), so the extra power was shared fairly to all capable
actors. Therefore, the extra power can be granted to some actor which is
not related to this higher computation request.

Example related to the previous algorithm: Let's focus on ARM big.LITTLE
platform which has two cooling devices (one per cluster). If the big CPU
cluster gets more power that it can consume, the power above the limit is
cut from the big and spread between other actors.  In this case even the
GPU gets some extra power, but it does not need it and cannot help to
offload the big CPU.  The CPU overall performance suffers.

This new algorithm takes into account the actor's type and constructs
families/groups - which are containers for actors of the same type: CPUfreq
or devfreq. Currently it supports only these two families/groups.  The
families/groups are created based on cooling device name (string matching).

The algorithm calculates extra power in two phases: Phase 1: Calculates the
power budget for each group (CPU or devfreq devices).  Phase 2: The extra
power in each family/group is shared to the same actor's family/group
members.  Phase 3: If there is still some extra power (left because the
family/group actors were not able to consume it), it is shared fairly to
all of the capable actors in the system (so in this round, the same idea to
the former algorithm).

Example for current proposed algorithm: If the 'cpu type' actor gives some
extra power to the 'cpu type' family/group pool, it is shared fairly among
capable actors in this family/group ('cpu type'). If some extra power left,
it will be granted to 'devfreq type' actors which are capable to consume
it.

Suggested-by: Alex Wang <alex.wang@spreadtrum.com>
Suggested-by: Xin Wang <Xin.Wang@arm.com>
Signed-off-by: Lukasz Luba <lukasz.luba@arm.com>
---
 drivers/thermal/power_allocator.c | 174 ++++++++++++++++++++++++++++++++------
 1 file changed, 150 insertions(+), 24 deletions(-)

Comments

kernel test robot May 9, 2017, 9:43 a.m. UTC | #1
Hi Lukasz,

[auto build test ERROR on thermal/next]
[also build test ERROR on v4.11 next-20170509]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Lukasz-Luba/thermal-power-allocator-add-new-divvy-up-algorithm/20170509-032607
base:   https://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux.git next
config: frv-allyesconfig (attached as .config)
compiler: frv-linux-gcc (GCC) 6.2.0
reproduce:
        wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=frv 

All errors (new ones prefixed by >>):

   frv-linux-ld: Warning: size of symbol `sys_mremap' changed from 40 in kernel/built-in.o to 1220 in mm/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_inotify_init' changed from 40 in kernel/built-in.o to 44 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_eventfd2' changed from 40 in kernel/built-in.o to 156 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_userfaultfd' changed from 40 in kernel/built-in.o to 356 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_io_submit' changed from 40 in kernel/built-in.o to 1836 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_lookup_dcookie' changed from 40 in kernel/built-in.o to 456 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_fanotify_init' changed from 40 in kernel/built-in.o to 676 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_epoll_create' changed from 40 in kernel/built-in.o to 60 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_timerfd_gettime' changed from 40 in kernel/built-in.o to 520 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_name_to_handle_at' changed from 40 in kernel/built-in.o to 696 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_epoll_wait' changed from 40 in kernel/built-in.o to 1120 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_io_setup' changed from 40 in kernel/built-in.o to 2636 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_inotify_init1' changed from 40 in kernel/built-in.o to 356 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_io_destroy' changed from 40 in kernel/built-in.o to 296 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_signalfd' changed from 40 in kernel/built-in.o to 44 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_uselib' changed from 40 in kernel/built-in.o to 492 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_inotify_rm_watch' changed from 40 in kernel/built-in.o to 216 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_inotify_add_watch' changed from 40 in kernel/built-in.o to 828 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_timerfd_settime' changed from 40 in kernel/built-in.o to 1256 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_bdflush' changed from 40 in kernel/built-in.o to 136 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_epoll_pwait' changed from 40 in kernel/built-in.o to 396 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_quotactl' changed from 40 in kernel/built-in.o to 2732 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_epoll_ctl' changed from 40 in kernel/built-in.o to 3184 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_timerfd_create' changed from 40 in kernel/built-in.o to 480 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_io_getevents' changed from 40 in kernel/built-in.o to 832 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_epoll_create1' changed from 40 in kernel/built-in.o to 408 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_io_cancel' changed from 40 in kernel/built-in.o to 412 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_flock' changed from 40 in kernel/built-in.o to 488 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_signalfd4' changed from 40 in kernel/built-in.o to 512 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_sysfs' changed from 40 in kernel/built-in.o to 496 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_fanotify_mark' changed from 40 in kernel/built-in.o to 1560 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_execveat' changed from 40 in kernel/built-in.o to 140 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_eventfd' changed from 40 in kernel/built-in.o to 44 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_copy_file_range' changed from 40 in kernel/built-in.o to 756 in fs/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_msgget' changed from 40 in kernel/built-in.o to 72 in ipc/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_mq_getsetattr' changed from 40 in kernel/built-in.o to 620 in ipc/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_mq_unlink' changed from 40 in kernel/built-in.o to 320 in ipc/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_shmdt' changed from 40 in kernel/built-in.o to 472 in ipc/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_msgrcv' changed from 40 in kernel/built-in.o to 48 in ipc/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_mq_open' changed from 40 in kernel/built-in.o to 772 in ipc/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_semget' changed from 40 in kernel/built-in.o to 116 in ipc/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_mq_timedsend' changed from 40 in kernel/built-in.o to 768 in ipc/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_semctl' changed from 40 in kernel/built-in.o to 700 in ipc/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_mq_notify' changed from 40 in kernel/built-in.o to 1152 in ipc/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_semtimedop' changed from 40 in kernel/built-in.o to 3560 in ipc/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_shmctl' changed from 40 in kernel/built-in.o to 620 in ipc/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_mq_timedreceive' changed from 40 in kernel/built-in.o to 1204 in ipc/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_msgsnd' changed from 40 in kernel/built-in.o to 116 in ipc/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_semop' changed from 40 in kernel/built-in.o to 44 in ipc/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_shmat' changed from 40 in kernel/built-in.o to 64 in ipc/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_msgctl' changed from 40 in kernel/built-in.o to 188 in ipc/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_shmget' changed from 40 in kernel/built-in.o to 76 in ipc/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_ipc' changed from 40 in kernel/built-in.o to 616 in ipc/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_add_key' changed from 40 in kernel/built-in.o to 556 in security/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_request_key' changed from 40 in kernel/built-in.o to 404 in security/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_keyctl' changed from 40 in kernel/built-in.o to 388 in security/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_ioprio_set' changed from 40 in kernel/built-in.o to 692 in block/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_ioprio_get' changed from 40 in kernel/built-in.o to 792 in block/built-in.o
   frv-linux-ld: Warning: size of symbol `pcibios_setup' changed from 88 in arch/frv/mb93090-mb00/built-in.o to 36 in drivers/built-in.o
   frv-linux-ld: Warning: size of symbol `pcibios_enable_device' changed from 100 in arch/frv/mb93090-mb00/built-in.o to 40 in drivers/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_getsockopt' changed from 40 in kernel/built-in.o to 208 in net/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_setsockopt' changed from 40 in kernel/built-in.o to 228 in net/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_bind' changed from 40 in kernel/built-in.o to 192 in net/built-in.o
   frv-linux-ld: Warning: size of symbol `skb_copy_bits' changed from 40 in kernel/built-in.o to 512 in net/built-in.o
   frv-linux-ld: Warning: size of symbol `bpf_helper_changes_pkt_data' changed from 40 in kernel/built-in.o to 216 in net/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_connect' changed from 40 in kernel/built-in.o to 200 in net/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_getsockname' changed from 40 in kernel/built-in.o to 188 in net/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_socket' changed from 40 in kernel/built-in.o to 244 in net/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_recvfrom' changed from 40 in kernel/built-in.o to 304 in net/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_accept' changed from 40 in kernel/built-in.o to 44 in net/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_recv' changed from 40 in kernel/built-in.o to 48 in net/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_shutdown' changed from 40 in kernel/built-in.o to 140 in net/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_sendto' changed from 40 in kernel/built-in.o to 264 in net/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_listen' changed from 40 in kernel/built-in.o to 172 in net/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_accept4' changed from 40 in kernel/built-in.o to 492 in net/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_recvmmsg' changed from 40 in kernel/built-in.o to 316 in net/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_socketcall' changed from 40 in kernel/built-in.o to 552 in net/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_send' changed from 40 in kernel/built-in.o to 48 in net/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_socketpair' changed from 40 in kernel/built-in.o to 604 in net/built-in.o
   frv-linux-ld: Warning: size of symbol `sys_getpeername' changed from 40 in kernel/built-in.o to 204 in net/built-in.o
   init/built-in.o: In function `do_one_initcall':
   init/main.c:789:(.text+0x1b8): relocation truncated to fit: R_FRV_GPREL12 against symbol `initcall_debug' defined in .sbss section in init/built-in.o
   init/built-in.o: In function `test_and_set_bit':
   arch/frv/include/asm/bitops.h:43:(.text+0x308): relocation truncated to fit: R_FRV_GPREL12 against `once.63431'
   init/built-in.o: In function `rootfs_mount':
   init/do_mounts.c:617:(.text+0x340): relocation truncated to fit: R_FRV_GPREL12 against `is_tmpfs'
   init/built-in.o: In function `devt_from_partuuid':
   init/do_mounts.c:176:(.text+0x538): relocation truncated to fit: R_FRV_GPREL12 against `root_wait'
   init/do_mounts.c:174:(.text+0x7e4): relocation truncated to fit: R_FRV_GPREL12 against `root_wait'
   init/built-in.o: In function `set_reset_devices':
   init/main.c:159:(.init.text+0x24): relocation truncated to fit: R_FRV_GPREL12 against symbol `reset_devices' defined in .sbss section in init/built-in.o
   init/built-in.o: In function `init_setup':
   init/main.c:331:(.init.text+0xd0): relocation truncated to fit: R_FRV_GPREL12 against `execute_command'
   init/built-in.o: In function `rdinit_setup':
   init/main.c:348:(.init.text+0x120): relocation truncated to fit: R_FRV_GPREL12 against `ramdisk_execute_command'
   init/built-in.o: In function `set_init_arg':
   init/main.c:265:(.init.text+0x314): relocation truncated to fit: R_FRV_GPREL12 against `panic_later'
   init/main.c:272:(.init.text+0x35c): relocation truncated to fit: R_FRV_GPREL12 against `panic_later'
   init/main.c:273:(.init.text+0x360): additional relocation overflows omitted from the output
   drivers/built-in.o: In function `siblings_grants':
>> drivers/thermal/power_allocator.c:343: undefined reference to `__udivdi3'
   drivers/thermal/power_allocator.c:364: undefined reference to `__udivdi3'

vim +343 drivers/thermal/power_allocator.c

   337	
   338			type = thermal_actor[i].type;
   339	
   340			if (sum_avail_room[type] && avail_room[i]) {
   341				min_sum_extra_power = min(sum_spare_power[type],
   342							  sum_avail_room[type]);
 > 343				power = (avail_room[i] * min_sum_extra_power) /
   344					sum_avail_room[type];
   345	
   346				granted_power[i] += power;

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
kernel test robot May 9, 2017, 2:10 p.m. UTC | #2
Hi Lukasz,

[auto build test ERROR on thermal/next]
[also build test ERROR on v4.11 next-20170509]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Lukasz-Luba/thermal-power-allocator-add-new-divvy-up-algorithm/20170509-032607
base:   https://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux.git next
config: arm-allyesconfig (attached as .config)
compiler: arm-linux-gnueabi-gcc (Debian 6.1.1-9) 6.1.1 20160705
reproduce:
        wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=arm 

All errors (new ones prefixed by >>):

   drivers/built-in.o: In function `alpine_msix_middle_domain_alloc':
   fsi-core.c:(.text+0xb8): relocation truncated to fit: R_ARM_CALL against symbol `_raw_spin_lock' defined in .spinlock.text section in kernel/built-in.o
   fsi-core.c:(.text+0xf0): relocation truncated to fit: R_ARM_CALL against symbol `_raw_spin_unlock' defined in .spinlock.text section in kernel/built-in.o
   fsi-core.c:(.text+0x114): relocation truncated to fit: R_ARM_CALL against symbol `_raw_spin_unlock' defined in .spinlock.text section in kernel/built-in.o
   fsi-core.c:(.text+0x214): relocation truncated to fit: R_ARM_CALL against symbol `_raw_spin_lock' defined in .spinlock.text section in kernel/built-in.o
   fsi-core.c:(.text+0x22c): relocation truncated to fit: R_ARM_CALL against symbol `_raw_spin_unlock' defined in .spinlock.text section in kernel/built-in.o
   drivers/built-in.o: In function `alpine_msix_init_domains':
   fsi-core.c:(.text+0x2d8): relocation truncated to fit: R_ARM_CALL against symbol `of_irq_find_parent' defined in .text section in drivers/built-in.o
   drivers/built-in.o: In function `alpine_msix_init':
   fsi-core.c:(.text+0x414): relocation truncated to fit: R_ARM_CALL against symbol `of_address_to_resource' defined in .text section in drivers/built-in.o
   fsi-core.c:(.text+0x454): relocation truncated to fit: R_ARM_CALL against symbol `of_property_read_variable_u32_array' defined in .text section in drivers/built-in.o
   fsi-core.c:(.text+0x4c4): relocation truncated to fit: R_ARM_CALL against symbol `of_property_read_variable_u32_array' defined in .text section in drivers/built-in.o
   drivers/built-in.o: In function `alpine_msix_middle_domain_free':
   fsi-core.c:(.text+0x5a4): relocation truncated to fit: R_ARM_CALL against symbol `_raw_spin_lock' defined in .spinlock.text section in kernel/built-in.o
   fsi-core.c:(.text+0x5bc): additional relocation overflows omitted from the output
   drivers/built-in.o: In function `divvy_up_power':
>> fsi-core.c:(.text+0x26cab28): undefined reference to `__aeabi_uldivmod'
   fsi-core.c:(.text+0x26cabe8): undefined reference to `__aeabi_uldivmod'

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
diff mbox

Patch

diff --git a/drivers/thermal/power_allocator.c b/drivers/thermal/power_allocator.c
index b4d3116..db74e05 100644
--- a/drivers/thermal/power_allocator.c
+++ b/drivers/thermal/power_allocator.c
@@ -30,6 +30,18 @@ 
 #define int_to_frac(x) ((x) << FRAC_BITS)
 #define frac_to_int(x) ((x) >> FRAC_BITS)
 
+enum thermal_actor_type {
+	OTHER_CDEV = 0,
+	CPUFREQ_CDEV,
+	DEVFREQ_CDEV,
+	NUM_CDEV_TYPES
+};
+
+struct thermal_actor {
+	struct thermal_cooling_device *cdev;
+	enum thermal_actor_type type;
+};
+
 /**
  * mul_frac() - multiply two fixed-point numbers
  * @x:	first multiplicand
@@ -258,6 +270,103 @@  static u32 pid_controller(struct thermal_zone_device *tz,
 	return power_range;
 }
 
+
+/**
+ * siblings_grants() - splits the extra power
+ * @max_power:		each actor's maximum available power
+ * @granted_power:	each actor's granted power which is the output array
+ *			and will be increased here
+ * @avail_room:		each actor's capability to consume some more power
+ * @spare_power:	each actor's power value above it's max_power limit,
+ *			which will be consumed by other actors
+ * @num_actors:		size of the @avail_room, @spare_power, @max_power and
+ *			@granted_power's array
+ * @thermal_actor:	array of filtered actors which will be involved in
+ *			this process
+ *
+ * The algorithm calculates the total extra power available per actor's family
+ * and splits this power equally between the family members.
+ * The algorithm distinguishes actors based on their names and adds them to the
+ * proper family. Each family has its own sum of available power to consume
+ * (which can be equal 0).
+ * When the family sharing is done and some power still is available, it goes
+ * to generic power sum, which will be split fairly between all capable actors.
+ */
+static void siblings_grants(struct thermal_zone_device *tz,
+			u32 *max_power, u32 *granted_power,
+			u32 *avail_room, u32 *spare_power,
+			int num_actors,	struct thermal_actor *thermal_actor)
+{
+	struct thermal_cooling_device *cdev;
+	int i;
+	const char *devfreq_name = "thermal-devfreq-";
+	const char *cpufreq_name = "thermal-cpufreq-";
+	u64 sum_spare_power[NUM_CDEV_TYPES] = {0};
+	u64 sum_avail_room[NUM_CDEV_TYPES] = {0};
+	u64 min_sum_extra_power = 0;
+	enum thermal_actor_type type;
+	u64 rest_avail_room = 0;
+	u64 total_spare_power = 0;
+	u64 total_avail_room = 0;
+	unsigned int rounding_gap = num_actors - 1;
+
+	/* Group the thermal actors and calculate power budget
+	 * for each family.
+	 */
+	for (i = 0; i < num_actors; i++) {
+		cdev = thermal_actor[i].cdev;
+		if (!cdev)
+			continue;
+		if (strstarts(cdev->type, cpufreq_name))
+			type = CPUFREQ_CDEV;
+		else if (strstarts(cdev->type, devfreq_name))
+			type = DEVFREQ_CDEV;
+		else
+			type = OTHER_CDEV;
+
+		thermal_actor[i].type = type;
+		sum_spare_power[type] += spare_power[i];
+		sum_avail_room[type] += avail_room[i];
+		total_spare_power += spare_power[i];
+		total_avail_room += avail_room[i];
+	}
+
+	/* Share the extra power inside the same family. */
+	for (i = 0; i < num_actors; i++) {
+		u32 power;
+
+		type = thermal_actor[i].type;
+
+		if (sum_avail_room[type] && avail_room[i]) {
+			min_sum_extra_power = min(sum_spare_power[type],
+						  sum_avail_room[type]);
+			power = (avail_room[i] * min_sum_extra_power) /
+				sum_avail_room[type];
+
+			granted_power[i] += power;
+			avail_room[i] -= power;
+			total_spare_power -= power;
+			rest_avail_room += avail_room[i];
+		}
+	}
+
+	/* Share the rest (if any) of power between all actors,
+	 *  no mater the group they belong to.
+	 */
+	if (rest_avail_room && total_spare_power > rounding_gap) {
+		u32 min_extra_power;
+
+		min_extra_power = min(total_spare_power, rest_avail_room);
+		for (i = 0; i < num_actors; i++) {
+			if (!avail_room[i])
+				continue;
+
+			granted_power[i] += (avail_room[i] * min_extra_power) /
+				rest_avail_room;
+		}
+	}
+}
+
 /**
  * divvy_up_power() - divvy the allocated power between the actors
  * @req_power:	each actor's requested power
@@ -266,9 +375,12 @@  static u32 pid_controller(struct thermal_zone_device *tz,
  * @total_req_power: sum of @req_power
  * @power_range:	total allocated power
  * @granted_power:	output array: each actor's granted power
- * @extra_actor_power:	an appropriately sized array to be used in the
- *			function as temporary storage of the extra power given
- *			to the actors
+ * @avail_room:		an appropriately sized array to be used in the
+ *			function as temporary storage of the available room for
+ *			extra power for each actor
+ * @spare_power:	an appropriately sized array to be used in the
+ *			function as temporary storage of the spare power
+ *			for each actor, which can be taken by some other actors
  *
  * This function divides the total allocated power (@power_range)
  * fairly between the actors.  It first tries to give each actor a
@@ -285,12 +397,16 @@  static u32 pid_controller(struct thermal_zone_device *tz,
  * 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,
+static void divvy_up_power(struct thermal_zone_device *tz, u32 *req_power,
+			   u32 *max_power, int num_actors,
 			   u32 total_req_power, u32 power_range,
-			   u32 *granted_power, u32 *extra_actor_power)
+			   u32 *granted_power, u32 *avail_room,
+			   u32 *spare_power,
+			   struct thermal_actor *thermal_actor)
 {
-	u32 extra_power, capped_extra_power;
 	int i;
+	u64 sum_avail_room = 0;
+	u64 sum_spare_power = 0;
 
 	/*
 	 * Prevent division by 0 if none of the actors request power.
@@ -298,8 +414,6 @@  static void divvy_up_power(u32 *req_power, u32 *max_power, int num_actors,
 	if (!total_req_power)
 		total_req_power = 1;
 
-	capped_extra_power = 0;
-	extra_power = 0;
 	for (i = 0; i < num_actors; i++) {
 		u64 req_range = (u64)req_power[i] * power_range;
 
@@ -307,26 +421,25 @@  static void divvy_up_power(u32 *req_power, u32 *max_power, int num_actors,
 							 total_req_power);
 
 		if (granted_power[i] > max_power[i]) {
-			extra_power += granted_power[i] - max_power[i];
+			spare_power[i] = granted_power[i] - max_power[i];
+			sum_spare_power += spare_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];
+		avail_room[i] = max_power[i] - granted_power[i];
+		sum_avail_room += avail_room[i];
 	}
 
-	if (!extra_power)
+	if (!sum_avail_room || !sum_spare_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;
+	siblings_grants(tz, max_power, granted_power, avail_room,
+			spare_power, num_actors, thermal_actor);
+
 }
 
 static int allocate_power(struct thermal_zone_device *tz,
@@ -334,12 +447,13 @@  static int allocate_power(struct thermal_zone_device *tz,
 {
 	struct thermal_instance *instance;
 	struct power_allocator_params *params = tz->governor_data;
-	u32 *req_power, *max_power, *granted_power, *extra_actor_power;
-	u32 *weighted_req_power;
+	u32 *req_power, *max_power, *granted_power, *avail_room;
+	u32 *weighted_req_power, *spare_power;
 	u32 total_req_power, max_allocatable_power, total_weighted_req_power;
 	u32 total_granted_power, power_range;
 	int i, num_actors, total_weight, ret = 0;
 	int trip_max_desired_temperature = params->trip_max_desired_temperature;
+	struct thermal_actor *thermal_actor;
 
 	mutex_lock(&tz->lock);
 
@@ -358,6 +472,13 @@  static int allocate_power(struct thermal_zone_device *tz,
 		goto unlock;
 	}
 
+	thermal_actor = kcalloc(num_actors, sizeof(struct thermal_actor),
+				GFP_KERNEL);
+	if (!thermal_actor) {
+		ret = -ENOMEM;
+		goto unlock;
+	}
+
 	/*
 	 * We need to allocate five arrays of the same size:
 	 * req_power, max_power, granted_power, extra_actor_power and
@@ -367,18 +488,21 @@  static int allocate_power(struct thermal_zone_device *tz,
 	 */
 	BUILD_BUG_ON(sizeof(*req_power) != sizeof(*max_power));
 	BUILD_BUG_ON(sizeof(*req_power) != sizeof(*granted_power));
-	BUILD_BUG_ON(sizeof(*req_power) != sizeof(*extra_actor_power));
+	BUILD_BUG_ON(sizeof(*req_power) != sizeof(*avail_room));
 	BUILD_BUG_ON(sizeof(*req_power) != sizeof(*weighted_req_power));
-	req_power = kcalloc(num_actors * 5, sizeof(*req_power), GFP_KERNEL);
+	BUILD_BUG_ON(sizeof(*req_power) != sizeof(*spare_power));
+	req_power = kzalloc(num_actors * 6 * sizeof(*req_power), GFP_KERNEL);
 	if (!req_power) {
 		ret = -ENOMEM;
+		kfree(thermal_actor);
 		goto unlock;
 	}
 
 	max_power = &req_power[num_actors];
 	granted_power = &req_power[2 * num_actors];
-	extra_actor_power = &req_power[3 * num_actors];
+	avail_room = &req_power[3 * num_actors];
 	weighted_req_power = &req_power[4 * num_actors];
+	spare_power = &req_power[5 * num_actors];
 
 	i = 0;
 	total_weighted_req_power = 0;
@@ -412,14 +536,15 @@  static int allocate_power(struct thermal_zone_device *tz,
 		max_allocatable_power += max_power[i];
 		total_weighted_req_power += weighted_req_power[i];
 
+		thermal_actor[i].cdev = cdev;
 		i++;
 	}
 
 	power_range = pid_controller(tz, control_temp, max_allocatable_power);
 
-	divvy_up_power(weighted_req_power, max_power, num_actors,
+	divvy_up_power(tz, weighted_req_power, max_power, num_actors,
 		       total_weighted_req_power, power_range, granted_power,
-		       extra_actor_power);
+		       avail_room, spare_power, thermal_actor);
 
 	total_granted_power = 0;
 	i = 0;
@@ -444,6 +569,7 @@  static int allocate_power(struct thermal_zone_device *tz,
 				      control_temp - tz->temperature);
 
 	kfree(req_power);
+	kfree(thermal_actor);
 unlock:
 	mutex_unlock(&tz->lock);