powercap/drivers/dtpm: Encapsulate even more the code
PATCH 1/5 powercap/drivers/dtpm: Encapsulate even more the code
In order to increase the self-encapsulation of the dtpm generic code, the following changes are adding a power update ops to the dtpm ops. That allows the generic code to call directly the dtpm backend function to update the power values.
The power update function does compute the power characteristics when the function is invoked. In the case of the CPUs, the power consumption depends on the number of online CPUs. The online CPUs mask is not up to date at CPUHP_AP_ONLINE_DYN state in the tear down callback. That is the reason why the online / offline are at separate state. As there is already an existing state for DTPM, this one is only moved to the DEAD state, so there is no addition of new state with these changes.
That simplifies the code for the next changes and results in a more self-encapsulated code.
--- drivers/powercap/dtpm.c | 54 ++++++++-------- drivers/powercap/dtpm_cpu.c | 124 +++++++++++++----------------------- include/linux/cpuhotplug.h | 2 +- include/linux/dtpm.h | 3 +- 4 files changed, 76 insertions(+), 107 deletions(-)
drivers/powercap/dtpm.c +27 -27
@@ -116,8 +116,6 @@ static void __dtpm_sub_power(struct dtpm *dtpm) parent->power_limit -= dtpm->power_limit; parent = parent->parent; } - -__dtpm_rebalance_weight (root); }static void __dtpm_add_power (struct dtpm *dtpm)
@@ -130,45 +128,45 @@ static void __dtpm_add_power(struct dtpm *dtpm) parent->power_limit += dtpm->power_limit; parent = parent->parent; } +} + +static int __dtpm_update_power (struct dtpm *dtpm) +{ +int ret; + +__dtpm_sub_power (dtpm); -__dtpm_rebalance_weight (root); + ret = dtpm->ops->upt_power_uw (dtpm); +if (ret) +pr_err ("Failed to update power for '%s': %d\n" , + dtpm->zone.name, ret); + +if (!test_bit (DTPM_POWER_LIMIT_FLAG , &dtpm->flags)) + dtpm->power_limit = dtpm->power_max; + +__dtpm_add_power (dtpm); + +if (root) +__dtpm_rebalance_weight (root); + +return ret; }/** * dtpm_update_power - Update the power on the dtpm * @dtpm: a pointer to a dtpm structure to update -* @power_min: a u64 representing the new power_min value -* @power_max: a u64 representing the new power_max value * * Function to update the power values of the dtpm node specified in * parameter. These new values will be propagated to the tree. * * Return: zero on success, -EINVAL if the values are inconsistent */ -int dtpm_update_power (struct dtpm *dtpm,u64 power_min,u64 power_max) +int dtpm_update_power (struct dtpm *dtpm) { -int ret =0 ; +int ret;mutex_lock (&dtpm_lock); - -if (power_min == dtpm->power_min && power_max == dtpm->power_max) - goto unlock; - -if (power_max < power_min) { - ret = -EINVAL ; - goto unlock; - } - -__dtpm_sub_power (dtpm); - - dtpm->power_min = power_min; - dtpm->power_max = power_max; -if (!test_bit (DTPM_POWER_LIMIT_FLAG , &dtpm->flags)) - dtpm->power_limit = power_max; - -__dtpm_add_power (dtpm); - -unlock: + ret =__dtpm_update_power (dtpm);mutex_unlock (&dtpm_lock);return ret;
@@ -436,6 +434,7 @@ int dtpm_register(const char *name, struct dtpm *dtpm, struct dtpm *parent)if (dtpm->ops && !(dtpm->ops->set_power_uw && dtpm->ops->get_power_uw && + dtpm->ops->upt_power_uw && dtpm->ops->release))return -EINVAL ;
@@ -455,7 +454,8 @@ int dtpm_register(const char *name, struct dtpm *dtpm, struct dtpm *parent) root = dtpm; } -__dtpm_add_power (dtpm); +if (dtpm->ops && !dtpm->ops->upt_power_uw (dtpm)) +__dtpm_add_power (dtpm);pr_info ("Registered dtpm node '%s' / %llu-%llu uW, \n" , dtpm->zone.name, dtpm->power_min, dtpm->power_max);
drivers/powercap/dtpm_cpu.c +46 -78
217 lines not on this page · raw message
include/linux/cpuhotplug.h +1 -1
@@ -61,6 +61,7 @@ enum cpuhp_state {CPUHP_LUSTRE_CFS_DEAD ,CPUHP_AP_ARM_CACHE_B15_RAC_DEAD ,CPUHP_PADATA_DEAD , +CPUHP_AP_DTPM_CPU_DEAD ,CPUHP_WORKQUEUE_PREP ,CPUHP_POWER_NUMA_PREPARE , CPUHP_HRTIMERS_PREPARE,
@@ -193,7 +194,6 @@ enum cpuhp_state {CPUHP_AP_ONLINE_DYN_END =CPUHP_AP_ONLINE_DYN +30 ,CPUHP_AP_X86_HPET_ONLINE ,CPUHP_AP_X86_KVM_CLK_ONLINE , -CPUHP_AP_DTPM_CPU_ONLINE ,CPUHP_AP_ACTIVE , CPUHP_ONLINE, };
include/linux/dtpm.h +2 -1
@@ -29,6 +29,7 @@ struct dtpm {struct dtpm_ops {u64 (*set_power_uw)(struct dtpm *,u64 );u64 (*get_power_uw)(struct dtpm *); +int (*upt_power_uw)(struct dtpm *);void (*release)(struct dtpm *); };
@@ -62,7 +63,7 @@ static inline struct dtpm *to_dtpm(struct powercap_zone *zone) return container_of(zone, struct dtpm, zone); } -int dtpm_update_power (struct dtpm *dtpm,u64 power_min,u64 power_max); +int dtpm_update_power (struct dtpm *dtpm);int dtpm_release_zone (struct powercap_zone *pcz);
Signature · 1 line
2.17.1
PATCH 2/5 powercap/drivers/dtpm: Create a registering system
A SoC can be differently structured depending on the platform and the kernel can not be aware of all the combinations, as well as the specific tweaks for a particular board.
The creation of the hierarchy must be delegated to userspace.
These changes provide a registering mechanism where the different subsystems will initialize their dtpm backends and register with a name the dtpm node in a list.
The next changes will provide an userspace interface to create hierachically the different nodes. Those will be created by name and found via the list filled by the different subsystem.
If a specified name is not found in the list, it is assumed to be a virtual node which will have children and the default is to allocate such node.
When the node register in the list, the function will be dtpm_register where the previous semantic was to create the node. Thus, the functions are renamed to reflect their purpose.
--- drivers/powercap/dtpm.c | 158 ++++++++++++++++++++++++++++++++++-- drivers/powercap/dtpm_cpu.c | 4 +- include/linux/dtpm.h | 12 ++- 3 files changed, 161 insertions(+), 13 deletions(-)
drivers/powercap/dtpm.c +149 -9
@@ -20,6 +20,7 @@#include <linux/dtpm.h> #include <linux/init.h> #include <linux/kernel.h> +#include <linux/kref.h> #include <linux/powercap.h> #include <linux/slab.h> #include <linux/mutex.h>
@@ -34,6 +35,14 @@ static DEFINE_MUTEX(dtpm_lock);static struct powercap_control_type *pct;static struct dtpm *root; +struct dtpm_node { +const char *name; +struct dtpm *dtpm; +struct list_head node; +}; + +static LIST_HEAD (dtpm_list); +static int get_time_window_us (struct powercap_zone *pcz,int cid,u64 *window) {return -ENOSYS ;
@@ -152,6 +161,135 @@ static int __dtpm_update_power(struct dtpm *dtpm)return ret; } +static struct dtpm *__dtpm_lookup (const char *name) +{ +struct dtpm_node *node; + +list_for_each_entry (node, &dtpm_list, node) { +if (!strcmp (name, node->name)) +return node->dtpm; + } + +return NULL ; +} + +/** +* dtpm_get - Get a reference to a dtpm structure +* @name: the name of the dtpm device +* +* The function looks up in the list of the registered dtpm +* devices. If the dtpm device is not found, a virtual one is +* allocated. This function must be called to create a dtpm node in +* the powercap hierarchy. +* +* Return: a pointer to a dtpm structure, NULL if there is not enough +* memory +*/ +struct dtpm *dtpm_get (const char *name) +{ +struct dtpm *dtpm; + +mutex_lock (&dtpm_lock); + dtpm =__dtpm_lookup (name); +if (!dtpm) + dtpm =dtpm_alloc (NULL ); +else +kref_get (&dtpm->kref); +mutex_unlock (&dtpm_lock); + +return dtpm; +} + +static void dtpm_release (struct kref *kref) +{ + struct dtpm *dtpm = container_of(kref, struct dtpm, kref); + +kfree (dtpm); +} + +/** +* dtpm_put - Release a reference on a dtpm device +* @dtpm: a pointer to a dtpm structure +* +* Release the reference on the specified dtpm device. The last +* reference leads to a memory release. +*/ +void dtpm_put (struct dtpm *dtpm) +{ +kref_put (&dtpm->kref, dtpm_release); +} + +/** +* dtpm_register - Register the dtpm in the dtpm list +* @name: a name used as an identifier +* @dtpm: the dtpm node to be registered +* +* Stores the dtpm device in a list. +* +* Return: 0 on success, -EEXIST if the device name is already present +* in the list, -ENOMEM in case of memory allocation failure. +*/ +int dtpm_register (const char *name,struct dtpm *dtpm) +{ +struct dtpm_node *node; + +mutex_lock (&dtpm_lock); + +if (__dtpm_lookup (name)) { +mutex_unlock (&dtpm_lock); +return -EEXIST ; + } + + node =kzalloc (sizeof (*node),GFP_KERNEL ); +if (!node) +return -ENOMEM ; + + node->name =kstrdup (name,GFP_KERNEL ); +if (!node->name) { +kfree (node); +return -ENOMEM ; + } + + node->dtpm = dtpm; + +list_add (&node->node, &dtpm_list); + +pr_info ("Registered %s\n" , name); + +mutex_unlock (&dtpm_lock); + +return 0 ; +} + +/** +* dtpm_unregister - Remove the dtpm device from the list +* @name: the dtpm device name to be removed +* +* Remove the dtpm device from the list of the registered devices. +*/ +void dtpm_unregister (const char *name) +{ +struct dtpm_node *node; + +mutex_lock (&dtpm_lock); + +list_for_each_entry (node, &dtpm_list, node) { + +if (strcmp (name, node->name)) +continue ; + +list_del (&node->node); +kfree (node->name); +kfree (node); + +pr_info ("Unregistered %s\n" , name); + +break ; + } + +mutex_unlock (&dtpm_lock); +} +/** * dtpm_update_power - Update the power on the dtpm * @dtpm: a pointer to a dtpm structure to update
@@ -208,7 +346,7 @@ int dtpm_release_zone(struct powercap_zone *pcz)if (root == dtpm) root =NULL ; -kfree (dtpm); +dtpm_put (dtpm);return 0 ; }
@@ -370,6 +508,7 @@ struct dtpm *dtpm_alloc(struct dtpm_ops *ops)if (dtpm) {INIT_LIST_HEAD (&dtpm->children);INIT_LIST_HEAD (&dtpm->sibling); +kref_init (&dtpm->kref); dtpm->weight =1024 ; dtpm->ops = ops; }
@@ -378,28 +517,29 @@ struct dtpm *dtpm_alloc(struct dtpm_ops *ops) }/** -* dtpm_unregister - Unregister a dtpm node from the hierarchy tree -* @dtpm: a pointer to a dtpm structure corresponding to the node to be removed +* dtpm_destroy - Destroy a dtpm node from the hierarchy tree +* @dtpm: a pointer to a dtpm structure corresponding to the node to be +* removed and destroyed * * Call the underlying powercap unregister function. That will call * the release callback of the powercap zone. */ -void dtpm_unregister (struct dtpm *dtpm) +void dtpm_destroy (struct dtpm *dtpm) {powercap_unregister_zone (pct, &dtpm->zone); -pr_info ("Unregistered dtpm node '%s'\n" , dtpm->zone.name); +pr_info ("Destroyed dtpm node '%s'\n" , dtpm->zone.name); }/** -* dtpm_register - Register a dtpm node in the hierarchy tree +* dtpm_create - Create a dtpm node in the hierarchy tree * @name: a string specifying the name of the node * @dtpm: a pointer to a dtpm structure corresponding to the new node * @parent: a pointer to a dtpm structure corresponding to the parent node * * Create a dtpm node in the tree. If no parent is specified, the node * is the root node of the hierarchy. If the root node already exists, -* then the registration will fail. The powercap controller must be +* then the creation will fail. The powercap controller must be * initialized before calling this function. * * The dtpm structure must be initialized with the power numbers
@@ -413,7 +553,7 @@ void dtpm_unregister(struct dtpm *dtpm)* * parent have ops which are reserved for leaves * Other negative values are reported back from the powercap framework */ -int dtpm_register (const char *name,struct dtpm *dtpm,struct dtpm *parent) +int dtpm_create (const char *name,struct dtpm *dtpm,struct dtpm *parent) {struct powercap_zone *pcz;
@@ -457,7 +597,7 @@ int dtpm_register(const char *name, struct dtpm *dtpm, struct dtpm *parent)if (dtpm->ops && !dtpm->ops->upt_power_uw (dtpm))__dtpm_add_power (dtpm); -pr_info ("Registered dtpm node '%s' / %llu-%llu uW, \n" , +pr_info ("Created dtpm node '%s' / %llu-%llu uW, \n" , dtpm->zone.name, dtpm->power_min, dtpm->power_max);mutex_unlock (&dtpm_lock);
drivers/powercap/dtpm_cpu.c +2 -2
@@ -180,7 +180,7 @@ static int cpuhp_dtpm_cpu_online(unsigned int cpu)sprintf (name,"cpu%d-cpufreq" , dtpm_cpu->cpu); - ret =dtpm_register (name, dtpm,NULL ); + ret =dtpm_register (name, dtpm);if (ret) goto out_kfree_dtpm_cpu;
@@ -193,7 +193,7 @@ static int cpuhp_dtpm_cpu_online(unsigned int cpu)return 0 ; out_dtpm_unregister: -dtpm_unregister (dtpm); +dtpm_unregister (name); dtpm_cpu =NULL ; dtpm =NULL ;
include/linux/dtpm.h +10 -2
@@ -14,6 +14,7 @@struct dtpm {struct powercap_zone zone; +struct kref kref;struct dtpm *parent;struct list_head sibling;struct list_head children;
@@ -69,10 +70,17 @@ int dtpm_release_zone(struct powercap_zone *pcz);struct dtpm *dtpm_alloc (struct dtpm_ops *ops); -void dtpm_unregister (struct dtpm *dtpm); +void dtpm_destroy (struct dtpm *dtpm); -int dtpm_register (const char *name,struct dtpm *dtpm,struct dtpm *parent); +int dtpm_create (const char *name,struct dtpm *dtpm,struct dtpm *parent);int dtpm_register_cpu (struct dtpm *parent); +int dtpm_register (const char *name,struct dtpm *dtpm); + +void dtpm_unregister (const char *name); + +struct dtpm *dtpm_get (const char *name); + +void dtpm_put (struct dtpm *dtpm);#endif
Signature · 1 line
2.17.1
Re: [PATCH 2/5] powercap/drivers/dtpm: Create a registering system
Quoted 131 lines · Daniel Lezcano
A SoC can be differently structured depending on the platform and the kernel can not be aware of all the combinations, as well as the specific tweaks for a particular board.
The creation of the hierarchy must be delegated to userspace.
These changes provide a registering mechanism where the different subsystems will initialize their dtpm backends and register with a name the dtpm node in a list.
The next changes will provide an userspace interface to create hierachically the different nodes. Those will be created by name and found via the list filled by the different subsystem.
If a specified name is not found in the list, it is assumed to be a virtual node which will have children and the default is to allocate such node.
When the node register in the list, the function will be
dtpm_registerwhere the previous semantic was to create the node. Thus, the functions are renamed to reflect their purpose.Signed-off-by Daniel Lezcano <[email protected]>--- drivers/powercap/dtpm.c | 158 ++++++++++++++++++++++++++++++++++-- drivers/powercap/dtpm_cpu.c | 4 +- include/linux/dtpm.h | 12 ++- 3 files changed, 161 insertions(+), 13 deletions(-)drivers/powercap/dtpm.c +80
-0@@ -20,6 +20,7 @@#include <linux/dtpm.h> #include <linux/init.h> #include <linux/kernel.h> +#include <linux/kref.h> #include <linux/powercap.h> #include <linux/slab.h> #include <linux/mutex.h> @@ -34,6 +35,14 @@ static DEFINE_MUTEX(dtpm_lock);static struct powercap_control_type *pct;static struct dtpm *root; +struct dtpm_node { +const char *name; +struct dtpm *dtpm; +struct list_head node; +}; + +static LIST_HEAD (dtpm_list); +static int get_time_window_us (struct powercap_zone *pcz,int cid,u64 *window) {return -ENOSYS ;@@ -152,6 +161,135 @@ static int __dtpm_update_power(struct dtpm *dtpm)return ret; } +static struct dtpm *__dtpm_lookup (const char *name) +{ +struct dtpm_node *node; + +list_for_each_entry (node, &dtpm_list, node) { +if (!strcmp (name, node->name)) +return node->dtpm; + } + +return NULL ; +} + +/** +* dtpm_get - Get a reference to a dtpm structure +* @name: the name of the dtpm device +* +* The function looks up in the list of the registered dtpm +* devices. If the dtpm device is not found, a virtual one is +* allocated. This function must be called to create a dtpm node in +* the powercap hierarchy. +* +* Return: a pointer to a dtpm structure, NULL if there is not enough +* memory +*/ +struct dtpm *dtpm_get (const char *name) +{ +struct dtpm *dtpm; + +mutex_lock (&dtpm_lock); + dtpm =__dtpm_lookup (name); +if (!dtpm) + dtpm =dtpm_alloc (NULL ); +else +kref_get (&dtpm->kref); +mutex_unlock (&dtpm_lock); + +return dtpm; +} + +static void dtpm_release (struct kref *kref) +{ + struct dtpm *dtpm = container_of(kref, struct dtpm, kref); + +kfree (dtpm); +} + +/** +* dtpm_put - Release a reference on a dtpm device +* @dtpm: a pointer to a dtpm structure +* +* Release the reference on the specified dtpm device. The last +* reference leads to a memory release. +*/ +void dtpm_put (struct dtpm *dtpm) +{ +kref_put (&dtpm->kref, dtpm_release); +} + +/** +* dtpm_register - Register the dtpm in the dtpm list +* @name: a name used as an identifier +* @dtpm: the dtpm node to be registered +* +* Stores the dtpm device in a list. +* +* Return: 0 on success, -EEXIST if the device name is already present +* in the list, -ENOMEM in case of memory allocation failure. +*/ +int dtpm_register (const char *name,struct dtpm *dtpm) +{ +struct dtpm_node *node;
drivers/powercap/dtpm.c static int __dtpm_update_power(struct dtpm *dtpm) · line 244
Quoted 10 lines
drivers/powercap/dtpm.c +10
-0+ +mutex_lock (&dtpm_lock); + +if (__dtpm_lookup (name)) { +mutex_unlock (&dtpm_lock); +return -EEXIST ; + } + + node =kzalloc (sizeof (*node),GFP_KERNEL ); +if (!node)
mutex_unlock()
drivers/powercap/dtpm.c static int __dtpm_update_power(struct dtpm *dtpm) · line 249
Quoted 5 lines
drivers/powercap/dtpm.c +5
-0+return -ENOMEM ; + + node->name =kstrdup (name,GFP_KERNEL ); +if (!node->name) { +kfree (node);
mutex_unlock()
Quoted 17 lines
drivers/powercap/dtpm.c +17
-0+return -ENOMEM ; + } + + node->dtpm = dtpm; + +list_add (&node->node, &dtpm_list); + +pr_info ("Registered %s\n" , name); + +mutex_unlock (&dtpm_lock); + +return 0 ; +} + +/** +* dtpm_unregister - Remove the dtpm device from the list +* @name: the dtpm device name to be removed
drivers/powercap/dtpm.c static int __dtpm_update_power(struct dtpm *dtpm) · line 276
Quoted 10 lines
drivers/powercap/dtpm.c +10
-0+* +* Remove the dtpm device from the list of the registered devices. +*/ +void dtpm_unregister (const char *name) +{ +struct dtpm_node *node; + +mutex_lock (&dtpm_lock); + +list_for_each_entry (node, &dtpm_list, node) {
At first glance list_for_each_entry_safe() is needed here, but this code is safe. The node is remove and the loop stops.
Quoted 145 lines
drivers/powercap/dtpm.c +27
-9+ +if (strcmp (name, node->name)) +continue ; + +list_del (&node->node); +kfree (node->name); +kfree (node); + +pr_info ("Unregistered %s\n" , name); + +break ; + } + +mutex_unlock (&dtpm_lock); +} +/** * dtpm_update_power - Update the power on the dtpm * @dtpm: a pointer to a dtpm structure to update @@ -208,7 +346,7 @@ int dtpm_release_zone(struct powercap_zone *pcz)if (root == dtpm) root =NULL ; -kfree (dtpm); +dtpm_put (dtpm);return 0 ; }@@ -370,6 +508,7 @@ struct dtpm *dtpm_alloc(struct dtpm_ops *ops)if (dtpm) {INIT_LIST_HEAD (&dtpm->children);INIT_LIST_HEAD (&dtpm->sibling); +kref_init (&dtpm->kref); dtpm->weight =1024 ; dtpm->ops = ops; }@@ -378,28 +517,29 @@ struct dtpm *dtpm_alloc(struct dtpm_ops *ops) }/** -* dtpm_unregister - Unregister a dtpm node from the hierarchy tree -* @dtpm: a pointer to a dtpm structure corresponding to the node to be removed +* dtpm_destroy - Destroy a dtpm node from the hierarchy tree +* @dtpm: a pointer to a dtpm structure corresponding to the node to be +* removed and destroyed * * Call the underlying powercap unregister function. That will call * the release callback of the powercap zone. */ -void dtpm_unregister (struct dtpm *dtpm) +void dtpm_destroy (struct dtpm *dtpm) {powercap_unregister_zone (pct, &dtpm->zone); -pr_info ("Unregistered dtpm node '%s'\n" , dtpm->zone.name); +pr_info ("Destroyed dtpm node '%s'\n" , dtpm->zone.name); }/** -* dtpm_register - Register a dtpm node in the hierarchy tree +* dtpm_create - Create a dtpm node in the hierarchy tree * @name: a string specifying the name of the node * @dtpm: a pointer to a dtpm structure corresponding to the new node * @parent: a pointer to a dtpm structure corresponding to the parent node * * Create a dtpm node in the tree. If no parent is specified, the node * is the root node of the hierarchy. If the root node already exists, -* then the registration will fail. The powercap controller must be +* then the creation will fail. The powercap controller must be * initialized before calling this function. * * The dtpm structure must be initialized with the power numbers @@ -413,7 +553,7 @@ void dtpm_unregister(struct dtpm *dtpm)* * parent have ops which are reserved for leaves * Other negative values are reported back from the powercap framework */ -int dtpm_register (const char *name,struct dtpm *dtpm,struct dtpm *parent) +int dtpm_create (const char *name,struct dtpm *dtpm,struct dtpm *parent) {struct powercap_zone *pcz;@@ -457,7 +597,7 @@ int dtpm_register(const char *name, struct dtpm *dtpm, struct dtpm *parent)if (dtpm->ops && !dtpm->ops->upt_power_uw (dtpm))__dtpm_add_power (dtpm); -pr_info ("Registered dtpm node '%s' / %llu-%llu uW, \n" , +pr_info ("Created dtpm node '%s' / %llu-%llu uW, \n" , dtpm->zone.name, dtpm->power_min, dtpm->power_max);mutex_unlock (&dtpm_lock);drivers/powercap/dtpm_cpu.c +2
-2@@ -180,7 +180,7 @@ static int cpuhp_dtpm_cpu_online(unsigned int cpu)sprintf (name,"cpu%d-cpufreq" , dtpm_cpu->cpu); - ret =dtpm_register (name, dtpm,NULL ); + ret =dtpm_register (name, dtpm);if (ret) goto out_kfree_dtpm_cpu;@@ -193,7 +193,7 @@ static int cpuhp_dtpm_cpu_online(unsigned int cpu)return 0 ; out_dtpm_unregister: -dtpm_unregister (dtpm); +dtpm_unregister (name); dtpm_cpu =NULL ; dtpm =NULL ;include/linux/dtpm.h +10
-2@@ -14,6 +14,7 @@struct dtpm {struct powercap_zone zone; +struct kref kref;struct dtpm *parent;struct list_head sibling;struct list_head children;@@ -69,10 +70,17 @@ int dtpm_release_zone(struct powercap_zone *pcz);struct dtpm *dtpm_alloc (struct dtpm_ops *ops); -void dtpm_unregister (struct dtpm *dtpm); +void dtpm_destroy (struct dtpm *dtpm); -int dtpm_register (const char *name,struct dtpm *dtpm,struct dtpm *parent); +int dtpm_create (const char *name,struct dtpm *dtpm,struct dtpm *parent);int dtpm_register_cpu (struct dtpm *parent); +int dtpm_register (const char *name,struct dtpm *dtpm); + +void dtpm_unregister (const char *name); + +struct dtpm *dtpm_get (const char *name); + +void dtpm_put (struct dtpm *dtpm);#endif
PATCH 3/5 powercap/drivers/dtpm: Simplify the dtpm table
In reply to Daniel LezcanoThe dtpm table is an array of pointers, that forces the user of the table to define initdata along with the declaration of the table entry. It is more efficient to create an array of dtpm structure, so the declaration of the table entry can be done by initializing the different fields.
--- drivers/powercap/dtpm.c | 4 ++-- drivers/powercap/dtpm_cpu.c | 4 +++- include/linux/dtpm.h | 22 +++++++++------------- 3 files changed, 14 insertions(+), 16 deletions(-)
drivers/powercap/dtpm.c +2 -2
@@ -607,7 +607,7 @@ int dtpm_create(const char *name, struct dtpm *dtpm, struct dtpm *parent)static int __init dtpm_init (void ) { -struct dtpm_descr **dtpm_descr; +struct dtpm_descr *dtpm_descr; pct =powercap_register_control_type (NULL ,"dtpm" ,NULL );if (IS_ERR (pct)) {
@@ -616,7 +616,7 @@ static int __init dtpm_init(void) }for_each_dtpm_table (dtpm_descr) - (*dtpm_descr)->init (*dtpm_descr); + dtpm_descr->init ();return 0 ; }
drivers/powercap/dtpm_cpu.c +3 -1
@@ -207,7 +207,7 @@ static int cpuhp_dtpm_cpu_online(unsigned int cpu)return ret; } -int dtpm_register_cpu (struct dtpm *parent) +static int __init dtpm_cpu_init (void ) {int ret;
@@ -223,3 +223,5 @@ int dtpm_register_cpu(struct dtpm *parent)return 0 ; } + +DTPM_DECLARE (dtpm_cpu, dtpm_cpu_init);
include/linux/dtpm.h +9 -13
@@ -34,25 +34,23 @@ struct dtpm_ops {void (*release)(struct dtpm *); }; -struct dtpm_descr ; - -typedef int (*dtpm_init_t )(struct dtpm_descr *); +typedef int (*dtpm_init_t )(void );struct dtpm_descr { -struct dtpm *parent; -const char *name;dtpm_init_t init; };/* Init section thermal table */ -extern struct dtpm_descr *__dtpm_table[]; -extern struct dtpm_descr *__dtpm_table_end[]; +extern struct dtpm_descr __dtpm_table[]; +extern struct dtpm_descr __dtpm_table_end[]; -#define DTPM_TABLE_ENTRY (name) \ - static typeof(name) *__dtpm_table_entry_##name \ - __used __section("__dtpm_table") = &name +#define DTPM_TABLE_ENTRY (name, __init) \ + static struct dtpm_descr __dtpm_table_entry_##name \ + __used __section("__dtpm_table") = { \ + .init = __init, \ + } -#define DTPM_DECLARE (name) DTPM_TABLE_ENTRY(name) +#define DTPM_DECLARE (name, init) DTPM_TABLE_ENTRY(name, init)#define for_each_dtpm_table (__dtpm) \ for (__dtpm = __dtpm_table; \
@@ -74,8 +72,6 @@ void dtpm_destroy(struct dtpm *dtpm);int dtpm_create (const char *name,struct dtpm *dtpm,struct dtpm *parent); -int dtpm_register_cpu (struct dtpm *parent); -int dtpm_register (const char *name,struct dtpm *dtpm);void dtpm_unregister (const char *name);
Signature · 1 line
2.17.1
Re: [PATCH 3/5] powercap/drivers/dtpm: Simplify the dtpm table
Quoted 6 lines · Daniel Lezcano
The dtpm table is an array of pointers, that forces the user of the table to define initdata along with the declaration of the table entry. It is more efficient to create an array of dtpm structure, so the declaration of the table entry can be done by initializing the different fields.
Quoted 7 lines
Signed-off-by Daniel Lezcano <[email protected]>--- drivers/powercap/dtpm.c | 4 ++-- drivers/powercap/dtpm_cpu.c | 4 +++- include/linux/dtpm.h | 22 +++++++++------------- 3 files changed, 14 insertions(+), 16 deletions(-)
LGTM
PATCH 4/5 powercap/drivers/dtpm: Use container_of instead of a private data field
In reply to Daniel LezcanoThe dtpm framework provides an API to allocate a dtpm node. However when a backend dtpm driver needs to allocate a dtpm node it must define its own structure and store the pointer of this structure in the private field of the dtpm structure.
It is more elegant to use the container_of macro and add the dtpm structure inside the dtpm backend specific structure. The code will be able to deal properly with the dtpm structure as a generic entity, making all this even more self-encapsulated.
The dtpm_alloc() function does no longer make sense as the dtpm structure will be allocated when allocating the device specific dtpm structure. The dtpm_init() is provided instead.
--- drivers/powercap/dtpm.c | 27 ++++++++++------------ drivers/powercap/dtpm_cpu.c | 46 ++++++++++++++++++------------------- include/linux/dtpm.h | 3 +-- 3 files changed, 35 insertions(+), 41 deletions(-)
drivers/powercap/dtpm.c +12 -15
@@ -191,10 +191,13 @@ struct dtpm *dtpm_get(const char *name)mutex_lock (&dtpm_lock); dtpm =__dtpm_lookup (name); -if (!dtpm) - dtpm =dtpm_alloc (NULL ); -else +if (!dtpm) { + dtpm =kzalloc (sizeof (*dtpm),GFP_KERNEL ); +if (dtpm) +dtpm_init (dtpm,NULL ); + }else {kref_get (&dtpm->kref); + }mutex_unlock (&dtpm_lock);return dtpm;
@@ -495,16 +498,12 @@ static struct powercap_zone_ops zone_ops = { };/** -* dtpm_alloc - Allocate and initialize a dtpm struct -* @name: a string specifying the name of the node -* -* Return: a struct dtpm pointer, NULL in case of error +* dtpm_init - Allocate and initialize a dtpm struct +* @dtpm: The dtpm struct pointer to be initialized +* @ops: The dtpm device specific ops, NULL for a virtual node */ -struct dtpm *dtpm_alloc (struct dtpm_ops *ops) +void dtpm_init (struct dtpm *dtpm,struct dtpm_ops *ops) { -struct dtpm *dtpm; - - dtpm =kzalloc (sizeof (*dtpm),GFP_KERNEL );if (dtpm) {INIT_LIST_HEAD (&dtpm->children);INIT_LIST_HEAD (&dtpm->sibling);
@@ -512,8 +511,6 @@ struct dtpm *dtpm_alloc(struct dtpm_ops *ops) dtpm->weight =1024 ; dtpm->ops = ops; } - -return dtpm; }/**
@@ -605,7 +602,7 @@ int dtpm_create(const char *name, struct dtpm *dtpm, struct dtpm *parent)return 0 ; } -static int __init dtpm_init (void ) +static int __init init_dtpm (void ) {struct dtpm_descr *dtpm_descr;
@@ -620,4 +617,4 @@ static int __init dtpm_init(void)return 0 ; } -late_initcall (dtpm_init); +late_initcall (init_dtpm);
drivers/powercap/dtpm_cpu.c +22 -24
122 lines not on this page · raw message
include/linux/dtpm.h +1 -2
@@ -24,7 +24,6 @@ struct dtpm {u64 power_max;u64 power_min;int weight; -void *private; };struct dtpm_ops {
@@ -66,7 +65,7 @@ int dtpm_update_power(struct dtpm *dtpm);int dtpm_release_zone (struct powercap_zone *pcz); -struct dtpm *dtpm_alloc (struct dtpm_ops *ops); +void dtpm_init (struct dtpm *dtpm,struct dtpm_ops *ops);void dtpm_destroy (struct dtpm *dtpm);
Signature · 1 line
2.17.1
Re: [PATCH 4/5] powercap/drivers/dtpm: Use container_of instead of a private data field
Quoted 14 lines · Daniel Lezcano
The dtpm framework provides an API to allocate a dtpm node. However when a backend dtpm driver needs to allocate a dtpm node it must define its own structure and store the pointer of this structure in the private field of the dtpm structure.
It is more elegant to use the
container_ofmacro and add the dtpm structure inside the dtpm backend specific structure. The code will be able to deal properly with the dtpm structure as a generic entity, making all this even more self-encapsulated.The
dtpm_alloc()function does no longer make sense as the dtpm structure will be allocated when allocating the device specific dtpm structure. Thedtpm_init()is provided instead.
Quoted 7 lines
Signed-off-by Daniel Lezcano <[email protected]>--- drivers/powercap/dtpm.c | 27 ++++++++++------------ drivers/powercap/dtpm_cpu.c | 46 ++++++++++++++++++------------------- include/linux/dtpm.h | 3 +-- 3 files changed, 35 insertions(+), 41 deletions(-)
PATCH 5/5 powercap/drivers/dtpm: Scale the power with the load
In reply to Daniel LezcanoCurrently the power consumption is based on the current OPP power assuming the entire performance domain is fully loaded.
That gives very gross power estimation and we can do much better by using the load to scale the power consumption.
Use the utilization to normalize and scale the power usage over the max possible power.
Tested on a rock960 with 2 big CPUS, the power consumption estimation conforms with the expected one.
Before this change:
~$ ~/dhrystone -t 1 -l 10000&
~$ cat /sys/devices/virtual/powercap/dtpm/dtpm:0/dtpm:0:1/constraint_0_max_power_uw 2260000
After this change:
~$ ~/dhrystone -t 1 -l 10000&
~$ cat /sys/devices/virtual/powercap/dtpm/dtpm:0/dtpm:0:1/constraint_0_max_power_uw 1130000
~$ ~/dhrystone -t 2 -l 10000&
~$ cat /sys/devices/virtual/powercap/dtpm/dtpm:0/dtpm:0:1/constraint_0_max_power_uw 2260000
--- drivers/powercap/dtpm_cpu.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-)
drivers/powercap/dtpm_cpu.c +17 -4
@@ -68,27 +68,40 @@ static u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit)return power_limit; } +static u64 scale_pd_power_uw (struct cpumask *cpus,u64 power) +{ +unsigned long max, util; +int cpu, load =0 ; + +for_each_cpu (cpu, cpus) { + max =arch_scale_cpu_capacity (cpu); + util =sched_cpu_util (cpu, max); + load += ((util *100 ) / max); + } + +return (power * load) /100 ; +} +static u64 get_pd_power_uw (struct dtpm *dtpm) {struct dtpm_cpu *dtpm_cpu =to_dtpm_cpu (dtpm);struct em_perf_domain *pd;struct cpumask cpus;unsigned long freq; -int i, nr_cpus; +int i; pd =em_cpu_get (dtpm_cpu->cpu); freq =cpufreq_quick_get (dtpm_cpu->cpu);cpumask_and (&cpus, cpu_online_mask,to_cpumask (pd->cpus)); - nr_cpus =cpumask_weight (&cpus);for (i =0 ; i < pd->nr_perf_states; i++) {if (pd->table[i].frequency < freq)continue ; -return pd->table[i].power * -MICROWATT_PER_MILLIWATT * nr_cpus; +return scale_pd_power_uw (&cpus, pd->table[i].power * +MICROWATT_PER_MILLIWATT ); }return 0 ;
Signature · 1 line
2.17.1
Re: [PATCH 5/5] powercap/drivers/dtpm: Scale the power with the load
Hi Daniel,
I've started reviewing the series, please find some comments below.
Quoted 38 lines · Daniel Lezcano
Currently the power consumption is based on the current OPP power assuming the entire performance domain is fully loaded.
That gives very gross power estimation and we can do much better by using the load to scale the power consumption.
Use the utilization to normalize and scale the power usage over the max possible power.
Tested on a rock960 with 2 big CPUS, the power consumption estimation conforms with the expected one.
Before this change:
~$ ~/dhrystone -t 1 -l 10000&
~$ cat /sys/devices/virtual/powercap/dtpm/dtpm:0/dtpm:0:1/constraint_0_max_power_uw 2260000After this change:
~$ ~/dhrystone -t 1 -l 10000&
~$ cat /sys/devices/virtual/powercap/dtpm/dtpm:0/dtpm:0:1/constraint_0_max_power_uw 1130000~$ ~/dhrystone -t 2 -l 10000&
~$ cat /sys/devices/virtual/powercap/dtpm/dtpm:0/dtpm:0:1/constraint_0_max_power_uw 2260000Signed-off-by Daniel Lezcano <[email protected]>--- drivers/powercap/dtpm_cpu.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-)drivers/powercap/dtpm_cpu.c +0
-0
drivers/powercap/dtpm_cpu.c static u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit) · line 71
Quoted 4 lines
drivers/powercap/dtpm_cpu.c +1
-0@@ -68,27 +68,40 @@ static u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit)return power_limit; } +static u64 scale_pd_power_uw (struct cpumask *cpus,u64 power)
renamed 'cpus' into 'pd_mask', see below
drivers/powercap/dtpm_cpu.c static u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit) · line 74
Quoted 3 lines
drivers/powercap/dtpm_cpu.c +3
-0+{ +unsigned long max, util; +int cpu, load =0 ;
IMHO 'int load' looks odd when used with 'util' and 'max'. I would put in the line above to have them all the same type and renamed to 'sum_util'.
drivers/powercap/dtpm_cpu.c static u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit) · line 76
Quoted 2 lines
drivers/powercap/dtpm_cpu.c +2
-0+ +for_each_cpu (cpu, cpus) {
I would avoid the temporary CPU mask in the get_pd_power_uw() with this modified loop:
for_each_cpu_and(cpu, pd_mask, cpu_online_mask) {
drivers/powercap/dtpm_cpu.c static u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit) · line 79
Quoted 3 lines
drivers/powercap/dtpm_cpu.c +3
-0+ max =arch_scale_cpu_capacity (cpu); + util =sched_cpu_util (cpu, max); + load += ((util *100 ) / max);
Below you can find 3 optimizations. Since we are not in the hot path here, it's up to if you would like to use all/some of them or just ignore.
1st optimization.
If we use 'load += (util << 10) / max' in the loop, then we could avoid div by 100 and use a right shift:
(power * load) >> 10
2nd optimization.
Since we use EM CPU mask, which span all CPUs with the same arch_scale_cpu_capacity(), you can avoid N divs inside the loop and do it once, below the loop.
3rd optimization.
If we just simply add all 'util' into 'sum_util' (no mul or div in the loop), then we might just have simple macro
#define CALC_POWER_USAGE(power, sum_util, max) \ (((power * (sum_util << 10)) / max) >> 10)
drivers/powercap/dtpm_cpu.c static u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit) · line 89
Quoted 10 lines
drivers/powercap/dtpm_cpu.c +5
-0+ } + +return (power * load) /100 ; +} +static u64 get_pd_power_uw (struct dtpm *dtpm) {struct dtpm_cpu *dtpm_cpu =to_dtpm_cpu (dtpm);struct em_perf_domain *pd;struct cpumask cpus;
Since we don't need the 'nr_cpus' we also don't need the cpumask which occupy stack; Maybe use
struct cpumask *pd_mask;
then
drivers/powercap/dtpm_cpu.c static u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit) · line 96
Quoted 8 lines
drivers/powercap/dtpm_cpu.c +1
-1unsigned long freq; -int i, nr_cpus; +int i; pd =em_cpu_get (dtpm_cpu->cpu); freq =cpufreq_quick_get (dtpm_cpu->cpu);cpumask_and (&cpus, cpu_online_mask,to_cpumask (pd->cpus));
remove ^^^^^ and set pd_mask = em_span_cpus(pd);
drivers/powercap/dtpm_cpu.c static u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit) · line 104
Quoted 11 lines
drivers/powercap/dtpm_cpu.c +2
-3- nr_cpus =cpumask_weight (&cpus);for (i =0 ; i < pd->nr_perf_states; i++) {if (pd->table[i].frequency < freq)continue ; -return pd->table[i].power * -MICROWATT_PER_MILLIWATT * nr_cpus; +return scale_pd_power_uw (&cpus, pd->table[i].power * +MICROWATT_PER_MILLIWATT );
Instead of '&cpus' I would put 'pd_mask' and that should do the job.
drivers/powercap/dtpm_cpu.c static u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit) · line 107
Quoted 4 lines
drivers/powercap/dtpm_cpu.c +0
-0}return 0 ;
Apart from that, the design idea with util looks good.
Regards,
Lukasz
Re: [PATCH 5/5] powercap/drivers/dtpm: Scale the power with the load
Hi Lukasz,
thanks for your comments, one question below.
On 09/03/2021 11:01, Lukasz Luba wrote:
[ ... ]
Quoted 39 lines
Quoted 1 line
+static u64
scale_pd_power_uw(struct cpumask *cpus, u64 power)renamed 'cpus' into '
pd_mask', see belowQuoted 3 lines
+{
+ unsigned long max, util;
+ int cpu, load = 0;IMHO 'int load' looks odd when used with 'util' and 'max'. I would put in the line above to have them all the same type and renamed to '
sum_util'.Quoted 2 lines
+
+for_each_cpu(cpu, cpus){I would avoid the temporary CPU mask in the
get_pd_power_uw()with this modified loop:
for_each_cpu_and(cpu, pd_mask, cpu_online_mask){Quoted 3 lines
+ max =
arch_scale_cpu_capacity(cpu);
+ util =sched_cpu_util(cpu, max);
+ load += ((util * 100) / max);Below you can find 3 optimizations. Since we are not in the hot path here, it's up to if you would like to use all/some of them or just ignore.
1st optimization.
If we use 'load += (util << 10) / max' in the loop, then we could avoid div by 100 and use a right shift:
(power * load) >> 102nd optimization.
Since we use EM CPU mask, which span all CPUs with the samearch_scale_cpu_capacity(), you can avoid N divs inside the loop and do it once, below the loop.
Quoted 6 lines
3rd optimization.
If we just simply add all 'util' into 'sum_util' (no mul or div in the loop), then we might just have simple macro#define
CALC_POWER_USAGE(power, sum_util, max)\
(((power * (sum_util<< 10)) / max) >> 10)
I don't understand the 'max' division, I was expecting here something
like: ((sum_util << 10) / sum_max) >> 10)
no ?
Signature · 5 lines
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook | <http://twitter.com/#!/linaroorg> Twitter | <http://www.linaro.org/linaro-blog/> Blog
Re: [PATCH 5/5] powercap/drivers/dtpm: Scale the power with the load
Quoted 52 lines · Daniel Lezcano
Hi Lukasz,
thanks for your comments, one question below.
On 09/03/2021 11:01, Lukasz Luba wrote:
[ ... ]
Quoted 43 lines
Quoted 1 line
+static u64
scale_pd_power_uw(struct cpumask *cpus, u64 power)renamed 'cpus' into '
pd_mask', see belowQuoted 3 lines
+{
+ unsigned long max, util;
+ int cpu, load = 0;IMHO 'int load' looks odd when used with 'util' and 'max'. I would put in the line above to have them all the same type and renamed to '
sum_util'.Quoted 2 lines
+
+for_each_cpu(cpu, cpus){I would avoid the temporary CPU mask in the
get_pd_power_uw()with this modified loop:
for_each_cpu_and(cpu, pd_mask, cpu_online_mask){Quoted 3 lines
+ max =
arch_scale_cpu_capacity(cpu);
+ util =sched_cpu_util(cpu, max);
+ load += ((util * 100) / max);Below you can find 3 optimizations. Since we are not in the hot path here, it's up to if you would like to use all/some of them or just ignore.
1st optimization.
If we use 'load += (util << 10) / max' in the loop, then we could avoid div by 100 and use a right shift:
(power * load) >> 102nd optimization.
Since we use EM CPU mask, which span all CPUs with the samearch_scale_cpu_capacity(), you can avoid N divs inside the loop and do it once, below the loop.3rd optimization.
If we just simply add all 'util' into 'sum_util' (no mul or div in the loop), then we might just have simple macro
Quoted 8 lines
Quoted 2 lines
#define
CALC_POWER_USAGE(power, sum_util, max)\
(((power * (sum_util<< 10)) / max) >> 10)I don't understand the 'max' division, I was expecting here something
like: ((sum_util<< 10) /sum_max) >> 10)no ?
No, it should be single 'max', which is in range 0..1024. We would like to calculate the power for the whole perf domain, e.g. 4 CPUs almost fully utilized would have util ~1000, then total power should be around ~4 * EM_table[i].power. This '~4' is coming from 4 utils divided by one max util
4000 / 1024
The 'max' in the equation can be put before the bracket, as well as 'power'.
If we had floating point number, simple power for cpu1, cpu2, cpuN would be just:power_1 = power * util_1 / maxpower_2 = power * util_2 / maxpower_N = power * util_N / max
(since they have the same 'max' capacity and the same EM 'power')
The total domain power would be:total_power = power_1 + power_2 + ... + power_N
which is:total_power = (power * util_1 / max) + (power * util_2 / max) + ... +
+ (power * util_N / max)
put the 'power' and 'max' before the bracket:total_power = power * (util_1 + util_2 + ... + util_N) * (1/max)
introduce the 'sum_util':sum_util = util_1 + util_2 + ... + util_N
then:total_power = power * sum_util / max
Unfortunately, we don't use floating point, so temporary fixed point tricks, thus the '<< 10' and '>> 10' avoid some errors
Re: [PATCH 5/5] powercap/drivers/dtpm: Scale the power with the load
In reply to Lukasz LubaQuoted 91 lines · Lukasz Luba
Hi Daniel,
I've started reviewing the series, please find some comments below.
Quoted 44 lines · Daniel Lezcano
Currently the power consumption is based on the current OPP power assuming the entire performance domain is fully loaded.
That gives very gross power estimation and we can do much better by using the load to scale the power consumption.
Use the utilization to normalize and scale the power usage over the max possible power.
Tested on a rock960 with 2 big CPUS, the power consumption estimation conforms with the expected one.
Before this change:
~$ ~/dhrystone -t 1 -l 10000&
~$ cat /sys/devices/virtual/powercap/dtpm/dtpm:0/dtpm:0:1/constraint_0_max_power_uw2260000
After this change:
~$ ~/dhrystone -t 1 -l 10000&
~$ cat /sys/devices/virtual/powercap/dtpm/dtpm:0/dtpm:0:1/constraint_0_max_power_uw1130000
~$ ~/dhrystone -t 2 -l 10000&
~$ cat /sys/devices/virtual/powercap/dtpm/dtpm:0/dtpm:0:1/constraint_0_max_power_uw2260000
Signed-off-by Daniel Lezcano <[email protected]>---
drivers/powercap/dtpm_cpu.c| 21 +++++++++++++++++----1 file changed, 17 insertions(+), 4 deletions(-)drivers/powercap/dtpm_cpu.c +0
-0drivers/powercap/dtpm_cpu.c static u64 set_pd_power_limit(struct dtpm *dtpm, · line 71
Quoted 4 lines
drivers/powercap/dtpm_cpu.c +0
-0@@ -68,27 +68,40 @@ static u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit) return power_limit; } +static u64 scale_pd_power_uw(struct cpumask *cpus, u64 power)renamed 'cpus' into '
pd_mask', see belowdrivers/powercap/dtpm_cpu.c static u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit) · line 74
Quoted 3 lines
drivers/powercap/dtpm_cpu.c +3
-0+{ + unsigned long max, util; + int cpu, load = 0;IMHO 'int load' looks odd when used with 'util' and 'max'. I would put in the line above to have them all the same type and renamed to '
sum_util'.drivers/powercap/dtpm_cpu.c static u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit) · line 76
Quoted 2 lines
drivers/powercap/dtpm_cpu.c +2
-0+ + for_each_cpu(cpu, cpus) {I would avoid the temporary CPU mask in the
get_pd_power_uw()with this modified loop:
for_each_cpu_and(cpu, pd_mask, cpu_online_mask){drivers/powercap/dtpm_cpu.c static u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit) · line 79
Quoted 3 lines
drivers/powercap/dtpm_cpu.c +3
-0+ max = arch_scale_cpu_capacity(cpu); + util = sched_cpu_util(cpu, max); + load += ((util * 100) / max);Below you can find 3 optimizations. Since we are not in the hot path here, it's up to if you would like to use all/some of them or just ignore.
1st optimization.
If we use 'load += (util << 10) / max' in the loop, then we could avoid div by 100 and use a right shift:
(power * load) >> 102nd optimization.
Since we use EM CPU mask, which span all CPUs with the samearch_scale_cpu_capacity(), you can avoid N divs inside the loop and do it once, below the loop.
Quoted 6 lines
3rd optimization.
If we just simply add all 'util' into 'sum_util' (no mul or div in the loop), then we might just have simple macro#define
CALC_POWER_USAGE(power, sum_util, max)\
(((power * (sum_util<< 10)) / max) >> 10)
static u64 scale_pd_power_uw(struct cpumask *pd_mask, u64 power)
{
unsigned long max, sum_max = 0, sum_util = 0;
int cpu;
for_each_cpu_and(cpu, pd_mask, cpu_online_mask) {
max = arch_scale_cpu_capacity(cpu);
sum_util += sched_cpu_util(cpu, max);
sum_max += max;
}
return (power * ((sum_util << 10) / sum_max)) >> 10;
}??
Signature · 5 lines
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook | <http://twitter.com/#!/linaroorg> Twitter | <http://www.linaro.org/linaro-blog/> Blog
Quoted 14 lines · Daniel Lezcano
In order to increase the self-encapsulation of the dtpm generic code, the following changes are adding a power update ops to the dtpm ops. That allows the generic code to call directly the dtpm backend function to update the power values.
The power update function does compute the power characteristics when the function is invoked. In the case of the CPUs, the power consumption depends on the number of online CPUs. The online CPUs mask is not up to date at
CPUHP_AP_ONLINE_DYNstate in the tear down callback. That is the reason why the online / offline are at separate state. As there is already an existing state for DTPM, this one is only moved to the DEAD state, so there is no addition of new state with these changes.
Quoted 4 lines
That simplifies the code for the next changes and results in a more self-encapsulated code.
Signed-off-by Daniel Lezcano <[email protected]>
Is there any comment on this series ?
Quoted 381 lines
--- drivers/powercap/dtpm.c | 54 ++++++++-------- drivers/powercap/dtpm_cpu.c | 124 +++++++++++++----------------------- include/linux/cpuhotplug.h | 2 +- include/linux/dtpm.h | 3 +- 4 files changed, 76 insertions(+), 107 deletions(-)drivers/powercap/dtpm.c +27
-27@@ -116,8 +116,6 @@ static void __dtpm_sub_power(struct dtpm *dtpm) parent->power_limit -= dtpm->power_limit; parent = parent->parent; } - -__dtpm_rebalance_weight (root); }static void __dtpm_add_power (struct dtpm *dtpm)@@ -130,45 +128,45 @@ static void __dtpm_add_power(struct dtpm *dtpm) parent->power_limit += dtpm->power_limit; parent = parent->parent; } +} + +static int __dtpm_update_power (struct dtpm *dtpm) +{ +int ret; + +__dtpm_sub_power (dtpm); -__dtpm_rebalance_weight (root); + ret = dtpm->ops->upt_power_uw (dtpm); +if (ret) +pr_err ("Failed to update power for '%s': %d\n" , + dtpm->zone.name, ret); + +if (!test_bit (DTPM_POWER_LIMIT_FLAG , &dtpm->flags)) + dtpm->power_limit = dtpm->power_max; + +__dtpm_add_power (dtpm); + +if (root) +__dtpm_rebalance_weight (root); + +return ret; }/** * dtpm_update_power - Update the power on the dtpm * @dtpm: a pointer to a dtpm structure to update -* @power_min: a u64 representing the new power_min value -* @power_max: a u64 representing the new power_max value * * Function to update the power values of the dtpm node specified in * parameter. These new values will be propagated to the tree. * * Return: zero on success, -EINVAL if the values are inconsistent */ -int dtpm_update_power (struct dtpm *dtpm,u64 power_min,u64 power_max) +int dtpm_update_power (struct dtpm *dtpm) { -int ret =0 ; +int ret;mutex_lock (&dtpm_lock); - -if (power_min == dtpm->power_min && power_max == dtpm->power_max) - goto unlock; - -if (power_max < power_min) { - ret = -EINVAL ; - goto unlock; - } - -__dtpm_sub_power (dtpm); - - dtpm->power_min = power_min; - dtpm->power_max = power_max; -if (!test_bit (DTPM_POWER_LIMIT_FLAG , &dtpm->flags)) - dtpm->power_limit = power_max; - -__dtpm_add_power (dtpm); - -unlock: + ret =__dtpm_update_power (dtpm);mutex_unlock (&dtpm_lock);return ret;@@ -436,6 +434,7 @@ int dtpm_register(const char *name, struct dtpm *dtpm, struct dtpm *parent)if (dtpm->ops && !(dtpm->ops->set_power_uw && dtpm->ops->get_power_uw && + dtpm->ops->upt_power_uw && dtpm->ops->release))return -EINVAL ;@@ -455,7 +454,8 @@ int dtpm_register(const char *name, struct dtpm *dtpm, struct dtpm *parent) root = dtpm; } -__dtpm_add_power (dtpm); +if (dtpm->ops && !dtpm->ops->upt_power_uw (dtpm)) +__dtpm_add_power (dtpm);pr_info ("Registered dtpm node '%s' / %llu-%llu uW, \n" , dtpm->zone.name, dtpm->power_min, dtpm->power_max);drivers/powercap/dtpm_cpu.c +46
-78@@ -14,6 +14,8 @@* The CPU hotplug is supported and the power numbers will be updated * if a CPU is hot plugged / unplugged. */ +#define pr_fmt (fmt) KBUILD_MODNAME ": " fmt +#include <linux/cpumask.h> #include <linux/cpufreq.h> #include <linux/cpuhotplug.h> @@ -23,8 +25,6 @@#include <linux/slab.h> #include <linux/units.h> -static struct dtpm *__parent; - static DEFINE_PER_CPU(struct dtpm *, dtpm_per_cpu);struct dtpm_cpu {@@ -32,57 +32,16 @@ struct dtpm_cpu {int cpu; }; -/* -* When a new CPU is inserted at hotplug or boot time, add the power -* contribution and update the dtpm tree. -*/ -static int power_add (struct dtpm *dtpm,struct em_perf_domain *em) -{ -u64 power_min, power_max; - - power_min = em->table[0 ].power; - power_min *=MICROWATT_PER_MILLIWATT ; - power_min += dtpm->power_min; - - power_max = em->table[em->nr_perf_states -1 ].power; - power_max *=MICROWATT_PER_MILLIWATT ; - power_max += dtpm->power_max; - -return dtpm_update_power (dtpm, power_min, power_max); -} - -/* -* When a CPU is unplugged, remove its power contribution from the -* dtpm tree. -*/ -static int power_sub (struct dtpm *dtpm,struct em_perf_domain *em) -{ -u64 power_min, power_max; - - power_min = em->table[0 ].power; - power_min *=MICROWATT_PER_MILLIWATT ; - power_min = dtpm->power_min - power_min; - - power_max = em->table[em->nr_perf_states -1 ].power; - power_max *=MICROWATT_PER_MILLIWATT ; - power_max = dtpm->power_max - power_max; - -return dtpm_update_power (dtpm, power_min, power_max); -} -static u64 set_pd_power_limit (struct dtpm *dtpm,u64 power_limit) {struct dtpm_cpu *dtpm_cpu = dtpm->private; -struct em_perf_domain *pd; +struct em_perf_domain *pd =em_cpu_get (dtpm_cpu->cpu);struct cpumask cpus;unsigned long freq;u64 power;int i, nr_cpus; - pd =em_cpu_get (dtpm_cpu->cpu); -cpumask_and (&cpus, cpu_online_mask,to_cpumask (pd->cpus)); - nr_cpus =cpumask_weight (&cpus);for (i =0 ; i < pd->nr_perf_states; i++) {@@ -113,6 +72,7 @@ static u64 get_pd_power_uw(struct dtpm *dtpm) pd =em_cpu_get (dtpm_cpu->cpu); freq =cpufreq_quick_get (dtpm_cpu->cpu); +cpumask_and (&cpus, cpu_online_mask,to_cpumask (pd->cpus)); nr_cpus =cpumask_weight (&cpus);@@ -128,6 +88,27 @@ static u64 get_pd_power_uw(struct dtpm *dtpm)return 0 ; } +static int upt_pd_power_uw (struct dtpm *dtpm) +{ +struct dtpm_cpu *dtpm_cpu = dtpm->private; +struct em_perf_domain *em =em_cpu_get (dtpm_cpu->cpu); +struct cpumask cpus; +int nr_cpus; + +cpumask_and (&cpus, cpu_online_mask,to_cpumask (em->cpus)); + nr_cpus =cpumask_weight (&cpus); + + dtpm->power_min = em->table[0 ].power; + dtpm->power_min *=MICROWATT_PER_MILLIWATT ; + dtpm->power_min *= nr_cpus; + + dtpm->power_max = em->table[em->nr_perf_states -1 ].power; + dtpm->power_max *=MICROWATT_PER_MILLIWATT ; + dtpm->power_max *= nr_cpus; + +return 0 ; +} +static void pd_release (struct dtpm *dtpm) {struct dtpm_cpu *dtpm_cpu = dtpm->private;@@ -141,37 +122,25 @@ static void pd_release(struct dtpm *dtpm)static struct dtpm_ops dtpm_ops = { .set_power_uw = set_pd_power_limit, .get_power_uw = get_pd_power_uw, + .upt_power_uw = upt_pd_power_uw, .release = pd_release, };static int cpuhp_dtpm_cpu_offline (unsigned int cpu) { -struct cpufreq_policy *policy; +struct cpumask cpus;struct em_perf_domain *pd;struct dtpm *dtpm; - policy =cpufreq_cpu_get (cpu); - -if (!policy) -return 0 ; - pd =em_cpu_get (cpu);if (!pd)return -EINVAL ; - dtpm =per_cpu (dtpm_per_cpu, cpu); - -power_sub (dtpm, pd); - -if (cpumask_weight (policy->cpus) !=1 ) -return 0 ; - -for_each_cpu (cpu, policy->related_cpus) -per_cpu (dtpm_per_cpu, cpu) =NULL ; +cpumask_and (&cpus, cpu_online_mask,to_cpumask (pd->cpus)); -dtpm_unregister (dtpm); + dtpm =per_cpu (dtpm_per_cpu, cpu); -return 0 ; +return dtpm_update_power (dtpm); }static int cpuhp_dtpm_cpu_online (unsigned int cpu)@@ -184,7 +153,6 @@ static int cpuhp_dtpm_cpu_online(unsigned int cpu)int ret = -ENOMEM ; policy =cpufreq_cpu_get (cpu); -if (!policy)return 0 ;@@ -194,7 +162,7 @@ static int cpuhp_dtpm_cpu_online(unsigned int cpu) dtpm =per_cpu (dtpm_per_cpu, cpu);if (dtpm) -return power_add (dtpm, pd); +return dtpm_update_power (dtpm); dtpm =dtpm_alloc (&dtpm_ops);if (!dtpm)@@ -210,27 +178,20 @@ static int cpuhp_dtpm_cpu_online(unsigned int cpu)for_each_cpu (cpu, policy->related_cpus)per_cpu (dtpm_per_cpu, cpu) = dtpm; -sprintf (name,"cpu%d" , dtpm_cpu->cpu); +sprintf (name,"cpu%d-cpufreq" , dtpm_cpu->cpu); - ret =dtpm_register (name, dtpm, __parent); + ret =dtpm_register (name, dtpm,NULL );if (ret) goto out_kfree_dtpm_cpu; - ret =power_add (dtpm, pd); -if (ret) - goto out_dtpm_unregister; - ret =freq_qos_add_request (&policy->constraints, &dtpm_cpu->qos_req,FREQ_QOS_MAX , pd->table[pd->nr_perf_states -1 ].frequency);if (ret) - goto out_power_sub; + goto out_dtpm_unregister;return 0 ; -out_power_sub: -power_sub (dtpm, pd); - out_dtpm_unregister:dtpm_unregister (dtpm); dtpm_cpu =NULL ;@@ -248,10 +209,17 @@ static int cpuhp_dtpm_cpu_online(unsigned int cpu)int dtpm_register_cpu (struct dtpm *parent) { - __parent = parent; +int ret; -return cpuhp_setup_state (CPUHP_AP_DTPM_CPU_ONLINE , -"dtpm_cpu:online" , - cpuhp_dtpm_cpu_online, - cpuhp_dtpm_cpu_offline); + ret =cpuhp_setup_state (CPUHP_AP_DTPM_CPU_DEAD ,"dtpm_cpu:offline" , +NULL , cpuhp_dtpm_cpu_offline); +if (ret <0 ) +return ret; + + ret =cpuhp_setup_state (CPUHP_AP_ONLINE_DYN ,"dtpm_cpu:online" , + cpuhp_dtpm_cpu_online,NULL ); +if (ret <0 ) +return ret; + +return 0 ; }include/linux/cpuhotplug.h +1
-1@@ -61,6 +61,7 @@ enum cpuhp_state {CPUHP_LUSTRE_CFS_DEAD ,CPUHP_AP_ARM_CACHE_B15_RAC_DEAD ,CPUHP_PADATA_DEAD , +CPUHP_AP_DTPM_CPU_DEAD ,CPUHP_WORKQUEUE_PREP ,CPUHP_POWER_NUMA_PREPARE , CPUHP_HRTIMERS_PREPARE,@@ -193,7 +194,6 @@ enum cpuhp_state {CPUHP_AP_ONLINE_DYN_END =CPUHP_AP_ONLINE_DYN +30 ,CPUHP_AP_X86_HPET_ONLINE ,CPUHP_AP_X86_KVM_CLK_ONLINE , -CPUHP_AP_DTPM_CPU_ONLINE ,CPUHP_AP_ACTIVE , CPUHP_ONLINE, };include/linux/dtpm.h +2
-1@@ -29,6 +29,7 @@ struct dtpm {struct dtpm_ops {u64 (*set_power_uw)(struct dtpm *,u64 );u64 (*get_power_uw)(struct dtpm *); +int (*upt_power_uw)(struct dtpm *);void (*release)(struct dtpm *); };@@ -62,7 +63,7 @@ static inline struct dtpm *to_dtpm(struct powercap_zone *zone) return container_of(zone, struct dtpm, zone); } -int dtpm_update_power (struct dtpm *dtpm,u64 power_min,u64 power_max); +int dtpm_update_power (struct dtpm *dtpm);int dtpm_release_zone (struct powercap_zone *pcz);
Signature · 5 lines
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook | <http://twitter.com/#!/linaroorg> Twitter | <http://www.linaro.org/linaro-blog/> Blog
Hi Daniel,
Quoted 16 lines · Daniel Lezcano
Quoted 14 lines · Daniel Lezcano
In order to increase the self-encapsulation of the dtpm generic code, the following changes are adding a power update ops to the dtpm ops. That allows the generic code to call directly the dtpm backend function to update the power values.
The power update function does compute the power characteristics when the function is invoked. In the case of the CPUs, the power consumption depends on the number of online CPUs. The online CPUs mask is not up to date at
CPUHP_AP_ONLINE_DYNstate in the tear down callback. That is the reason why the online / offline are at separate state. As there is already an existing state for DTPM, this one is only moved to the DEAD state, so there is no addition of new state with these changes.
Quoted 6 lines
Quoted 4 lines
That simplifies the code for the next changes and results in a more self-encapsulated code.
Signed-off-by Daniel Lezcano <[email protected]>Is there any comment on this series ?
If you can wait 1 day, I will review it tomorrow... I was quite busy recently and put it at the end of my list.
Regards,
Lukasz
Quoted 19 lines · Lukasz Luba
Hi Daniel,
Quoted 16 lines · Daniel Lezcano
Quoted 14 lines · Daniel Lezcano
In order to increase the self-encapsulation of the dtpm generic code, the following changes are adding a power update ops to the dtpm ops. That allows the generic code to call directly the dtpm backend function to update the power values.
The power update function does compute the power characteristics when the function is invoked. In the case of the CPUs, the power consumption depends on the number of online CPUs. The online CPUs mask is not up to date at
CPUHP_AP_ONLINE_DYNstate in the tear down callback. That is the reason why the online / offline are at separate state. As there is already an existing state for DTPM, this one is only moved to the DEAD state, so there is no addition of new state with these changes.
Quoted 8 lines
Quoted 6 lines
Quoted 4 lines
That simplifies the code for the next changes and results in a more self-encapsulated code.
Signed-off-by Daniel Lezcano <[email protected]>Is there any comment on this series ?
If you can wait 1 day, I will review it tomorrow...
Sure, thanks
-- Daniel
Signature · 5 lines
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook | <http://twitter.com/#!/linaroorg> Twitter | <http://www.linaro.org/linaro-blog/> Blog
Quoted 5 lines · Daniel Lezcano
In order to increase the self-encapsulation of the dtpm generic code, the following changes are adding a power update ops to the dtpm ops. That allows the generic code to call directly the dtpm backend function to update the power values.
Quoted 8 lines
The power update function does compute the power characteristics when the function is invoked. In the case of the CPUs, the power consumption depends on the number of online CPUs. The online CPUs mask is not up to date at
CPUHP_AP_ONLINE_DYNstate in the tear down callback. That is the reason why the online / offline are at separate state. As there is already an existing state for DTPM, this one is only moved to the DEAD state, so there is no addition of new state with these changes.
AFAICS in this implementation we don't remove the dtmp node when we hotplug out the CPU - which is very good. It should be mentioned in this description explicietely IMO.
I see the reason behind this new DEAD state, which we need for operating on updated cpu_online_mask with the currently off CPU bit set to 0. This also might be added either here or in the comment above the cpuhp_
Quoted 203 lines
That simplifies the code for the next changes and results in a more self-encapsulated code.
Signed-off-by Daniel Lezcano <[email protected]>--- drivers/powercap/dtpm.c | 54 ++++++++-------- drivers/powercap/dtpm_cpu.c | 124 +++++++++++++----------------------- include/linux/cpuhotplug.h | 2 +- include/linux/dtpm.h | 3 +- 4 files changed, 76 insertions(+), 107 deletions(-)drivers/powercap/dtpm.c +27
-27@@ -116,8 +116,6 @@ static void __dtpm_sub_power(struct dtpm *dtpm) parent->power_limit -= dtpm->power_limit; parent = parent->parent; } - -__dtpm_rebalance_weight (root); }static void __dtpm_add_power (struct dtpm *dtpm)@@ -130,45 +128,45 @@ static void __dtpm_add_power(struct dtpm *dtpm) parent->power_limit += dtpm->power_limit; parent = parent->parent; } +} + +static int __dtpm_update_power (struct dtpm *dtpm) +{ +int ret; + +__dtpm_sub_power (dtpm); -__dtpm_rebalance_weight (root); + ret = dtpm->ops->upt_power_uw (dtpm); +if (ret) +pr_err ("Failed to update power for '%s': %d\n" , + dtpm->zone.name, ret); + +if (!test_bit (DTPM_POWER_LIMIT_FLAG , &dtpm->flags)) + dtpm->power_limit = dtpm->power_max; + +__dtpm_add_power (dtpm); + +if (root) +__dtpm_rebalance_weight (root); + +return ret; }/** * dtpm_update_power - Update the power on the dtpm * @dtpm: a pointer to a dtpm structure to update -* @power_min: a u64 representing the new power_min value -* @power_max: a u64 representing the new power_max value * * Function to update the power values of the dtpm node specified in * parameter. These new values will be propagated to the tree. * * Return: zero on success, -EINVAL if the values are inconsistent */ -int dtpm_update_power (struct dtpm *dtpm,u64 power_min,u64 power_max) +int dtpm_update_power (struct dtpm *dtpm) { -int ret =0 ; +int ret;mutex_lock (&dtpm_lock); - -if (power_min == dtpm->power_min && power_max == dtpm->power_max) - goto unlock; - -if (power_max < power_min) { - ret = -EINVAL ; - goto unlock; - } - -__dtpm_sub_power (dtpm); - - dtpm->power_min = power_min; - dtpm->power_max = power_max; -if (!test_bit (DTPM_POWER_LIMIT_FLAG , &dtpm->flags)) - dtpm->power_limit = power_max; - -__dtpm_add_power (dtpm); - -unlock: + ret =__dtpm_update_power (dtpm);mutex_unlock (&dtpm_lock);return ret;@@ -436,6 +434,7 @@ int dtpm_register(const char *name, struct dtpm *dtpm, struct dtpm *parent)if (dtpm->ops && !(dtpm->ops->set_power_uw && dtpm->ops->get_power_uw && + dtpm->ops->upt_power_uw && dtpm->ops->release))return -EINVAL ;@@ -455,7 +454,8 @@ int dtpm_register(const char *name, struct dtpm *dtpm, struct dtpm *parent) root = dtpm; } -__dtpm_add_power (dtpm); +if (dtpm->ops && !dtpm->ops->upt_power_uw (dtpm)) +__dtpm_add_power (dtpm);pr_info ("Registered dtpm node '%s' / %llu-%llu uW, \n" , dtpm->zone.name, dtpm->power_min, dtpm->power_max);drivers/powercap/dtpm_cpu.c +4
-44@@ -14,6 +14,8 @@* The CPU hotplug is supported and the power numbers will be updated * if a CPU is hot plugged / unplugged. */ +#define pr_fmt (fmt) KBUILD_MODNAME ": " fmt +#include <linux/cpumask.h> #include <linux/cpufreq.h> #include <linux/cpuhotplug.h> @@ -23,8 +25,6 @@#include <linux/slab.h> #include <linux/units.h> -static struct dtpm *__parent; - static DEFINE_PER_CPU(struct dtpm *, dtpm_per_cpu);struct dtpm_cpu {@@ -32,57 +32,16 @@ struct dtpm_cpu {int cpu; }; -/* -* When a new CPU is inserted at hotplug or boot time, add the power -* contribution and update the dtpm tree. -*/ -static int power_add (struct dtpm *dtpm,struct em_perf_domain *em) -{ -u64 power_min, power_max; - - power_min = em->table[0 ].power; - power_min *=MICROWATT_PER_MILLIWATT ; - power_min += dtpm->power_min; - - power_max = em->table[em->nr_perf_states -1 ].power; - power_max *=MICROWATT_PER_MILLIWATT ; - power_max += dtpm->power_max; - -return dtpm_update_power (dtpm, power_min, power_max); -} - -/* -* When a CPU is unplugged, remove its power contribution from the -* dtpm tree. -*/ -static int power_sub (struct dtpm *dtpm,struct em_perf_domain *em) -{ -u64 power_min, power_max; - - power_min = em->table[0 ].power; - power_min *=MICROWATT_PER_MILLIWATT ; - power_min = dtpm->power_min - power_min; - - power_max = em->table[em->nr_perf_states -1 ].power; - power_max *=MICROWATT_PER_MILLIWATT ; - power_max = dtpm->power_max - power_max; - -return dtpm_update_power (dtpm, power_min, power_max); -} -static u64 set_pd_power_limit (struct dtpm *dtpm,u64 power_limit) {struct dtpm_cpu *dtpm_cpu = dtpm->private; -struct em_perf_domain *pd; +struct em_perf_domain *pd =em_cpu_get (dtpm_cpu->cpu);struct cpumask cpus;unsigned long freq;u64 power;int i, nr_cpus; - pd =em_cpu_get (dtpm_cpu->cpu); -cpumask_and (&cpus, cpu_online_mask,to_cpumask (pd->cpus)); - nr_cpus =cpumask_weight (&cpus);for (i =0 ; i < pd->nr_perf_states; i++) {@@ -113,6 +72,7 @@ static u64 get_pd_power_uw(struct dtpm *dtpm) pd =em_cpu_get (dtpm_cpu->cpu); freq =cpufreq_quick_get (dtpm_cpu->cpu); +cpumask_and (&cpus, cpu_online_mask,to_cpumask (pd->cpus)); nr_cpus =cpumask_weight (&cpus);
drivers/powercap/dtpm_cpu.c static u64 get_pd_power_uw(struct dtpm *dtpm) · line 95
Quoted 8 lines
drivers/powercap/dtpm_cpu.c +5
-0@@ -128,6 +88,27 @@ static u64 get_pd_power_uw(struct dtpm *dtpm)return 0 ; } +static int upt_pd_power_uw (struct dtpm *dtpm) +{ +struct dtpm_cpu *dtpm_cpu = dtpm->private; +struct em_perf_domain *em =em_cpu_get (dtpm_cpu->cpu); +struct cpumask cpus;
Maybe using cpumask_var_t, allocate and then free is more frendly for the static analyzies tools, instead of cpumask on stack. It's in a few places, but should harm platforms which use EM, so up to you.
Quoted 20 lines
drivers/powercap/dtpm_cpu.c +16
-0+int nr_cpus; + +cpumask_and (&cpus, cpu_online_mask,to_cpumask (em->cpus)); + nr_cpus =cpumask_weight (&cpus); + + dtpm->power_min = em->table[0 ].power; + dtpm->power_min *=MICROWATT_PER_MILLIWATT ; + dtpm->power_min *= nr_cpus; + + dtpm->power_max = em->table[em->nr_perf_states -1 ].power; + dtpm->power_max *=MICROWATT_PER_MILLIWATT ; + dtpm->power_max *= nr_cpus; + +return 0 ; +} +static void pd_release (struct dtpm *dtpm) {struct dtpm_cpu *dtpm_cpu = dtpm->private;
drivers/powercap/dtpm_cpu.c static void pd_release(struct dtpm *dtpm) · line 125
Quoted 4 lines
drivers/powercap/dtpm_cpu.c +1
-0@@ -141,37 +122,25 @@ static void pd_release(struct dtpm *dtpm)static struct dtpm_ops dtpm_ops = { .set_power_uw = set_pd_power_limit, .get_power_uw = get_pd_power_uw, + .upt_power_uw = upt_pd_power_uw,
I'd just use full names here.
drivers/powercap/dtpm_cpu.c static void pd_release(struct dtpm *dtpm) · line 131
Quoted 7 lines
drivers/powercap/dtpm_cpu.c +1
-1.release = pd_release, };static int cpuhp_dtpm_cpu_offline (unsigned int cpu) { -struct cpufreq_policy *policy; +struct cpumask cpus;
It's not needed, or I'm missing something
Quoted 12 lines
drivers/powercap/dtpm_cpu.c +0
-5struct em_perf_domain *pd;struct dtpm *dtpm; - policy =cpufreq_cpu_get (cpu); - -if (!policy) -return 0 ; - pd =em_cpu_get (cpu);if (!pd)return -EINVAL ;
drivers/powercap/dtpm_cpu.c static void pd_release(struct dtpm *dtpm) · line 139
Quoted 10 lines
drivers/powercap/dtpm_cpu.c +1
-9- dtpm =per_cpu (dtpm_per_cpu, cpu); - -power_sub (dtpm, pd); - -if (cpumask_weight (policy->cpus) !=1 ) -return 0 ; - -for_each_cpu (cpu, policy->related_cpus) -per_cpu (dtpm_per_cpu, cpu) =NULL ; +cpumask_and (&cpus, cpu_online_mask,to_cpumask (pd->cpus));
and the same here.
Quoted 60 lines
drivers/powercap/dtpm_cpu.c +6
-14-dtpm_unregister (dtpm); + dtpm =per_cpu (dtpm_per_cpu, cpu); -return 0 ; +return dtpm_update_power (dtpm); }static int cpuhp_dtpm_cpu_online (unsigned int cpu)@@ -184,7 +153,6 @@ static int cpuhp_dtpm_cpu_online(unsigned int cpu)int ret = -ENOMEM ; policy =cpufreq_cpu_get (cpu); -if (!policy)return 0 ;@@ -194,7 +162,7 @@ static int cpuhp_dtpm_cpu_online(unsigned int cpu) dtpm =per_cpu (dtpm_per_cpu, cpu);if (dtpm) -return power_add (dtpm, pd); +return dtpm_update_power (dtpm); dtpm =dtpm_alloc (&dtpm_ops);if (!dtpm)@@ -210,27 +178,20 @@ static int cpuhp_dtpm_cpu_online(unsigned int cpu)for_each_cpu (cpu, policy->related_cpus)per_cpu (dtpm_per_cpu, cpu) = dtpm; -sprintf (name,"cpu%d" , dtpm_cpu->cpu); +sprintf (name,"cpu%d-cpufreq" , dtpm_cpu->cpu); - ret =dtpm_register (name, dtpm, __parent); + ret =dtpm_register (name, dtpm,NULL );if (ret) goto out_kfree_dtpm_cpu; - ret =power_add (dtpm, pd); -if (ret) - goto out_dtpm_unregister; - ret =freq_qos_add_request (&policy->constraints, &dtpm_cpu->qos_req,FREQ_QOS_MAX , pd->table[pd->nr_perf_states -1 ].frequency);if (ret) - goto out_power_sub; + goto out_dtpm_unregister;return 0 ; -out_power_sub: -power_sub (dtpm, pd); - out_dtpm_unregister:dtpm_unregister (dtpm); dtpm_cpu =NULL ;@@ -248,10 +209,17 @@ static int cpuhp_dtpm_cpu_online(unsigned int cpu)int dtpm_register_cpu (struct dtpm *parent)
drivers/powercap/dtpm_cpu.c static int cpuhp_dtpm_cpu_online(unsigned int cpu) · line 215
Quoted 10 lines
drivers/powercap/dtpm_cpu.c +3
-5{ - __parent = parent; +int ret; -return cpuhp_setup_state (CPUHP_AP_DTPM_CPU_ONLINE , -"dtpm_cpu:online" , - cpuhp_dtpm_cpu_online, - cpuhp_dtpm_cpu_offline); + ret =cpuhp_setup_state (CPUHP_AP_DTPM_CPU_DEAD ,"dtpm_cpu:offline" , +NULL , cpuhp_dtpm_cpu_offline);
Maybe a comment above this line with description why we need this?
drivers/powercap/dtpm_cpu.c static int cpuhp_dtpm_cpu_online(unsigned int cpu) · line 220
Quoted 5 lines
drivers/powercap/dtpm_cpu.c +5
-0+if (ret <0 ) +return ret; + + ret =cpuhp_setup_state (CPUHP_AP_ONLINE_DYN ,"dtpm_cpu:online" , + cpuhp_dtpm_cpu_online,NULL );
For this, a small comment also wouldn't harm.
Quoted 30 lines
drivers/powercap/dtpm_cpu.c +4
-0+if (ret <0 ) +return ret; + +return 0 ; }include/linux/cpuhotplug.h +1
-1@@ -61,6 +61,7 @@ enum cpuhp_state {CPUHP_LUSTRE_CFS_DEAD ,CPUHP_AP_ARM_CACHE_B15_RAC_DEAD ,CPUHP_PADATA_DEAD , +CPUHP_AP_DTPM_CPU_DEAD ,CPUHP_WORKQUEUE_PREP ,CPUHP_POWER_NUMA_PREPARE , CPUHP_HRTIMERS_PREPARE,@@ -193,7 +194,6 @@ enum cpuhp_state {CPUHP_AP_ONLINE_DYN_END =CPUHP_AP_ONLINE_DYN +30 ,CPUHP_AP_X86_HPET_ONLINE ,CPUHP_AP_X86_KVM_CLK_ONLINE , -CPUHP_AP_DTPM_CPU_ONLINE ,CPUHP_AP_ACTIVE , CPUHP_ONLINE, };include/linux/dtpm.h +0
-0
include/linux/dtpm.h struct dtpm { · line 32
Quoted 4 lines
include/linux/dtpm.h +1
-0@@ -29,6 +29,7 @@ struct dtpm {struct dtpm_ops {u64 (*set_power_uw)(struct dtpm *,u64 );u64 (*get_power_uw)(struct dtpm *); +int (*upt_power_uw)(struct dtpm *);
IMHO as an API the full name 'update_power_uq' looks better here.
Quoted 13 lines
include/linux/dtpm.h +1
-1void (*release)(struct dtpm *); };@@ -62,7 +63,7 @@ static inline struct dtpm *to_dtpm(struct powercap_zone *zone) return container_of(zone, struct dtpm, zone); } -int dtpm_update_power (struct dtpm *dtpm,u64 power_min,u64 power_max); +int dtpm_update_power (struct dtpm *dtpm);int dtpm_release_zone (struct powercap_zone *pcz);