Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branches 'pm-em' and 'pm-opp'

Merge energy model management updates and operating performance points
(OPP) library changes for 6.19-rc1:

- Add support for sending netlink notifications to user space on energy
model updates (Changwoo Mini, Peng Fan)

- Minor improvements to the Rust OPP interface (Tamir Duberstein)

- Fixes to scope-based pointers in the OPP library (Viresh Kumar)

* pm-em:
PM: EM: Add to em_pd_list only when no failure
PM: EM: Notify an event when the performance domain changes
PM: EM: Implement em_notify_pd_created/updated()
PM: EM: Implement em_notify_pd_deleted()
PM: EM: Implement em_nl_get_pd_table_doit()
PM: EM: Implement em_nl_get_pds_doit()
PM: EM: Add an iterator and accessor for the performance domain
PM: EM: Add a skeleton code for netlink notification
PM: EM: Add em.yaml and autogen files
PM: EM: Expose the ID of a performance domain via debugfs
PM: EM: Assign a unique ID when creating a performance domain

* pm-opp:
rust: opp: simplify callers of `to_c_str_array`
OPP: Initialize scope-based pointers inline
rust: opp: fix broken rustdoc link

+870 -154
+113
Documentation/netlink/specs/em.yaml
··· 1 + # SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) 2 + 3 + name: em 4 + 5 + doc: | 6 + Energy model netlink interface to notify its changes. 7 + 8 + protocol: genetlink 9 + 10 + uapi-header: linux/energy_model.h 11 + 12 + attribute-sets: 13 + - 14 + name: pds 15 + attributes: 16 + - 17 + name: pd 18 + type: nest 19 + nested-attributes: pd 20 + multi-attr: true 21 + - 22 + name: pd 23 + attributes: 24 + - 25 + name: pad 26 + type: pad 27 + - 28 + name: pd-id 29 + type: u32 30 + - 31 + name: flags 32 + type: u64 33 + - 34 + name: cpus 35 + type: string 36 + - 37 + name: pd-table 38 + attributes: 39 + - 40 + name: pd-id 41 + type: u32 42 + - 43 + name: ps 44 + type: nest 45 + nested-attributes: ps 46 + multi-attr: true 47 + - 48 + name: ps 49 + attributes: 50 + - 51 + name: pad 52 + type: pad 53 + - 54 + name: performance 55 + type: u64 56 + - 57 + name: frequency 58 + type: u64 59 + - 60 + name: power 61 + type: u64 62 + - 63 + name: cost 64 + type: u64 65 + - 66 + name: flags 67 + type: u64 68 + 69 + operations: 70 + list: 71 + - 72 + name: get-pds 73 + attribute-set: pds 74 + doc: Get the list of information for all performance domains. 75 + do: 76 + reply: 77 + attributes: 78 + - pd 79 + - 80 + name: get-pd-table 81 + attribute-set: pd-table 82 + doc: Get the energy model table of a performance domain. 83 + do: 84 + request: 85 + attributes: 86 + - pd-id 87 + reply: 88 + attributes: 89 + - pd-id 90 + - ps 91 + - 92 + name: pd-created 93 + doc: A performance domain is created. 94 + notify: get-pd-table 95 + mcgrp: event 96 + - 97 + name: pd-updated 98 + doc: A performance domain is updated. 99 + notify: get-pd-table 100 + mcgrp: event 101 + - 102 + name: pd-deleted 103 + doc: A performance domain is deleted. 104 + attribute-set: pd-table 105 + event: 106 + attributes: 107 + - pd-id 108 + mcgrp: event 109 + 110 + mcast-groups: 111 + list: 112 + - 113 + name: event
+3
MAINTAINERS
··· 9188 9188 F: kernel/power/energy_model.c 9189 9189 F: include/linux/energy_model.h 9190 9190 F: Documentation/power/energy-model.rst 9191 + F: Documentation/netlink/specs/em.yaml 9192 + F: include/uapi/linux/energy_model.h 9193 + F: kernel/power/em_netlink*.* 9191 9194 9192 9195 EPAPR HYPERVISOR BYTE CHANNEL DEVICE DRIVER 9193 9196 M: Laurentiu Tudor <laurentiu.tudor@nxp.com>
+38 -31
drivers/opp/core.c
··· 309 309 */ 310 310 unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev) 311 311 { 312 - struct opp_table *opp_table __free(put_opp_table); 312 + struct opp_table *opp_table __free(put_opp_table) = 313 + _find_opp_table(dev); 313 314 314 - opp_table = _find_opp_table(dev); 315 315 if (IS_ERR(opp_table)) 316 316 return 0; 317 317 ··· 327 327 */ 328 328 unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev) 329 329 { 330 - struct opp_table *opp_table __free(put_opp_table); 331 330 struct dev_pm_opp *opp; 332 331 struct regulator *reg; 333 332 unsigned long latency_ns = 0; ··· 336 337 unsigned long max; 337 338 } *uV; 338 339 339 - opp_table = _find_opp_table(dev); 340 + struct opp_table *opp_table __free(put_opp_table) = 341 + _find_opp_table(dev); 342 + 340 343 if (IS_ERR(opp_table)) 341 344 return 0; 342 345 ··· 410 409 */ 411 410 unsigned long dev_pm_opp_get_suspend_opp_freq(struct device *dev) 412 411 { 413 - struct opp_table *opp_table __free(put_opp_table); 414 412 unsigned long freq = 0; 415 413 416 - opp_table = _find_opp_table(dev); 414 + struct opp_table *opp_table __free(put_opp_table) = 415 + _find_opp_table(dev); 416 + 417 417 if (IS_ERR(opp_table)) 418 418 return 0; 419 419 ··· 449 447 */ 450 448 int dev_pm_opp_get_opp_count(struct device *dev) 451 449 { 452 - struct opp_table *opp_table __free(put_opp_table); 450 + struct opp_table *opp_table __free(put_opp_table) = 451 + _find_opp_table(dev); 453 452 454 - opp_table = _find_opp_table(dev); 455 453 if (IS_ERR(opp_table)) { 456 454 dev_dbg(dev, "%s: OPP table not found (%ld)\n", 457 455 __func__, PTR_ERR(opp_table)); ··· 607 605 unsigned long opp_key, unsigned long key), 608 606 bool (*assert)(struct opp_table *opp_table, unsigned int index)) 609 607 { 610 - struct opp_table *opp_table __free(put_opp_table); 608 + struct opp_table *opp_table __free(put_opp_table) = 609 + _find_opp_table(dev); 611 610 612 - opp_table = _find_opp_table(dev); 613 611 if (IS_ERR(opp_table)) { 614 612 dev_err(dev, "%s: OPP table not found (%ld)\n", __func__, 615 613 PTR_ERR(opp_table)); ··· 1412 1410 */ 1413 1411 int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) 1414 1412 { 1415 - struct opp_table *opp_table __free(put_opp_table); 1416 1413 struct dev_pm_opp *opp __free(put_opp) = NULL; 1417 1414 unsigned long freq = 0, temp_freq; 1418 1415 bool forced = false; 1419 1416 1420 - opp_table = _find_opp_table(dev); 1417 + struct opp_table *opp_table __free(put_opp_table) = 1418 + _find_opp_table(dev); 1419 + 1421 1420 if (IS_ERR(opp_table)) { 1422 1421 dev_err(dev, "%s: device's opp table doesn't exist\n", __func__); 1423 1422 return PTR_ERR(opp_table); ··· 1480 1477 */ 1481 1478 int dev_pm_opp_set_opp(struct device *dev, struct dev_pm_opp *opp) 1482 1479 { 1483 - struct opp_table *opp_table __free(put_opp_table); 1480 + struct opp_table *opp_table __free(put_opp_table) = 1481 + _find_opp_table(dev); 1484 1482 1485 - opp_table = _find_opp_table(dev); 1486 1483 if (IS_ERR(opp_table)) { 1487 1484 dev_err(dev, "%s: device opp doesn't exist\n", __func__); 1488 1485 return PTR_ERR(opp_table); ··· 1797 1794 */ 1798 1795 void dev_pm_opp_remove(struct device *dev, unsigned long freq) 1799 1796 { 1800 - struct opp_table *opp_table __free(put_opp_table); 1801 1797 struct dev_pm_opp *opp = NULL, *iter; 1802 1798 1803 - opp_table = _find_opp_table(dev); 1799 + struct opp_table *opp_table __free(put_opp_table) = 1800 + _find_opp_table(dev); 1801 + 1804 1802 if (IS_ERR(opp_table)) 1805 1803 return; 1806 1804 ··· 1889 1885 */ 1890 1886 void dev_pm_opp_remove_all_dynamic(struct device *dev) 1891 1887 { 1892 - struct opp_table *opp_table __free(put_opp_table); 1888 + struct opp_table *opp_table __free(put_opp_table) = 1889 + _find_opp_table(dev); 1893 1890 1894 - opp_table = _find_opp_table(dev); 1895 1891 if (IS_ERR(opp_table)) 1896 1892 return; 1897 1893 ··· 2875 2871 bool availability_req) 2876 2872 { 2877 2873 struct dev_pm_opp *opp __free(put_opp) = ERR_PTR(-ENODEV), *tmp_opp; 2878 - struct opp_table *opp_table __free(put_opp_table); 2879 2874 2880 2875 /* Find the opp_table */ 2881 - opp_table = _find_opp_table(dev); 2876 + struct opp_table *opp_table __free(put_opp_table) = 2877 + _find_opp_table(dev); 2878 + 2882 2879 if (IS_ERR(opp_table)) { 2883 2880 dev_warn(dev, "%s: Device OPP not found (%ld)\n", __func__, 2884 2881 PTR_ERR(opp_table)); ··· 2937 2932 2938 2933 { 2939 2934 struct dev_pm_opp *opp __free(put_opp) = ERR_PTR(-ENODEV), *tmp_opp; 2940 - struct opp_table *opp_table __free(put_opp_table); 2941 2935 int r; 2942 2936 2943 2937 /* Find the opp_table */ 2944 - opp_table = _find_opp_table(dev); 2938 + struct opp_table *opp_table __free(put_opp_table) = 2939 + _find_opp_table(dev); 2940 + 2945 2941 if (IS_ERR(opp_table)) { 2946 2942 r = PTR_ERR(opp_table); 2947 2943 dev_warn(dev, "%s: Device OPP not found (%d)\n", __func__, r); ··· 2992 2986 */ 2993 2987 int dev_pm_opp_sync_regulators(struct device *dev) 2994 2988 { 2995 - struct opp_table *opp_table __free(put_opp_table); 2996 2989 struct regulator *reg; 2997 2990 int ret, i; 2998 2991 2999 2992 /* Device may not have OPP table */ 3000 - opp_table = _find_opp_table(dev); 2993 + struct opp_table *opp_table __free(put_opp_table) = 2994 + _find_opp_table(dev); 2995 + 3001 2996 if (IS_ERR(opp_table)) 3002 2997 return 0; 3003 2998 ··· 3069 3062 */ 3070 3063 int dev_pm_opp_register_notifier(struct device *dev, struct notifier_block *nb) 3071 3064 { 3072 - struct opp_table *opp_table __free(put_opp_table); 3065 + struct opp_table *opp_table __free(put_opp_table) = 3066 + _find_opp_table(dev); 3073 3067 3074 - opp_table = _find_opp_table(dev); 3075 3068 if (IS_ERR(opp_table)) 3076 3069 return PTR_ERR(opp_table); 3077 3070 ··· 3089 3082 int dev_pm_opp_unregister_notifier(struct device *dev, 3090 3083 struct notifier_block *nb) 3091 3084 { 3092 - struct opp_table *opp_table __free(put_opp_table); 3085 + struct opp_table *opp_table __free(put_opp_table) = 3086 + _find_opp_table(dev); 3093 3087 3094 - opp_table = _find_opp_table(dev); 3095 3088 if (IS_ERR(opp_table)) 3096 3089 return PTR_ERR(opp_table); 3097 3090 ··· 3108 3101 */ 3109 3102 void dev_pm_opp_remove_table(struct device *dev) 3110 3103 { 3111 - struct opp_table *opp_table __free(put_opp_table); 3112 - 3113 3104 /* Check for existing table for 'dev' */ 3114 - opp_table = _find_opp_table(dev); 3105 + struct opp_table *opp_table __free(put_opp_table) = 3106 + _find_opp_table(dev); 3107 + 3115 3108 if (IS_ERR(opp_table)) { 3116 3109 int error = PTR_ERR(opp_table); 3117 3110
+9 -7
drivers/opp/cpu.c
··· 56 56 return -ENOMEM; 57 57 58 58 for (i = 0, rate = 0; i < max_opps; i++, rate++) { 59 - struct dev_pm_opp *opp __free(put_opp); 60 - 61 59 /* find next rate */ 62 - opp = dev_pm_opp_find_freq_ceil(dev, &rate); 60 + struct dev_pm_opp *opp __free(put_opp) = 61 + dev_pm_opp_find_freq_ceil(dev, &rate); 62 + 63 63 if (IS_ERR(opp)) { 64 64 ret = PTR_ERR(opp); 65 65 goto out; ··· 154 154 int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, 155 155 const struct cpumask *cpumask) 156 156 { 157 - struct opp_table *opp_table __free(put_opp_table); 158 157 struct opp_device *opp_dev; 159 158 struct device *dev; 160 159 int cpu; 161 160 162 - opp_table = _find_opp_table(cpu_dev); 161 + struct opp_table *opp_table __free(put_opp_table) = 162 + _find_opp_table(cpu_dev); 163 + 163 164 if (IS_ERR(opp_table)) 164 165 return PTR_ERR(opp_table); 165 166 ··· 202 201 */ 203 202 int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask) 204 203 { 205 - struct opp_table *opp_table __free(put_opp_table); 206 204 struct opp_device *opp_dev; 207 205 208 - opp_table = _find_opp_table(cpu_dev); 206 + struct opp_table *opp_table __free(put_opp_table) = 207 + _find_opp_table(cpu_dev); 208 + 209 209 if (IS_ERR(opp_table)) 210 210 return PTR_ERR(opp_table); 211 211
+70 -55
drivers/opp/of.c
··· 45 45 struct opp_table *_managed_opp(struct device *dev, int index) 46 46 { 47 47 struct opp_table *opp_table, *managed_table = NULL; 48 - struct device_node *np __free(device_node); 49 48 50 - np = _opp_of_get_opp_desc_node(dev->of_node, index); 49 + struct device_node *np __free(device_node) = 50 + _opp_of_get_opp_desc_node(dev->of_node, index); 51 + 51 52 if (!np) 52 53 return NULL; 53 54 ··· 96 95 /* The caller must call dev_pm_opp_put_opp_table() after the table is used */ 97 96 static struct opp_table *_find_table_of_opp_np(struct device_node *opp_np) 98 97 { 99 - struct device_node *opp_table_np __free(device_node); 100 98 struct opp_table *opp_table; 101 99 102 - opp_table_np = of_get_parent(opp_np); 100 + struct device_node *opp_table_np __free(device_node) = 101 + of_get_parent(opp_np); 102 + 103 103 if (!opp_table_np) 104 104 return ERR_PTR(-ENODEV); 105 105 ··· 148 146 struct device_node *opp_np) 149 147 { 150 148 struct opp_table **required_opp_tables; 151 - struct device_node *np __free(device_node); 152 149 bool lazy = false; 153 150 int count, i, size; 154 151 155 152 /* Traversing the first OPP node is all we need */ 156 - np = of_get_next_available_child(opp_np, NULL); 153 + struct device_node *np __free(device_node) = 154 + of_get_next_available_child(opp_np, NULL); 155 + 157 156 if (!np) { 158 157 dev_warn(dev, "Empty OPP table\n"); 159 158 return; ··· 174 171 opp_table->required_opp_count = count; 175 172 176 173 for (i = 0; i < count; i++) { 177 - struct device_node *required_np __free(device_node); 174 + struct device_node *required_np __free(device_node) = 175 + of_parse_required_opp(np, i); 178 176 179 - required_np = of_parse_required_opp(np, i); 180 177 if (!required_np) { 181 178 _opp_table_free_required_tables(opp_table); 182 179 return; ··· 202 199 void _of_init_opp_table(struct opp_table *opp_table, struct device *dev, 203 200 int index) 204 201 { 205 - struct device_node *np __free(device_node), *opp_np; 202 + struct device_node *opp_np; 206 203 u32 val; 207 204 208 205 /* 209 206 * Only required for backward compatibility with v1 bindings, but isn't 210 207 * harmful for other cases. And so we do it unconditionally. 211 208 */ 212 - np = of_node_get(dev->of_node); 209 + struct device_node *np __free(device_node) = of_node_get(dev->of_node); 210 + 213 211 if (!np) 214 212 return; 215 213 ··· 277 273 static int _link_required_opps(struct dev_pm_opp *opp, 278 274 struct opp_table *required_table, int index) 279 275 { 280 - struct device_node *np __free(device_node); 276 + struct device_node *np __free(device_node) = 277 + of_parse_required_opp(opp->np, index); 281 278 282 - np = of_parse_required_opp(opp->np, index); 283 279 if (unlikely(!np)) 284 280 return -ENODEV; 285 281 ··· 353 349 guard(mutex)(&opp_table_lock); 354 350 355 351 list_for_each_entry_safe(opp_table, temp, &lazy_opp_tables, lazy) { 356 - struct device_node *opp_np __free(device_node); 357 352 bool lazy = false; 358 353 359 354 /* opp_np can't be invalid here */ 360 - opp_np = of_get_next_available_child(opp_table->np, NULL); 355 + struct device_node *opp_np __free(device_node) = 356 + of_get_next_available_child(opp_table->np, NULL); 361 357 362 358 for (i = 0; i < opp_table->required_opp_count; i++) { 363 - struct device_node *required_np __free(device_node) = NULL; 364 - struct device_node *required_table_np __free(device_node) = NULL; 365 - 366 359 required_opp_tables = opp_table->required_opp_tables; 367 360 368 361 /* Required opp-table is already parsed */ ··· 367 366 continue; 368 367 369 368 /* required_np can't be invalid here */ 370 - required_np = of_parse_required_opp(opp_np, i); 371 - required_table_np = of_get_parent(required_np); 369 + struct device_node *required_np __free(device_node) = 370 + of_parse_required_opp(opp_np, i); 371 + struct device_node *required_table_np __free(device_node) = 372 + of_get_parent(required_np); 372 373 373 374 /* 374 375 * Newly added table isn't the required opp-table for ··· 405 402 static int _bandwidth_supported(struct device *dev, struct opp_table *opp_table) 406 403 { 407 404 struct device_node *opp_np __free(device_node) = NULL; 408 - struct device_node *np __free(device_node) = NULL; 409 405 struct property *prop; 410 406 411 407 if (!opp_table) { 412 - struct device_node *np __free(device_node); 408 + struct device_node *np __free(device_node) = 409 + of_node_get(dev->of_node); 413 410 414 - np = of_node_get(dev->of_node); 415 411 if (!np) 416 412 return -ENODEV; 417 413 ··· 424 422 return 0; 425 423 426 424 /* Checking only first OPP is sufficient */ 427 - np = of_get_next_available_child(opp_np, NULL); 425 + struct device_node *np __free(device_node) = 426 + of_get_next_available_child(opp_np, NULL); 427 + 428 428 if (!np) { 429 429 dev_err(dev, "OPP table empty\n"); 430 430 return -EINVAL; ··· 1273 1269 int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, 1274 1270 struct cpumask *cpumask) 1275 1271 { 1276 - struct device_node *np __free(device_node); 1277 1272 int cpu; 1278 1273 1279 1274 /* Get OPP descriptor node */ 1280 - np = dev_pm_opp_of_get_opp_desc_node(cpu_dev); 1275 + struct device_node *np __free(device_node) = 1276 + dev_pm_opp_of_get_opp_desc_node(cpu_dev); 1277 + 1281 1278 if (!np) { 1282 1279 dev_dbg(cpu_dev, "%s: Couldn't find opp node.\n", __func__); 1283 1280 return -ENOENT; ··· 1291 1286 return 0; 1292 1287 1293 1288 for_each_possible_cpu(cpu) { 1294 - struct device_node *cpu_np __free(device_node) = NULL; 1295 - struct device_node *tmp_np __free(device_node) = NULL; 1296 - 1297 1289 if (cpu == cpu_dev->id) 1298 1290 continue; 1299 1291 1300 - cpu_np = of_cpu_device_node_get(cpu); 1292 + struct device_node *cpu_np __free(device_node) = 1293 + of_cpu_device_node_get(cpu); 1294 + 1301 1295 if (!cpu_np) { 1302 1296 dev_err(cpu_dev, "%s: failed to get cpu%d node\n", 1303 1297 __func__, cpu); ··· 1304 1300 } 1305 1301 1306 1302 /* Get OPP descriptor node */ 1307 - tmp_np = _opp_of_get_opp_desc_node(cpu_np, 0); 1303 + struct device_node *tmp_np __free(device_node) = 1304 + _opp_of_get_opp_desc_node(cpu_np, 0); 1305 + 1308 1306 if (!tmp_np) { 1309 1307 pr_err("%pOF: Couldn't find opp node\n", cpu_np); 1310 1308 return -ENOENT; ··· 1334 1328 */ 1335 1329 int of_get_required_opp_performance_state(struct device_node *np, int index) 1336 1330 { 1337 - struct device_node *required_np __free(device_node); 1338 - struct opp_table *opp_table __free(put_opp_table) = NULL; 1339 - struct dev_pm_opp *opp __free(put_opp) = NULL; 1340 1331 int pstate = -EINVAL; 1341 1332 1342 - required_np = of_parse_required_opp(np, index); 1333 + struct device_node *required_np __free(device_node) = 1334 + of_parse_required_opp(np, index); 1335 + 1343 1336 if (!required_np) 1344 1337 return -ENODEV; 1345 1338 1346 - opp_table = _find_table_of_opp_np(required_np); 1339 + struct opp_table *opp_table __free(put_opp_table) = 1340 + _find_table_of_opp_np(required_np); 1341 + 1347 1342 if (IS_ERR(opp_table)) { 1348 1343 pr_err("%s: Failed to find required OPP table %pOF: %ld\n", 1349 1344 __func__, np, PTR_ERR(opp_table)); ··· 1357 1350 return -EINVAL; 1358 1351 } 1359 1352 1360 - opp = _find_opp_of_np(opp_table, required_np); 1353 + struct dev_pm_opp *opp __free(put_opp) = 1354 + _find_opp_of_np(opp_table, required_np); 1355 + 1361 1356 if (opp) { 1362 1357 if (opp->level == OPP_LEVEL_UNSET) { 1363 1358 pr_err("%s: OPP levels aren't available for %pOF\n", ··· 1385 1376 */ 1386 1377 bool dev_pm_opp_of_has_required_opp(struct device *dev) 1387 1378 { 1388 - struct device_node *np __free(device_node) = NULL, *opp_np __free(device_node); 1389 1379 int count; 1390 1380 1391 - opp_np = _opp_of_get_opp_desc_node(dev->of_node, 0); 1381 + struct device_node *opp_np __free(device_node) = 1382 + _opp_of_get_opp_desc_node(dev->of_node, 0); 1383 + 1392 1384 if (!opp_np) 1393 1385 return false; 1394 1386 1395 - np = of_get_next_available_child(opp_np, NULL); 1387 + struct device_node *np __free(device_node) = 1388 + of_get_next_available_child(opp_np, NULL); 1389 + 1396 1390 if (!np) { 1397 1391 dev_warn(dev, "Empty OPP table\n"); 1398 1392 return false; ··· 1437 1425 static int __maybe_unused 1438 1426 _get_dt_power(struct device *dev, unsigned long *uW, unsigned long *kHz) 1439 1427 { 1440 - struct dev_pm_opp *opp __free(put_opp); 1441 1428 unsigned long opp_freq, opp_power; 1442 1429 1443 1430 /* Find the right frequency and related OPP */ 1444 1431 opp_freq = *kHz * 1000; 1445 - opp = dev_pm_opp_find_freq_ceil(dev, &opp_freq); 1432 + 1433 + struct dev_pm_opp *opp __free(put_opp) = 1434 + dev_pm_opp_find_freq_ceil(dev, &opp_freq); 1435 + 1446 1436 if (IS_ERR(opp)) 1447 1437 return -EINVAL; 1448 1438 ··· 1479 1465 int dev_pm_opp_calc_power(struct device *dev, unsigned long *uW, 1480 1466 unsigned long *kHz) 1481 1467 { 1482 - struct dev_pm_opp *opp __free(put_opp) = NULL; 1483 - struct device_node *np __free(device_node); 1484 1468 unsigned long mV, Hz; 1485 1469 u32 cap; 1486 1470 u64 tmp; 1487 1471 int ret; 1488 1472 1489 - np = of_node_get(dev->of_node); 1473 + struct device_node *np __free(device_node) = of_node_get(dev->of_node); 1474 + 1490 1475 if (!np) 1491 1476 return -EINVAL; 1492 1477 ··· 1494 1481 return -EINVAL; 1495 1482 1496 1483 Hz = *kHz * 1000; 1497 - opp = dev_pm_opp_find_freq_ceil(dev, &Hz); 1484 + 1485 + struct dev_pm_opp *opp __free(put_opp) = 1486 + dev_pm_opp_find_freq_ceil(dev, &Hz); 1487 + 1498 1488 if (IS_ERR(opp)) 1499 1489 return -EINVAL; 1500 1490 ··· 1518 1502 1519 1503 static bool _of_has_opp_microwatt_property(struct device *dev) 1520 1504 { 1521 - struct dev_pm_opp *opp __free(put_opp); 1522 1505 unsigned long freq = 0; 1523 1506 1524 1507 /* Check if at least one OPP has needed property */ 1525 - opp = dev_pm_opp_find_freq_ceil(dev, &freq); 1508 + struct dev_pm_opp *opp __free(put_opp) = 1509 + dev_pm_opp_find_freq_ceil(dev, &freq); 1510 + 1526 1511 if (IS_ERR(opp)) 1527 1512 return false; 1528 1513 ··· 1543 1526 */ 1544 1527 int dev_pm_opp_of_register_em(struct device *dev, struct cpumask *cpus) 1545 1528 { 1546 - struct device_node *np __free(device_node) = NULL; 1547 1529 struct em_data_callback em_cb; 1548 1530 int ret, nr_opp; 1549 1531 u32 cap; 1550 1532 1551 - if (IS_ERR_OR_NULL(dev)) { 1533 + if (IS_ERR_OR_NULL(dev)) 1534 + return -EINVAL; 1535 + 1536 + struct device_node *np __free(device_node) = of_node_get(dev->of_node); 1537 + 1538 + if (!np) { 1552 1539 ret = -EINVAL; 1553 1540 goto failed; 1554 1541 } ··· 1567 1546 if (_of_has_opp_microwatt_property(dev)) { 1568 1547 EM_SET_ACTIVE_POWER_CB(em_cb, _get_dt_power); 1569 1548 goto register_em; 1570 - } 1571 - 1572 - np = of_node_get(dev->of_node); 1573 - if (!np) { 1574 - ret = -EINVAL; 1575 - goto failed; 1576 1549 } 1577 1550 1578 1551 /*
+4
include/linux/energy_model.h
··· 54 54 /** 55 55 * struct em_perf_domain - Performance domain 56 56 * @em_table: Pointer to the runtime modifiable em_perf_table 57 + * @node: node in em_pd_list (in energy_model.c) 58 + * @id: A unique ID number for each performance domain 57 59 * @nr_perf_states: Number of performance states 58 60 * @min_perf_state: Minimum allowed Performance State index 59 61 * @max_perf_state: Maximum allowed Performance State index ··· 73 71 */ 74 72 struct em_perf_domain { 75 73 struct em_perf_table __rcu *em_table; 74 + struct list_head node; 75 + int id; 76 76 int nr_perf_states; 77 77 int min_perf_state; 78 78 int max_perf_state;
+62
include/uapi/linux/energy_model.h
··· 1 + /* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */ 2 + /* Do not edit directly, auto-generated from: */ 3 + /* Documentation/netlink/specs/em.yaml */ 4 + /* YNL-GEN uapi header */ 5 + 6 + #ifndef _UAPI_LINUX_ENERGY_MODEL_H 7 + #define _UAPI_LINUX_ENERGY_MODEL_H 8 + 9 + #define EM_FAMILY_NAME "em" 10 + #define EM_FAMILY_VERSION 1 11 + 12 + enum { 13 + EM_A_PDS_PD = 1, 14 + 15 + __EM_A_PDS_MAX, 16 + EM_A_PDS_MAX = (__EM_A_PDS_MAX - 1) 17 + }; 18 + 19 + enum { 20 + EM_A_PD_PAD = 1, 21 + EM_A_PD_PD_ID, 22 + EM_A_PD_FLAGS, 23 + EM_A_PD_CPUS, 24 + 25 + __EM_A_PD_MAX, 26 + EM_A_PD_MAX = (__EM_A_PD_MAX - 1) 27 + }; 28 + 29 + enum { 30 + EM_A_PD_TABLE_PD_ID = 1, 31 + EM_A_PD_TABLE_PS, 32 + 33 + __EM_A_PD_TABLE_MAX, 34 + EM_A_PD_TABLE_MAX = (__EM_A_PD_TABLE_MAX - 1) 35 + }; 36 + 37 + enum { 38 + EM_A_PS_PAD = 1, 39 + EM_A_PS_PERFORMANCE, 40 + EM_A_PS_FREQUENCY, 41 + EM_A_PS_POWER, 42 + EM_A_PS_COST, 43 + EM_A_PS_FLAGS, 44 + 45 + __EM_A_PS_MAX, 46 + EM_A_PS_MAX = (__EM_A_PS_MAX - 1) 47 + }; 48 + 49 + enum { 50 + EM_CMD_GET_PDS = 1, 51 + EM_CMD_GET_PD_TABLE, 52 + EM_CMD_PD_CREATED, 53 + EM_CMD_PD_UPDATED, 54 + EM_CMD_PD_DELETED, 55 + 56 + __EM_CMD_MAX, 57 + EM_CMD_MAX = (__EM_CMD_MAX - 1) 58 + }; 59 + 60 + #define EM_MCGRP_EVENT "event" 61 + 62 + #endif /* _UAPI_LINUX_ENERGY_MODEL_H */
+3 -1
kernel/power/Makefile
··· 21 21 22 22 obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o 23 23 24 - obj-$(CONFIG_ENERGY_MODEL) += energy_model.o 24 + obj-$(CONFIG_ENERGY_MODEL) += em.o 25 + em-y := energy_model.o 26 + em-$(CONFIG_NET) += em_netlink_autogen.o em_netlink.o
+308
kernel/power/em_netlink.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * 4 + * Generic netlink for energy model. 5 + * 6 + * Copyright (c) 2025 Valve Corporation. 7 + * Author: Changwoo Min <changwoo@igalia.com> 8 + */ 9 + 10 + #define pr_fmt(fmt) "energy_model: " fmt 11 + 12 + #include <linux/energy_model.h> 13 + #include <net/sock.h> 14 + #include <net/genetlink.h> 15 + #include <uapi/linux/energy_model.h> 16 + 17 + #include "em_netlink.h" 18 + #include "em_netlink_autogen.h" 19 + 20 + #define EM_A_PD_CPUS_LEN 256 21 + 22 + /*************************** Command encoding ********************************/ 23 + static int __em_nl_get_pd_size(struct em_perf_domain *pd, void *data) 24 + { 25 + char cpus_buf[EM_A_PD_CPUS_LEN]; 26 + int *tot_msg_sz = data; 27 + int msg_sz, cpus_sz; 28 + 29 + cpus_sz = snprintf(cpus_buf, sizeof(cpus_buf), "%*pb", 30 + cpumask_pr_args(to_cpumask(pd->cpus))); 31 + 32 + msg_sz = nla_total_size(0) + /* EM_A_PDS_PD */ 33 + nla_total_size(sizeof(u32)) + /* EM_A_PD_PD_ID */ 34 + nla_total_size_64bit(sizeof(u64)) + /* EM_A_PD_FLAGS */ 35 + nla_total_size(cpus_sz); /* EM_A_PD_CPUS */ 36 + 37 + *tot_msg_sz += nlmsg_total_size(genlmsg_msg_size(msg_sz)); 38 + return 0; 39 + } 40 + 41 + static int __em_nl_get_pd(struct em_perf_domain *pd, void *data) 42 + { 43 + char cpus_buf[EM_A_PD_CPUS_LEN]; 44 + struct sk_buff *msg = data; 45 + struct nlattr *entry; 46 + 47 + entry = nla_nest_start(msg, EM_A_PDS_PD); 48 + if (!entry) 49 + goto out_cancel_nest; 50 + 51 + if (nla_put_u32(msg, EM_A_PD_PD_ID, pd->id)) 52 + goto out_cancel_nest; 53 + 54 + if (nla_put_u64_64bit(msg, EM_A_PD_FLAGS, pd->flags, EM_A_PD_PAD)) 55 + goto out_cancel_nest; 56 + 57 + snprintf(cpus_buf, sizeof(cpus_buf), "%*pb", 58 + cpumask_pr_args(to_cpumask(pd->cpus))); 59 + if (nla_put_string(msg, EM_A_PD_CPUS, cpus_buf)) 60 + goto out_cancel_nest; 61 + 62 + nla_nest_end(msg, entry); 63 + 64 + return 0; 65 + 66 + out_cancel_nest: 67 + nla_nest_cancel(msg, entry); 68 + 69 + return -EMSGSIZE; 70 + } 71 + 72 + int em_nl_get_pds_doit(struct sk_buff *skb, struct genl_info *info) 73 + { 74 + struct sk_buff *msg; 75 + void *hdr; 76 + int cmd = info->genlhdr->cmd; 77 + int ret = -EMSGSIZE, msg_sz = 0; 78 + 79 + for_each_em_perf_domain(__em_nl_get_pd_size, &msg_sz); 80 + 81 + msg = genlmsg_new(msg_sz, GFP_KERNEL); 82 + if (!msg) 83 + return -ENOMEM; 84 + 85 + hdr = genlmsg_put_reply(msg, info, &em_nl_family, 0, cmd); 86 + if (!hdr) 87 + goto out_free_msg; 88 + 89 + ret = for_each_em_perf_domain(__em_nl_get_pd, msg); 90 + if (ret) 91 + goto out_cancel_msg; 92 + 93 + genlmsg_end(msg, hdr); 94 + 95 + return genlmsg_reply(msg, info); 96 + 97 + out_cancel_msg: 98 + genlmsg_cancel(msg, hdr); 99 + out_free_msg: 100 + nlmsg_free(msg); 101 + 102 + return ret; 103 + } 104 + 105 + static struct em_perf_domain *__em_nl_get_pd_table_id(struct nlattr **attrs) 106 + { 107 + struct em_perf_domain *pd; 108 + int id; 109 + 110 + if (!attrs[EM_A_PD_TABLE_PD_ID]) 111 + return NULL; 112 + 113 + id = nla_get_u32(attrs[EM_A_PD_TABLE_PD_ID]); 114 + pd = em_perf_domain_get_by_id(id); 115 + return pd; 116 + } 117 + 118 + static int __em_nl_get_pd_table_size(const struct em_perf_domain *pd) 119 + { 120 + int id_sz, ps_sz; 121 + 122 + id_sz = nla_total_size(sizeof(u32)); /* EM_A_PD_TABLE_PD_ID */ 123 + ps_sz = nla_total_size(0) + /* EM_A_PD_TABLE_PS */ 124 + nla_total_size_64bit(sizeof(u64)) + /* EM_A_PS_PERFORMANCE */ 125 + nla_total_size_64bit(sizeof(u64)) + /* EM_A_PS_FREQUENCY */ 126 + nla_total_size_64bit(sizeof(u64)) + /* EM_A_PS_POWER */ 127 + nla_total_size_64bit(sizeof(u64)) + /* EM_A_PS_COST */ 128 + nla_total_size_64bit(sizeof(u64)); /* EM_A_PS_FLAGS */ 129 + ps_sz *= pd->nr_perf_states; 130 + 131 + return nlmsg_total_size(genlmsg_msg_size(id_sz + ps_sz)); 132 + } 133 + 134 + static int __em_nl_get_pd_table(struct sk_buff *msg, const struct em_perf_domain *pd) 135 + { 136 + struct em_perf_state *table, *ps; 137 + struct nlattr *entry; 138 + int i; 139 + 140 + if (nla_put_u32(msg, EM_A_PD_TABLE_PD_ID, pd->id)) 141 + goto out_err; 142 + 143 + rcu_read_lock(); 144 + table = em_perf_state_from_pd((struct em_perf_domain *)pd); 145 + 146 + for (i = 0; i < pd->nr_perf_states; i++) { 147 + ps = &table[i]; 148 + 149 + entry = nla_nest_start(msg, EM_A_PD_TABLE_PS); 150 + if (!entry) 151 + goto out_unlock_ps; 152 + 153 + if (nla_put_u64_64bit(msg, EM_A_PS_PERFORMANCE, 154 + ps->performance, EM_A_PS_PAD)) 155 + goto out_cancel_ps_nest; 156 + if (nla_put_u64_64bit(msg, EM_A_PS_FREQUENCY, 157 + ps->frequency, EM_A_PS_PAD)) 158 + goto out_cancel_ps_nest; 159 + if (nla_put_u64_64bit(msg, EM_A_PS_POWER, 160 + ps->power, EM_A_PS_PAD)) 161 + goto out_cancel_ps_nest; 162 + if (nla_put_u64_64bit(msg, EM_A_PS_COST, 163 + ps->cost, EM_A_PS_PAD)) 164 + goto out_cancel_ps_nest; 165 + if (nla_put_u64_64bit(msg, EM_A_PS_FLAGS, 166 + ps->flags, EM_A_PS_PAD)) 167 + goto out_cancel_ps_nest; 168 + 169 + nla_nest_end(msg, entry); 170 + } 171 + rcu_read_unlock(); 172 + return 0; 173 + 174 + out_cancel_ps_nest: 175 + nla_nest_cancel(msg, entry); 176 + out_unlock_ps: 177 + rcu_read_unlock(); 178 + out_err: 179 + return -EMSGSIZE; 180 + } 181 + 182 + int em_nl_get_pd_table_doit(struct sk_buff *skb, struct genl_info *info) 183 + { 184 + int cmd = info->genlhdr->cmd; 185 + int msg_sz, ret = -EMSGSIZE; 186 + struct em_perf_domain *pd; 187 + struct sk_buff *msg; 188 + void *hdr; 189 + 190 + pd = __em_nl_get_pd_table_id(info->attrs); 191 + if (!pd) 192 + return -EINVAL; 193 + 194 + msg_sz = __em_nl_get_pd_table_size(pd); 195 + 196 + msg = genlmsg_new(msg_sz, GFP_KERNEL); 197 + if (!msg) 198 + return -ENOMEM; 199 + 200 + hdr = genlmsg_put_reply(msg, info, &em_nl_family, 0, cmd); 201 + if (!hdr) 202 + goto out_free_msg; 203 + 204 + ret = __em_nl_get_pd_table(msg, pd); 205 + if (ret) 206 + goto out_free_msg; 207 + 208 + genlmsg_end(msg, hdr); 209 + return genlmsg_reply(msg, info); 210 + 211 + out_free_msg: 212 + nlmsg_free(msg); 213 + return ret; 214 + } 215 + 216 + 217 + /**************************** Event encoding *********************************/ 218 + static void __em_notify_pd_table(const struct em_perf_domain *pd, int ntf_type) 219 + { 220 + struct sk_buff *msg; 221 + int msg_sz, ret = -EMSGSIZE; 222 + void *hdr; 223 + 224 + if (!genl_has_listeners(&em_nl_family, &init_net, EM_NLGRP_EVENT)) 225 + return; 226 + 227 + msg_sz = __em_nl_get_pd_table_size(pd); 228 + 229 + msg = genlmsg_new(msg_sz, GFP_KERNEL); 230 + if (!msg) 231 + return; 232 + 233 + hdr = genlmsg_put(msg, 0, 0, &em_nl_family, 0, ntf_type); 234 + if (!hdr) 235 + goto out_free_msg; 236 + 237 + ret = __em_nl_get_pd_table(msg, pd); 238 + if (ret) 239 + goto out_free_msg; 240 + 241 + genlmsg_end(msg, hdr); 242 + 243 + genlmsg_multicast(&em_nl_family, msg, 0, EM_NLGRP_EVENT, GFP_KERNEL); 244 + 245 + return; 246 + 247 + out_free_msg: 248 + nlmsg_free(msg); 249 + return; 250 + } 251 + 252 + void em_notify_pd_created(const struct em_perf_domain *pd) 253 + { 254 + __em_notify_pd_table(pd, EM_CMD_PD_CREATED); 255 + } 256 + 257 + void em_notify_pd_updated(const struct em_perf_domain *pd) 258 + { 259 + __em_notify_pd_table(pd, EM_CMD_PD_UPDATED); 260 + } 261 + 262 + static int __em_notify_pd_deleted_size(const struct em_perf_domain *pd) 263 + { 264 + int id_sz = nla_total_size(sizeof(u32)); /* EM_A_PD_TABLE_PD_ID */ 265 + 266 + return nlmsg_total_size(genlmsg_msg_size(id_sz)); 267 + } 268 + 269 + void em_notify_pd_deleted(const struct em_perf_domain *pd) 270 + { 271 + struct sk_buff *msg; 272 + void *hdr; 273 + int msg_sz; 274 + 275 + if (!genl_has_listeners(&em_nl_family, &init_net, EM_NLGRP_EVENT)) 276 + return; 277 + 278 + msg_sz = __em_notify_pd_deleted_size(pd); 279 + 280 + msg = genlmsg_new(msg_sz, GFP_KERNEL); 281 + if (!msg) 282 + return; 283 + 284 + hdr = genlmsg_put(msg, 0, 0, &em_nl_family, 0, EM_CMD_PD_DELETED); 285 + if (!hdr) 286 + goto out_free_msg; 287 + 288 + if (nla_put_u32(msg, EM_A_PD_TABLE_PD_ID, pd->id)) { 289 + goto out_free_msg; 290 + } 291 + 292 + genlmsg_end(msg, hdr); 293 + 294 + genlmsg_multicast(&em_nl_family, msg, 0, EM_NLGRP_EVENT, GFP_KERNEL); 295 + 296 + return; 297 + 298 + out_free_msg: 299 + nlmsg_free(msg); 300 + return; 301 + } 302 + 303 + /**************************** Initialization *********************************/ 304 + static int __init em_netlink_init(void) 305 + { 306 + return genl_register_family(&em_nl_family); 307 + } 308 + postcore_initcall(em_netlink_init);
+39
kernel/power/em_netlink.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * 4 + * Generic netlink for energy model. 5 + * 6 + * Copyright (c) 2025 Valve Corporation. 7 + * Author: Changwoo Min <changwoo@igalia.com> 8 + */ 9 + #ifndef _EM_NETLINK_H 10 + #define _EM_NETLINK_H 11 + 12 + #if defined(CONFIG_ENERGY_MODEL) && defined(CONFIG_NET) 13 + int for_each_em_perf_domain(int (*cb)(struct em_perf_domain*, void *), 14 + void *data); 15 + struct em_perf_domain *em_perf_domain_get_by_id(int id); 16 + void em_notify_pd_created(const struct em_perf_domain *pd); 17 + void em_notify_pd_deleted(const struct em_perf_domain *pd); 18 + void em_notify_pd_updated(const struct em_perf_domain *pd); 19 + #else 20 + static inline 21 + int for_each_em_perf_domain(int (*cb)(struct em_perf_domain*, void *), 22 + void *data) 23 + { 24 + return -EINVAL; 25 + } 26 + static inline 27 + struct em_perf_domain *em_perf_domain_get_by_id(int id) 28 + { 29 + return NULL; 30 + } 31 + 32 + static inline void em_notify_pd_created(const struct em_perf_domain *pd) {} 33 + 34 + static inline void em_notify_pd_deleted(const struct em_perf_domain *pd) {} 35 + 36 + static inline void em_notify_pd_updated(const struct em_perf_domain *pd) {} 37 + #endif 38 + 39 + #endif /* _EM_NETLINK_H */
+88 -2
kernel/power/energy_model.c
··· 17 17 #include <linux/sched/topology.h> 18 18 #include <linux/slab.h> 19 19 20 + #include "em_netlink.h" 21 + 20 22 /* 21 23 * Mutex serializing the registrations of performance domains and letting 22 24 * callbacks defined by drivers sleep. 23 25 */ 24 26 static DEFINE_MUTEX(em_pd_mutex); 27 + 28 + /* 29 + * Manage performance domains with IDs. One can iterate the performance domains 30 + * through the list and pick one with their associated ID. The mutex serializes 31 + * the list access. When holding em_pd_list_mutex, em_pd_mutex should not be 32 + * taken to avoid potential deadlock. 33 + */ 34 + static DEFINE_IDA(em_pd_ida); 35 + static LIST_HEAD(em_pd_list); 36 + static DEFINE_MUTEX(em_pd_list_mutex); 25 37 26 38 static void em_cpufreq_update_efficiencies(struct device *dev, 27 39 struct em_perf_state *table); ··· 128 116 } 129 117 DEFINE_SHOW_ATTRIBUTE(em_debug_flags); 130 118 119 + static int em_debug_id_show(struct seq_file *s, void *unused) 120 + { 121 + struct em_perf_domain *pd = s->private; 122 + 123 + seq_printf(s, "%d\n", pd->id); 124 + 125 + return 0; 126 + } 127 + DEFINE_SHOW_ATTRIBUTE(em_debug_id); 128 + 131 129 static void em_debug_create_pd(struct device *dev) 132 130 { 133 131 struct em_dbg_info *em_dbg; ··· 153 131 154 132 debugfs_create_file("flags", 0444, d, dev->em_pd, 155 133 &em_debug_flags_fops); 134 + 135 + debugfs_create_file("id", 0444, d, dev->em_pd, &em_debug_id_fops); 156 136 157 137 em_dbg = devm_kcalloc(dev, dev->em_pd->nr_perf_states, 158 138 sizeof(*em_dbg), GFP_KERNEL); ··· 352 328 em_table_free(old_table); 353 329 354 330 mutex_unlock(&em_pd_mutex); 331 + 332 + em_notify_pd_updated(pd); 355 333 return 0; 356 334 } 357 335 EXPORT_SYMBOL_GPL(em_dev_update_perf_domain); ··· 422 396 struct em_perf_table *em_table; 423 397 struct em_perf_domain *pd; 424 398 struct device *cpu_dev; 425 - int cpu, ret, num_cpus; 399 + int cpu, ret, num_cpus, id; 426 400 427 401 if (_is_cpu_device(dev)) { 428 402 num_cpus = cpumask_weight(cpus); ··· 445 419 } 446 420 447 421 pd->nr_perf_states = nr_states; 422 + 423 + INIT_LIST_HEAD(&pd->node); 424 + 425 + id = ida_alloc(&em_pd_ida, GFP_KERNEL); 426 + if (id < 0) 427 + return -ENOMEM; 428 + pd->id = id; 448 429 449 430 em_table = em_table_alloc(pd); 450 431 if (!em_table) ··· 477 444 kfree(em_table); 478 445 free_pd: 479 446 kfree(pd); 447 + ida_free(&em_pd_ida, id); 480 448 return -EINVAL; 481 449 } 482 450 ··· 693 659 694 660 unlock: 695 661 mutex_unlock(&em_pd_mutex); 662 + if (ret) 663 + return ret; 696 664 697 - return ret; 665 + mutex_lock(&em_pd_list_mutex); 666 + list_add_tail(&dev->em_pd->node, &em_pd_list); 667 + mutex_unlock(&em_pd_list_mutex); 668 + 669 + em_notify_pd_created(dev->em_pd); 670 + 671 + return 0; 698 672 } 699 673 EXPORT_SYMBOL_GPL(em_dev_register_pd_no_update); 700 674 ··· 720 678 if (_is_cpu_device(dev)) 721 679 return; 722 680 681 + mutex_lock(&em_pd_list_mutex); 682 + list_del_init(&dev->em_pd->node); 683 + mutex_unlock(&em_pd_list_mutex); 684 + 685 + em_notify_pd_deleted(dev->em_pd); 686 + 723 687 /* 724 688 * The mutex separates all register/unregister requests and protects 725 689 * from potential clean-up/setup issues in the debugfs directories. ··· 736 688 737 689 em_table_free(rcu_dereference_protected(dev->em_pd->em_table, 738 690 lockdep_is_held(&em_pd_mutex))); 691 + 692 + ida_free(&em_pd_ida, dev->em_pd->id); 739 693 740 694 kfree(dev->em_pd); 741 695 dev->em_pd = NULL; ··· 1008 958 */ 1009 959 schedule_work(&rebuild_sd_work); 1010 960 } 961 + 962 + #if defined(CONFIG_ENERGY_MODEL) && defined(CONFIG_NET) 963 + int for_each_em_perf_domain(int (*cb)(struct em_perf_domain*, void *), 964 + void *data) 965 + { 966 + struct em_perf_domain *pd; 967 + 968 + lockdep_assert_not_held(&em_pd_mutex); 969 + guard(mutex)(&em_pd_list_mutex); 970 + 971 + list_for_each_entry(pd, &em_pd_list, node) { 972 + int ret; 973 + 974 + ret = cb(pd, data); 975 + if (ret) 976 + return ret; 977 + } 978 + 979 + return 0; 980 + } 981 + 982 + struct em_perf_domain *em_perf_domain_get_by_id(int id) 983 + { 984 + struct em_perf_domain *pd; 985 + 986 + lockdep_assert_not_held(&em_pd_mutex); 987 + guard(mutex)(&em_pd_list_mutex); 988 + 989 + list_for_each_entry(pd, &em_pd_list, node) { 990 + if (pd->id == id) 991 + return pd; 992 + } 993 + 994 + return NULL; 995 + } 996 + #endif
+62 -58
rust/kernel/opp.rs
··· 87 87 88 88 use macros::vtable; 89 89 90 - /// Creates a null-terminated slice of pointers to [`Cstring`]s. 90 + /// Creates a null-terminated slice of pointers to [`CString`]s. 91 91 fn to_c_str_array(names: &[CString]) -> Result<KVec<*const u8>> { 92 92 // Allocated a null-terminated vector of pointers. 93 93 let mut list = KVec::with_capacity(names.len() + 1, GFP_KERNEL)?; ··· 443 443 /// 444 444 /// The returned [`ConfigToken`] will remove the configuration when dropped. 445 445 pub fn set(self, dev: &Device) -> Result<ConfigToken> { 446 - let (_clk_list, clk_names) = match &self.clk_names { 447 - Some(x) => { 448 - let list = to_c_str_array(x)?; 449 - let ptr = list.as_ptr(); 450 - (Some(list), ptr) 451 - } 452 - None => (None, ptr::null()), 446 + let clk_names = self.clk_names.as_deref().map(to_c_str_array).transpose()?; 447 + let regulator_names = self 448 + .regulator_names 449 + .as_deref() 450 + .map(to_c_str_array) 451 + .transpose()?; 452 + 453 + let set_config = || { 454 + let clk_names = clk_names.as_ref().map_or(ptr::null(), |c| c.as_ptr()); 455 + let regulator_names = regulator_names.as_ref().map_or(ptr::null(), |c| c.as_ptr()); 456 + 457 + let prop_name = self 458 + .prop_name 459 + .as_ref() 460 + .map_or(ptr::null(), |p| p.as_char_ptr()); 461 + 462 + let (supported_hw, supported_hw_count) = self 463 + .supported_hw 464 + .as_ref() 465 + .map_or((ptr::null(), 0), |hw| (hw.as_ptr(), hw.len() as u32)); 466 + 467 + let (required_dev, required_dev_index) = self 468 + .required_dev 469 + .as_ref() 470 + .map_or((ptr::null_mut(), 0), |(dev, idx)| (dev.as_raw(), *idx)); 471 + 472 + let mut config = bindings::dev_pm_opp_config { 473 + clk_names, 474 + config_clks: if T::HAS_CONFIG_CLKS { 475 + Some(Self::config_clks) 476 + } else { 477 + None 478 + }, 479 + prop_name, 480 + regulator_names, 481 + config_regulators: if T::HAS_CONFIG_REGULATORS { 482 + Some(Self::config_regulators) 483 + } else { 484 + None 485 + }, 486 + supported_hw, 487 + supported_hw_count, 488 + 489 + required_dev, 490 + required_dev_index, 491 + }; 492 + 493 + // SAFETY: The requirements are satisfied by the existence of [`Device`] and its safety 494 + // requirements. The OPP core guarantees not to access fields of [`Config`] after this 495 + // call and so we don't need to save a copy of them for future use. 496 + let ret = unsafe { bindings::dev_pm_opp_set_config(dev.as_raw(), &mut config) }; 497 + 498 + to_result(ret).map(|()| ConfigToken(ret)) 453 499 }; 454 500 455 - let (_regulator_list, regulator_names) = match &self.regulator_names { 456 - Some(x) => { 457 - let list = to_c_str_array(x)?; 458 - let ptr = list.as_ptr(); 459 - (Some(list), ptr) 460 - } 461 - None => (None, ptr::null()), 462 - }; 501 + // Ensure the closure does not accidentally drop owned data; if violated, the compiler 502 + // produces E0525 with e.g.: 503 + // 504 + // ``` 505 + // closure is `FnOnce` because it moves the variable `clk_names` out of its environment 506 + // ``` 507 + let _: &dyn Fn() -> _ = &set_config; 463 508 464 - let prop_name = self 465 - .prop_name 466 - .as_ref() 467 - .map_or(ptr::null(), |p| p.as_char_ptr()); 468 - 469 - let (supported_hw, supported_hw_count) = self 470 - .supported_hw 471 - .as_ref() 472 - .map_or((ptr::null(), 0), |hw| (hw.as_ptr(), hw.len() as u32)); 473 - 474 - let (required_dev, required_dev_index) = self 475 - .required_dev 476 - .as_ref() 477 - .map_or((ptr::null_mut(), 0), |(dev, idx)| (dev.as_raw(), *idx)); 478 - 479 - let mut config = bindings::dev_pm_opp_config { 480 - clk_names, 481 - config_clks: if T::HAS_CONFIG_CLKS { 482 - Some(Self::config_clks) 483 - } else { 484 - None 485 - }, 486 - prop_name, 487 - regulator_names, 488 - config_regulators: if T::HAS_CONFIG_REGULATORS { 489 - Some(Self::config_regulators) 490 - } else { 491 - None 492 - }, 493 - supported_hw, 494 - supported_hw_count, 495 - 496 - required_dev, 497 - required_dev_index, 498 - }; 499 - 500 - // SAFETY: The requirements are satisfied by the existence of [`Device`] and its safety 501 - // requirements. The OPP core guarantees not to access fields of [`Config`] after this call 502 - // and so we don't need to save a copy of them for future use. 503 - let ret = unsafe { bindings::dev_pm_opp_set_config(dev.as_raw(), &mut config) }; 504 - 505 - to_result(ret).map(|()| ConfigToken(ret)) 509 + set_config() 506 510 } 507 511 508 512 /// Config's clk callback.