Skip to content

Commit

Permalink
PM: EM: Add functions for memory allocations for new EM tables
Browse files Browse the repository at this point in the history
The runtime modified EM table can be provided from drivers. Create
mechanism which allows safely allocate and free the table for device
drivers. The same table can be used by the EAS in task scheduler code
paths, so make sure the memory is not freed when the device driver module
is unloaded.

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>
  • Loading branch information
Lukasz Luba authored and Rafael J. Wysocki committed Feb 8, 2024
1 parent aa11a7e commit ffcf9bc
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 5 deletions.
11 changes: 11 additions & 0 deletions include/linux/energy_model.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <linux/device.h>
#include <linux/jump_label.h>
#include <linux/kobject.h>
#include <linux/kref.h>
#include <linux/rcupdate.h>
#include <linux/sched/cpufreq.h>
#include <linux/sched/topology.h>
Expand Down Expand Up @@ -39,10 +40,12 @@ struct em_perf_state {
/**
* struct em_perf_table - Performance states table
* @rcu: RCU used for safe access and destruction
* @kref: Reference counter to track the users
* @state: List of performance states, in ascending order
*/
struct em_perf_table {
struct rcu_head rcu;
struct kref kref;
struct em_perf_state state[];
};

Expand Down Expand Up @@ -184,6 +187,8 @@ int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
struct em_data_callback *cb, cpumask_t *span,
bool microwatts);
void em_dev_unregister_perf_domain(struct device *dev);
struct em_perf_table __rcu *em_table_alloc(struct em_perf_domain *pd);
void em_table_free(struct em_perf_table __rcu *table);

/**
* em_pd_get_efficient_state() - Get an efficient performance state from the EM
Expand Down Expand Up @@ -365,6 +370,12 @@ static inline int em_pd_nr_perf_states(struct em_perf_domain *pd)
{
return 0;
}
static inline
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) {}
#endif

#endif
38 changes: 33 additions & 5 deletions kernel/power/energy_model.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,20 +114,48 @@ static void em_destroy_table_rcu(struct rcu_head *rp)
kfree(table);
}

static void em_free_table(struct em_perf_table __rcu *table)
static void em_release_table_kref(struct kref *kref)
{
struct em_perf_table __rcu *table;

/* It was the last owner of this table so we can free */
table = container_of(kref, struct em_perf_table, kref);

call_rcu(&table->rcu, em_destroy_table_rcu);
}

static struct em_perf_table __rcu *
em_allocate_table(struct em_perf_domain *pd)
/**
* em_table_free() - Handles safe free of the EM table when needed
* @table : EM table which is going to be freed
*
* No return values.
*/
void em_table_free(struct em_perf_table __rcu *table)
{
kref_put(&table->kref, em_release_table_kref);
}

/**
* em_table_alloc() - Allocate a new EM table
* @pd : EM performance domain for which this must be done
*
* Allocate a new EM table and initialize its kref to indicate that it
* has a user.
* Returns allocated table or NULL.
*/
struct em_perf_table __rcu *em_table_alloc(struct em_perf_domain *pd)
{
struct em_perf_table __rcu *table;
int table_size;

table_size = sizeof(struct em_perf_state) * pd->nr_perf_states;

table = kzalloc(sizeof(*table) + table_size, GFP_KERNEL);
if (!table)
return NULL;

kref_init(&table->kref);

return table;
}

Expand Down Expand Up @@ -186,7 +214,7 @@ static int em_create_runtime_table(struct em_perf_domain *pd)
struct em_perf_table __rcu *table;
int table_size;

table = em_allocate_table(pd);
table = em_table_alloc(pd);
if (!table)
return -ENOMEM;

Expand Down Expand Up @@ -512,7 +540,7 @@ void em_dev_unregister_perf_domain(struct device *dev)

kfree(dev->em_pd->table);

em_free_table(dev->em_pd->em_table);
em_table_free(dev->em_pd->em_table);

kfree(dev->em_pd);
dev->em_pd = NULL;
Expand Down

0 comments on commit ffcf9bc

Please sign in to comment.