]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
PM: EM: Introduce em_dev_update_perf_domain() for EM updates
authorLukasz Luba <lukasz.luba@arm.com>
Thu, 8 Feb 2024 11:55:45 +0000 (11:55 +0000)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Thu, 8 Feb 2024 14:00:30 +0000 (15:00 +0100)
Add API function em_dev_update_perf_domain() which allows the EM to be
changed safely.

Concurrent updaters are serialized with a mutex and the removal of memory
that will not be used any more is carried out with the help of RCU.

Reviewed-by: Dietmar Eggemann <dietmar.eggemann@arm.com>
Tested-by: Dietmar Eggemann <dietmar.eggemann@arm.com>
Signed-off-by: Lukasz Luba <lukasz.luba@arm.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
include/linux/energy_model.h
kernel/power/energy_model.c

index 27911dc1887e87b41080765d63912adb47d62767..324a3a8e0a2de948f8bde872996529eb18780ce3 100644 (file)
@@ -183,6 +183,8 @@ struct em_data_callback {
 
 struct em_perf_domain *em_cpu_get(int cpu);
 struct em_perf_domain *em_pd_get(struct device *dev);
+int em_dev_update_perf_domain(struct device *dev,
+                             struct em_perf_table __rcu *new_table);
 int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
                                struct em_data_callback *cb, cpumask_t *span,
                                bool microwatts);
@@ -376,6 +378,12 @@ struct em_perf_table __rcu *em_table_alloc(struct em_perf_domain *pd)
        return NULL;
 }
 static inline void em_table_free(struct em_perf_table __rcu *table) {}
+static inline
+int em_dev_update_perf_domain(struct device *dev,
+                             struct em_perf_table __rcu *new_table)
+{
+       return -EINVAL;
+}
 #endif
 
 #endif
index 16795743f96985b69c35883f3a6d9f0cf4113a2e..667619b70be7fe220bc4336ccab92de4c3c54618 100644 (file)
@@ -209,6 +209,50 @@ static int em_allocate_perf_table(struct em_perf_domain *pd,
        return 0;
 }
 
+/**
+ * em_dev_update_perf_domain() - Update runtime EM table for a device
+ * @dev                : Device for which the EM is to be updated
+ * @new_table  : The new EM table that is going to be used from now
+ *
+ * Update EM runtime modifiable table for the @dev using the provided @table.
+ *
+ * This function uses a mutex to serialize writers, so it must not be called
+ * from a non-sleeping context.
+ *
+ * Return 0 on success or an error code on failure.
+ */
+int em_dev_update_perf_domain(struct device *dev,
+                             struct em_perf_table __rcu *new_table)
+{
+       struct em_perf_table __rcu *old_table;
+       struct em_perf_domain *pd;
+
+       if (!dev)
+               return -EINVAL;
+
+       /* Serialize update/unregister or concurrent updates */
+       mutex_lock(&em_pd_mutex);
+
+       if (!dev->em_pd) {
+               mutex_unlock(&em_pd_mutex);
+               return -EINVAL;
+       }
+       pd = dev->em_pd;
+
+       kref_get(&new_table->kref);
+
+       old_table = pd->em_table;
+       rcu_assign_pointer(pd->em_table, new_table);
+
+       em_cpufreq_update_efficiencies(dev, new_table->state);
+
+       em_table_free(old_table);
+
+       mutex_unlock(&em_pd_mutex);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(em_dev_update_perf_domain);
+
 static int em_create_runtime_table(struct em_perf_domain *pd)
 {
        struct em_perf_table __rcu *table;