--- /dev/null
+From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Date: Tue, 21 Jul 2015 15:28:49 +0200
+Subject: cpufreq: Remove cpufreq_rwsem
+
+cpufreq_rwsem was introduced in commit 6eed9404ab3c4 ("cpufreq: Use
+rwsem for protecting critical sections) in order to replace
+try_module_get() on the cpu-freq driver. That try_module_get() worked
+well until the refcount was so heavily used that module removal became
+more or less impossible.
+
+Though when looking at the various (undocumented) protection
+mechanisms in that code, the randomly sprinkeled around cpufreq_rwsem
+locking sites are superfluous.
+
+The policy, which is acquired in cpufreq_cpu_get() and released in
+cpufreq_cpu_put() is sufficiently protected already.
+
+ cpufreq_cpu_get(cpu)
+ /* Protects against concurrent driver removal */
+ read_lock_irqsave(&cpufreq_driver_lock, flags);
+ policy = per_cpu(cpufreq_cpu_data, cpu);
+ kobject_get(&policy->kobj);
+ read_unlock_irqrestore(&cpufreq_driver_lock, flags);
+
+The reference on the policy serializes versus module unload already:
+
+ cpufreq_unregister_driver()
+ subsys_interface_unregister()
+ __cpufreq_remove_dev_finish()
+ per_cpu(cpufreq_cpu_data) = NULL;
+ cpufreq_policy_put_kobj()
+
+If there is a reference held on the policy, i.e. obtained prior to the
+unregister call, then cpufreq_policy_put_kobj() will wait until that
+reference is dropped. So once subsys_interface_unregister() returns
+there is no policy pointer in flight and no new reference can be
+obtained. So that rwsem protection is useless.
+
+The other usage of cpufreq_rwsem in show()/store() of the sysfs
+interface is redundant as well because sysfs already does the proper
+kobject_get()/put() pairs.
+
+That leaves CPU hotplug versus module removal. The current
+down_write() around the write_lock() in cpufreq_unregister_driver() is
+silly at best as it protects actually nothing.
+
+The trivial solution to this is to prevent hotplug across
+cpufreq_unregister_driver completely.
+
+[upstream: rafael/linux-pm 454d3a2500a4eb33be85dde3bfba9e5f6b5efadc]
+[fixes: "cpufreq_stat_notifier_trans: No policy found" since v4.0-rt]
+Cc: stable-rt@vger.kernel.org
+Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+---
+ drivers/cpufreq/cpufreq.c | 35 +++--------------------------------
+ 1 file changed, 3 insertions(+), 32 deletions(-)
+
+--- a/drivers/cpufreq/cpufreq.c
++++ b/drivers/cpufreq/cpufreq.c
+@@ -64,12 +64,6 @@ static inline bool has_target(void)
+ return cpufreq_driver->target_index || cpufreq_driver->target;
+ }
+
+-/*
+- * rwsem to guarantee that cpufreq driver module doesn't unload during critical
+- * sections
+- */
+-static DECLARE_RWSEM(cpufreq_rwsem);
+-
+ /* internal prototypes */
+ static int __cpufreq_governor(struct cpufreq_policy *policy,
+ unsigned int event);
+@@ -215,9 +209,6 @@ struct cpufreq_policy *cpufreq_cpu_get(u
+ if (cpu >= nr_cpu_ids)
+ return NULL;
+
+- if (!down_read_trylock(&cpufreq_rwsem))
+- return NULL;
+-
+ /* get the cpufreq driver */
+ read_lock_irqsave(&cpufreq_driver_lock, flags);
+
+@@ -230,9 +221,6 @@ struct cpufreq_policy *cpufreq_cpu_get(u
+
+ read_unlock_irqrestore(&cpufreq_driver_lock, flags);
+
+- if (!policy)
+- up_read(&cpufreq_rwsem);
+-
+ return policy;
+ }
+ EXPORT_SYMBOL_GPL(cpufreq_cpu_get);
+@@ -240,7 +228,6 @@ EXPORT_SYMBOL_GPL(cpufreq_cpu_get);
+ void cpufreq_cpu_put(struct cpufreq_policy *policy)
+ {
+ kobject_put(&policy->kobj);
+- up_read(&cpufreq_rwsem);
+ }
+ EXPORT_SYMBOL_GPL(cpufreq_cpu_put);
+
+@@ -765,9 +752,6 @@ static ssize_t show(struct kobject *kobj
+ struct freq_attr *fattr = to_attr(attr);
+ ssize_t ret;
+
+- if (!down_read_trylock(&cpufreq_rwsem))
+- return -EINVAL;
+-
+ down_read(&policy->rwsem);
+
+ if (fattr->show)
+@@ -776,7 +760,6 @@ static ssize_t show(struct kobject *kobj
+ ret = -EIO;
+
+ up_read(&policy->rwsem);
+- up_read(&cpufreq_rwsem);
+
+ return ret;
+ }
+@@ -793,9 +776,6 @@ static ssize_t store(struct kobject *kob
+ if (!cpu_online(policy->cpu))
+ goto unlock;
+
+- if (!down_read_trylock(&cpufreq_rwsem))
+- goto unlock;
+-
+ down_write(&policy->rwsem);
+
+ if (fattr->store)
+@@ -804,8 +784,6 @@ static ssize_t store(struct kobject *kob
+ ret = -EIO;
+
+ up_write(&policy->rwsem);
+-
+- up_read(&cpufreq_rwsem);
+ unlock:
+ put_online_cpus();
+
+@@ -1117,16 +1095,12 @@ static int __cpufreq_add_dev(struct devi
+ if (unlikely(policy))
+ return 0;
+
+- if (!down_read_trylock(&cpufreq_rwsem))
+- return 0;
+-
+ /* Check if this cpu was hot-unplugged earlier and has siblings */
+ read_lock_irqsave(&cpufreq_driver_lock, flags);
+ for_each_policy(policy) {
+ if (cpumask_test_cpu(cpu, policy->related_cpus)) {
+ read_unlock_irqrestore(&cpufreq_driver_lock, flags);
+ ret = cpufreq_add_policy_cpu(policy, cpu, dev);
+- up_read(&cpufreq_rwsem);
+ return ret;
+ }
+ }
+@@ -1269,8 +1243,6 @@ static int __cpufreq_add_dev(struct devi
+
+ kobject_uevent(&policy->kobj, KOBJ_ADD);
+
+- up_read(&cpufreq_rwsem);
+-
+ /* Callback for handling stuff after policy is ready */
+ if (cpufreq_driver->ready)
+ cpufreq_driver->ready(policy);
+@@ -1304,8 +1276,6 @@ static int __cpufreq_add_dev(struct devi
+ cpufreq_policy_free(policy);
+
+ nomem_out:
+- up_read(&cpufreq_rwsem);
+-
+ return ret;
+ }
+
+@@ -2499,19 +2469,20 @@ int cpufreq_unregister_driver(struct cpu
+
+ pr_debug("unregistering driver %s\n", driver->name);
+
++ /* Protect against concurrent cpu hotplug */
++ get_online_cpus();
+ subsys_interface_unregister(&cpufreq_interface);
+ if (cpufreq_boost_supported())
+ cpufreq_sysfs_remove_file(&boost.attr);
+
+ unregister_hotcpu_notifier(&cpufreq_cpu_notifier);
+
+- down_write(&cpufreq_rwsem);
+ write_lock_irqsave(&cpufreq_driver_lock, flags);
+
+ cpufreq_driver = NULL;
+
+ write_unlock_irqrestore(&cpufreq_driver_lock, flags);
+- up_write(&cpufreq_rwsem);
++ put_online_cpus();
+
+ return 0;
+ }