cpufreq: Synchronize the cpufreq store_*() routines with CPU hotplug
authorNicolin Chen <nicolinc@nvidia.com>
Sat, 23 Jan 2016 00:00:48 +0000 (16:00 -0800)
committermobile promotions <svcmobile_promotions@nvidia.com>
Sat, 10 Dec 2016 13:27:05 +0000 (05:27 -0800)
commit11dc119eca72f8d8077266f874c0a59564cef082
tree100df506857fab6cd84cfa03567f279fd8cb71f7
parent28a8787d1d1bc42e2227d47284bdd50bae87aa2a
cpufreq: Synchronize the cpufreq store_*() routines with CPU hotplug

The functions that are used to write to cpufreq sysfs files (such as
store_scaling_max_freq()) are not hotplug safe. They can race with CPU
hotplug tasks and lead to problems such as trying to acquire an already
destroyed timer-mutex etc.

Eg:

    __cpufreq_remove_dev()
     __cpufreq_governor(policy, CPUFREQ_GOV_STOP);
       policy->governor->governor(policy, CPUFREQ_GOV_STOP);
        cpufreq_governor_dbs()
         case CPUFREQ_GOV_STOP:
          mutex_destroy(&cpu_cdbs->timer_mutex)
          cpu_cdbs->cur_policy = NULL;
      <PREEMPT>
    store()
     __cpufreq_set_policy()
      __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS);
        policy->governor->governor(policy, CPUFREQ_GOV_LIMITS);
         case CPUFREQ_GOV_LIMITS:
          mutex_lock(&cpu_cdbs->timer_mutex); <-- Warning (destroyed mutex)
           if (policy->max < cpu_cdbs->cur_policy->cur) <- cur_policy == NULL

So use get_online_cpus()/put_online_cpus() in the store_*() functions, to
synchronize with CPU hotplug.

[ Merging the same patch from the Linux mainline, commited by Srivatsa
  S. Bhat <srivatsa.bhat@linux.vnet.ibm.com>; could not do cherry-pick
  due to conflicts. Also revised the commit log to make less confusion
  as the original commit log mentioned an issue that isn't included in
  our 3.10 branch.

  This commit could be treated as a fix to the Bug 200152270 as there
  is a race condition here between SYSFS operations and a cpu hotplug
  that when the init process tries to GOV_START all online cpus via
  SYSFS, a cpu hotplug may happen to turn off one cpu without updating
  the new_policy->cpus in time. So the new_policy->cpus might contain
  an offlined cpu which is the root cause of this bug. Adding a lock
  of hotplug here ensures no race would happen during the SYSFS access.

  As policy->cpus is always updated during hotplug in its add/remove
  functions, we don't need to worry that it gets out-of-date as long
  as any hotplug operation is locked during the store_*().

  I applied this change and passed both the cpufreqhotplugstress and
  the following test:

  while true; do echo 0 > /sys/devices/system/cpu/cpu3/online; echo 1 > /sys/devices/system/cpu/cpu3/online; done&
  while true; do echo userspace > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor; echo interactive > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor; done&

  -- Nicolin ]

Bug 200152270
Bug 200254695

Change-Id: If871094dc92d4478a9484e92fd5cbebaeb9ae5e8
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
Reviewed-on: http://git-master/r/1111679
Reviewed-by: Richard Wiley <rwiley@nvidia.com>
Tested-by: Richard Wiley <rwiley@nvidia.com>
(cherry picked from commit 39be41de331032a0fc596b9d20a8e11dcb0a5f7d)
Reviewed-on: http://git-master/r/1268903
Reviewed-by: Dhiren Parmar <dparmar@nvidia.com>
Tested-by: Dhiren Parmar <dparmar@nvidia.com>
GVS: Gerrit_Virtual_Submit
drivers/cpufreq/cpufreq.c