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.

at master 684 lines 19 kB view raw
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Power capping class 4 * Copyright (c) 2013, Intel Corporation. 5 */ 6 7#include <linux/module.h> 8#include <linux/device.h> 9#include <linux/err.h> 10#include <linux/kstrtox.h> 11#include <linux/slab.h> 12#include <linux/powercap.h> 13 14#define to_powercap_zone(n) container_of(n, struct powercap_zone, dev) 15#define to_powercap_control_type(n) \ 16 container_of(n, struct powercap_control_type, dev) 17 18/* Power zone show function */ 19#define define_power_zone_show(_attr) \ 20static ssize_t _attr##_show(struct device *dev, \ 21 struct device_attribute *dev_attr,\ 22 char *buf) \ 23{ \ 24 u64 value; \ 25 ssize_t len = -EINVAL; \ 26 struct powercap_zone *power_zone = to_powercap_zone(dev); \ 27 \ 28 if (power_zone->ops->get_##_attr) { \ 29 if (!power_zone->ops->get_##_attr(power_zone, &value)) \ 30 len = sysfs_emit(buf, "%lld\n", value); \ 31 } \ 32 \ 33 return len; \ 34} 35 36/* The only meaningful input is 0 (reset), others are silently ignored */ 37#define define_power_zone_store(_attr) \ 38static ssize_t _attr##_store(struct device *dev,\ 39 struct device_attribute *dev_attr, \ 40 const char *buf, size_t count) \ 41{ \ 42 int err; \ 43 struct powercap_zone *power_zone = to_powercap_zone(dev); \ 44 u64 value; \ 45 \ 46 err = kstrtoull(buf, 10, &value); \ 47 if (err) \ 48 return -EINVAL; \ 49 if (value) \ 50 return count; \ 51 if (power_zone->ops->reset_##_attr) { \ 52 if (!power_zone->ops->reset_##_attr(power_zone)) \ 53 return count; \ 54 } \ 55 \ 56 return -EINVAL; \ 57} 58 59/* Power zone constraint show function */ 60#define define_power_zone_constraint_show(_attr) \ 61static ssize_t show_constraint_##_attr(struct device *dev, \ 62 struct device_attribute *dev_attr,\ 63 char *buf) \ 64{ \ 65 u64 value; \ 66 ssize_t len = -ENODATA; \ 67 struct powercap_zone *power_zone = to_powercap_zone(dev); \ 68 int id; \ 69 struct powercap_zone_constraint *pconst;\ 70 \ 71 if (sscanf(dev_attr->attr.name, "constraint_%d_", &id) != 1) \ 72 return -EINVAL; \ 73 if (id >= power_zone->const_id_cnt) \ 74 return -EINVAL; \ 75 pconst = &power_zone->constraints[id]; \ 76 if (pconst && pconst->ops && pconst->ops->get_##_attr) { \ 77 if (!pconst->ops->get_##_attr(power_zone, id, &value)) \ 78 len = sysfs_emit(buf, "%lld\n", value); \ 79 } \ 80 \ 81 return len; \ 82} 83 84/* Power zone constraint store function */ 85#define define_power_zone_constraint_store(_attr) \ 86static ssize_t store_constraint_##_attr(struct device *dev,\ 87 struct device_attribute *dev_attr, \ 88 const char *buf, size_t count) \ 89{ \ 90 int err; \ 91 u64 value; \ 92 struct powercap_zone *power_zone = to_powercap_zone(dev); \ 93 int id; \ 94 struct powercap_zone_constraint *pconst;\ 95 \ 96 if (sscanf(dev_attr->attr.name, "constraint_%d_", &id) != 1) \ 97 return -EINVAL; \ 98 if (id >= power_zone->const_id_cnt) \ 99 return -EINVAL; \ 100 pconst = &power_zone->constraints[id]; \ 101 err = kstrtoull(buf, 10, &value); \ 102 if (err) \ 103 return -EINVAL; \ 104 if (pconst && pconst->ops && pconst->ops->set_##_attr) { \ 105 if (!pconst->ops->set_##_attr(power_zone, id, value)) \ 106 return count; \ 107 } \ 108 \ 109 return -ENODATA; \ 110} 111 112/* Power zone information callbacks */ 113define_power_zone_show(power_uw); 114define_power_zone_show(max_power_range_uw); 115define_power_zone_show(energy_uj); 116define_power_zone_store(energy_uj); 117define_power_zone_show(max_energy_range_uj); 118 119/* Power zone attributes */ 120static DEVICE_ATTR_RO(max_power_range_uw); 121static DEVICE_ATTR_RO(power_uw); 122static DEVICE_ATTR_RO(max_energy_range_uj); 123static DEVICE_ATTR_RW(energy_uj); 124 125/* Power zone constraint attributes callbacks */ 126define_power_zone_constraint_show(power_limit_uw); 127define_power_zone_constraint_store(power_limit_uw); 128define_power_zone_constraint_show(time_window_us); 129define_power_zone_constraint_store(time_window_us); 130define_power_zone_constraint_show(max_power_uw); 131define_power_zone_constraint_show(min_power_uw); 132define_power_zone_constraint_show(max_time_window_us); 133define_power_zone_constraint_show(min_time_window_us); 134 135/* For one time seeding of constraint device attributes */ 136struct powercap_constraint_attr { 137 struct device_attribute power_limit_attr; 138 struct device_attribute time_window_attr; 139 struct device_attribute max_power_attr; 140 struct device_attribute min_power_attr; 141 struct device_attribute max_time_window_attr; 142 struct device_attribute min_time_window_attr; 143 struct device_attribute name_attr; 144}; 145 146static struct powercap_constraint_attr 147 constraint_attrs[MAX_CONSTRAINTS_PER_ZONE]; 148 149/* A list of powercap control_types */ 150static LIST_HEAD(powercap_cntrl_list); 151/* Mutex to protect list of powercap control_types */ 152static DEFINE_MUTEX(powercap_cntrl_list_lock); 153 154#define POWERCAP_CONSTRAINT_NAME_LEN 30 /* Some limit to avoid overflow */ 155static ssize_t show_constraint_name(struct device *dev, 156 struct device_attribute *dev_attr, 157 char *buf) 158{ 159 const char *name; 160 struct powercap_zone *power_zone = to_powercap_zone(dev); 161 int id; 162 ssize_t len = -ENODATA; 163 struct powercap_zone_constraint *pconst; 164 165 if (sscanf(dev_attr->attr.name, "constraint_%d_", &id) != 1) 166 return -EINVAL; 167 if (id >= power_zone->const_id_cnt) 168 return -EINVAL; 169 pconst = &power_zone->constraints[id]; 170 171 if (pconst && pconst->ops && pconst->ops->get_name) { 172 name = pconst->ops->get_name(power_zone, id); 173 if (name) { 174 len = sysfs_emit(buf, "%.*s\n", 175 POWERCAP_CONSTRAINT_NAME_LEN - 1, name); 176 } 177 } 178 179 return len; 180} 181 182static int create_constraint_attribute(int id, const char *name, 183 int mode, 184 struct device_attribute *dev_attr, 185 ssize_t (*show)(struct device *, 186 struct device_attribute *, char *), 187 ssize_t (*store)(struct device *, 188 struct device_attribute *, 189 const char *, size_t) 190 ) 191{ 192 193 dev_attr->attr.name = kasprintf(GFP_KERNEL, "constraint_%d_%s", 194 id, name); 195 if (!dev_attr->attr.name) 196 return -ENOMEM; 197 dev_attr->attr.mode = mode; 198 dev_attr->show = show; 199 dev_attr->store = store; 200 201 return 0; 202} 203 204static void free_constraint_attributes(void) 205{ 206 int i; 207 208 for (i = 0; i < MAX_CONSTRAINTS_PER_ZONE; ++i) { 209 kfree(constraint_attrs[i].power_limit_attr.attr.name); 210 kfree(constraint_attrs[i].time_window_attr.attr.name); 211 kfree(constraint_attrs[i].name_attr.attr.name); 212 kfree(constraint_attrs[i].max_power_attr.attr.name); 213 kfree(constraint_attrs[i].min_power_attr.attr.name); 214 kfree(constraint_attrs[i].max_time_window_attr.attr.name); 215 kfree(constraint_attrs[i].min_time_window_attr.attr.name); 216 } 217} 218 219static int seed_constraint_attributes(void) 220{ 221 int i; 222 int ret; 223 224 for (i = 0; i < MAX_CONSTRAINTS_PER_ZONE; ++i) { 225 ret = create_constraint_attribute(i, "power_limit_uw", 226 S_IWUSR | S_IRUGO, 227 &constraint_attrs[i].power_limit_attr, 228 show_constraint_power_limit_uw, 229 store_constraint_power_limit_uw); 230 if (ret) 231 goto err_alloc; 232 ret = create_constraint_attribute(i, "time_window_us", 233 S_IWUSR | S_IRUGO, 234 &constraint_attrs[i].time_window_attr, 235 show_constraint_time_window_us, 236 store_constraint_time_window_us); 237 if (ret) 238 goto err_alloc; 239 ret = create_constraint_attribute(i, "name", S_IRUGO, 240 &constraint_attrs[i].name_attr, 241 show_constraint_name, 242 NULL); 243 if (ret) 244 goto err_alloc; 245 ret = create_constraint_attribute(i, "max_power_uw", S_IRUGO, 246 &constraint_attrs[i].max_power_attr, 247 show_constraint_max_power_uw, 248 NULL); 249 if (ret) 250 goto err_alloc; 251 ret = create_constraint_attribute(i, "min_power_uw", S_IRUGO, 252 &constraint_attrs[i].min_power_attr, 253 show_constraint_min_power_uw, 254 NULL); 255 if (ret) 256 goto err_alloc; 257 ret = create_constraint_attribute(i, "max_time_window_us", 258 S_IRUGO, 259 &constraint_attrs[i].max_time_window_attr, 260 show_constraint_max_time_window_us, 261 NULL); 262 if (ret) 263 goto err_alloc; 264 ret = create_constraint_attribute(i, "min_time_window_us", 265 S_IRUGO, 266 &constraint_attrs[i].min_time_window_attr, 267 show_constraint_min_time_window_us, 268 NULL); 269 if (ret) 270 goto err_alloc; 271 272 } 273 274 return 0; 275 276err_alloc: 277 free_constraint_attributes(); 278 279 return ret; 280} 281 282static int create_constraints(struct powercap_zone *power_zone, 283 int nr_constraints, 284 const struct powercap_zone_constraint_ops *const_ops) 285{ 286 int i; 287 int ret = 0; 288 int count; 289 struct powercap_zone_constraint *pconst; 290 291 if (!power_zone || !const_ops || !const_ops->get_power_limit_uw || 292 !const_ops->set_power_limit_uw || 293 !const_ops->get_time_window_us || 294 !const_ops->set_time_window_us) 295 return -EINVAL; 296 297 count = power_zone->zone_attr_count; 298 for (i = 0; i < nr_constraints; ++i) { 299 pconst = &power_zone->constraints[i]; 300 pconst->ops = const_ops; 301 pconst->id = power_zone->const_id_cnt; 302 power_zone->const_id_cnt++; 303 power_zone->zone_dev_attrs[count++] = 304 &constraint_attrs[i].power_limit_attr.attr; 305 power_zone->zone_dev_attrs[count++] = 306 &constraint_attrs[i].time_window_attr.attr; 307 if (pconst->ops->get_name) 308 power_zone->zone_dev_attrs[count++] = 309 &constraint_attrs[i].name_attr.attr; 310 if (pconst->ops->get_max_power_uw) 311 power_zone->zone_dev_attrs[count++] = 312 &constraint_attrs[i].max_power_attr.attr; 313 if (pconst->ops->get_min_power_uw) 314 power_zone->zone_dev_attrs[count++] = 315 &constraint_attrs[i].min_power_attr.attr; 316 if (pconst->ops->get_max_time_window_us) 317 power_zone->zone_dev_attrs[count++] = 318 &constraint_attrs[i].max_time_window_attr.attr; 319 if (pconst->ops->get_min_time_window_us) 320 power_zone->zone_dev_attrs[count++] = 321 &constraint_attrs[i].min_time_window_attr.attr; 322 } 323 power_zone->zone_attr_count = count; 324 325 return ret; 326} 327 328static bool control_type_valid(void *control_type) 329{ 330 struct powercap_control_type *pos = NULL; 331 bool found = false; 332 333 mutex_lock(&powercap_cntrl_list_lock); 334 335 list_for_each_entry(pos, &powercap_cntrl_list, node) { 336 if (pos == control_type) { 337 found = true; 338 break; 339 } 340 } 341 mutex_unlock(&powercap_cntrl_list_lock); 342 343 return found; 344} 345 346static ssize_t name_show(struct device *dev, 347 struct device_attribute *attr, 348 char *buf) 349{ 350 struct powercap_zone *power_zone = to_powercap_zone(dev); 351 352 return sysfs_emit(buf, "%s\n", power_zone->name); 353} 354 355static DEVICE_ATTR_RO(name); 356 357/* Create zone and attributes in sysfs */ 358static void create_power_zone_common_attributes( 359 struct powercap_zone *power_zone) 360{ 361 int count = 0; 362 363 power_zone->zone_dev_attrs[count++] = &dev_attr_name.attr; 364 if (power_zone->ops->get_max_energy_range_uj) 365 power_zone->zone_dev_attrs[count++] = 366 &dev_attr_max_energy_range_uj.attr; 367 if (power_zone->ops->get_energy_uj) { 368 if (power_zone->ops->reset_energy_uj) 369 dev_attr_energy_uj.attr.mode = S_IWUSR | S_IRUSR; 370 else 371 dev_attr_energy_uj.attr.mode = S_IRUSR; 372 power_zone->zone_dev_attrs[count++] = 373 &dev_attr_energy_uj.attr; 374 } 375 if (power_zone->ops->get_power_uw) 376 power_zone->zone_dev_attrs[count++] = 377 &dev_attr_power_uw.attr; 378 if (power_zone->ops->get_max_power_range_uw) 379 power_zone->zone_dev_attrs[count++] = 380 &dev_attr_max_power_range_uw.attr; 381 power_zone->zone_dev_attrs[count] = NULL; 382 power_zone->zone_attr_count = count; 383} 384 385static void powercap_release(struct device *dev) 386{ 387 bool allocated; 388 389 if (dev->parent) { 390 struct powercap_zone *power_zone = to_powercap_zone(dev); 391 392 /* Store flag as the release() may free memory */ 393 allocated = power_zone->allocated; 394 /* Remove id from parent idr struct */ 395 idr_remove(power_zone->parent_idr, power_zone->id); 396 /* Destroy idrs allocated for this zone */ 397 idr_destroy(&power_zone->idr); 398 kfree(power_zone->name); 399 kfree(power_zone->zone_dev_attrs); 400 kfree(power_zone->constraints); 401 if (power_zone->ops->release) 402 power_zone->ops->release(power_zone); 403 if (allocated) 404 kfree(power_zone); 405 } else { 406 struct powercap_control_type *control_type = 407 to_powercap_control_type(dev); 408 409 /* Store flag as the release() may free memory */ 410 allocated = control_type->allocated; 411 idr_destroy(&control_type->idr); 412 mutex_destroy(&control_type->lock); 413 if (control_type->ops && control_type->ops->release) 414 control_type->ops->release(control_type); 415 if (allocated) 416 kfree(control_type); 417 } 418} 419 420static ssize_t enabled_show(struct device *dev, 421 struct device_attribute *attr, 422 char *buf) 423{ 424 bool mode = true; 425 426 /* Default is enabled */ 427 if (dev->parent) { 428 struct powercap_zone *power_zone = to_powercap_zone(dev); 429 if (power_zone->ops->get_enable) 430 if (power_zone->ops->get_enable(power_zone, &mode)) 431 mode = false; 432 } else { 433 struct powercap_control_type *control_type = 434 to_powercap_control_type(dev); 435 if (control_type->ops && control_type->ops->get_enable) 436 if (control_type->ops->get_enable(control_type, &mode)) 437 mode = false; 438 } 439 440 return sysfs_emit(buf, "%d\n", mode); 441} 442 443static ssize_t enabled_store(struct device *dev, 444 struct device_attribute *attr, 445 const char *buf, size_t len) 446{ 447 bool mode; 448 449 if (kstrtobool(buf, &mode)) 450 return -EINVAL; 451 if (dev->parent) { 452 struct powercap_zone *power_zone = to_powercap_zone(dev); 453 if (power_zone->ops->set_enable) 454 if (!power_zone->ops->set_enable(power_zone, mode)) 455 return len; 456 } else { 457 struct powercap_control_type *control_type = 458 to_powercap_control_type(dev); 459 if (control_type->ops && control_type->ops->set_enable) 460 if (!control_type->ops->set_enable(control_type, mode)) 461 return len; 462 } 463 464 return -ENOSYS; 465} 466 467static DEVICE_ATTR_RW(enabled); 468 469static struct attribute *powercap_attrs[] = { 470 &dev_attr_enabled.attr, 471 NULL, 472}; 473ATTRIBUTE_GROUPS(powercap); 474 475static struct class powercap_class = { 476 .name = "powercap", 477 .dev_release = powercap_release, 478 .dev_groups = powercap_groups, 479}; 480 481struct powercap_zone *powercap_register_zone( 482 struct powercap_zone *power_zone, 483 struct powercap_control_type *control_type, 484 const char *name, 485 struct powercap_zone *parent, 486 const struct powercap_zone_ops *ops, 487 int nr_constraints, 488 const struct powercap_zone_constraint_ops *const_ops) 489{ 490 int result; 491 int nr_attrs; 492 493 if (!name || !control_type || !ops || 494 nr_constraints > MAX_CONSTRAINTS_PER_ZONE || 495 (!ops->get_energy_uj && !ops->get_power_uw) || 496 !control_type_valid(control_type)) 497 return ERR_PTR(-EINVAL); 498 499 if (power_zone) { 500 if (!ops->release) 501 return ERR_PTR(-EINVAL); 502 memset(power_zone, 0, sizeof(*power_zone)); 503 } else { 504 power_zone = kzalloc_obj(*power_zone); 505 if (!power_zone) 506 return ERR_PTR(-ENOMEM); 507 power_zone->allocated = true; 508 } 509 power_zone->ops = ops; 510 power_zone->control_type_inst = control_type; 511 if (!parent) { 512 power_zone->dev.parent = &control_type->dev; 513 power_zone->parent_idr = &control_type->idr; 514 } else { 515 power_zone->dev.parent = &parent->dev; 516 power_zone->parent_idr = &parent->idr; 517 } 518 power_zone->dev.class = &powercap_class; 519 520 mutex_lock(&control_type->lock); 521 /* Using idr to get the unique id */ 522 result = idr_alloc(power_zone->parent_idr, NULL, 0, 0, GFP_KERNEL); 523 if (result < 0) 524 goto err_idr_alloc; 525 526 power_zone->id = result; 527 idr_init(&power_zone->idr); 528 result = -ENOMEM; 529 power_zone->name = kstrdup(name, GFP_KERNEL); 530 if (!power_zone->name) 531 goto err_name_alloc; 532 power_zone->constraints = kzalloc_objs(*power_zone->constraints, 533 nr_constraints); 534 if (!power_zone->constraints) 535 goto err_const_alloc; 536 537 nr_attrs = nr_constraints * POWERCAP_CONSTRAINTS_ATTRS + 538 POWERCAP_ZONE_MAX_ATTRS + 1; 539 power_zone->zone_dev_attrs = kcalloc(nr_attrs, sizeof(void *), 540 GFP_KERNEL); 541 if (!power_zone->zone_dev_attrs) 542 goto err_attr_alloc; 543 create_power_zone_common_attributes(power_zone); 544 result = create_constraints(power_zone, nr_constraints, const_ops); 545 if (result) 546 goto err_dev_ret; 547 548 power_zone->zone_dev_attrs[power_zone->zone_attr_count] = NULL; 549 power_zone->dev_zone_attr_group.attrs = power_zone->zone_dev_attrs; 550 power_zone->dev_attr_groups[0] = &power_zone->dev_zone_attr_group; 551 power_zone->dev_attr_groups[1] = NULL; 552 power_zone->dev.groups = power_zone->dev_attr_groups; 553 dev_set_name(&power_zone->dev, "%s:%x", 554 dev_name(power_zone->dev.parent), 555 power_zone->id); 556 result = device_register(&power_zone->dev); 557 if (result) { 558 put_device(&power_zone->dev); 559 mutex_unlock(&control_type->lock); 560 561 return ERR_PTR(result); 562 } 563 564 control_type->nr_zones++; 565 mutex_unlock(&control_type->lock); 566 567 return power_zone; 568 569err_dev_ret: 570 kfree(power_zone->zone_dev_attrs); 571err_attr_alloc: 572 kfree(power_zone->constraints); 573err_const_alloc: 574 kfree(power_zone->name); 575err_name_alloc: 576 idr_remove(power_zone->parent_idr, power_zone->id); 577err_idr_alloc: 578 if (power_zone->allocated) 579 kfree(power_zone); 580 mutex_unlock(&control_type->lock); 581 582 return ERR_PTR(result); 583} 584EXPORT_SYMBOL_GPL(powercap_register_zone); 585 586int powercap_unregister_zone(struct powercap_control_type *control_type, 587 struct powercap_zone *power_zone) 588{ 589 if (!power_zone || !control_type) 590 return -EINVAL; 591 592 mutex_lock(&control_type->lock); 593 control_type->nr_zones--; 594 mutex_unlock(&control_type->lock); 595 596 device_unregister(&power_zone->dev); 597 598 return 0; 599} 600EXPORT_SYMBOL_GPL(powercap_unregister_zone); 601 602struct powercap_control_type *powercap_register_control_type( 603 struct powercap_control_type *control_type, 604 const char *name, 605 const struct powercap_control_type_ops *ops) 606{ 607 int result; 608 609 if (!name) 610 return ERR_PTR(-EINVAL); 611 if (control_type) { 612 if (!ops || !ops->release) 613 return ERR_PTR(-EINVAL); 614 memset(control_type, 0, sizeof(*control_type)); 615 } else { 616 control_type = kzalloc_obj(*control_type); 617 if (!control_type) 618 return ERR_PTR(-ENOMEM); 619 control_type->allocated = true; 620 } 621 mutex_init(&control_type->lock); 622 control_type->ops = ops; 623 INIT_LIST_HEAD(&control_type->node); 624 control_type->dev.class = &powercap_class; 625 dev_set_name(&control_type->dev, "%s", name); 626 idr_init(&control_type->idr); 627 628 mutex_lock(&powercap_cntrl_list_lock); 629 list_add_tail(&control_type->node, &powercap_cntrl_list); 630 mutex_unlock(&powercap_cntrl_list_lock); 631 632 result = device_register(&control_type->dev); 633 if (result) { 634 mutex_lock(&powercap_cntrl_list_lock); 635 list_del(&control_type->node); 636 mutex_unlock(&powercap_cntrl_list_lock); 637 638 idr_destroy(&control_type->idr); 639 put_device(&control_type->dev); 640 return ERR_PTR(result); 641 } 642 643 return control_type; 644} 645EXPORT_SYMBOL_GPL(powercap_register_control_type); 646 647int powercap_unregister_control_type(struct powercap_control_type *control_type) 648{ 649 struct powercap_control_type *pos = NULL; 650 651 if (control_type->nr_zones) { 652 dev_err(&control_type->dev, "Zones of this type still not freed\n"); 653 return -EINVAL; 654 } 655 mutex_lock(&powercap_cntrl_list_lock); 656 list_for_each_entry(pos, &powercap_cntrl_list, node) { 657 if (pos == control_type) { 658 list_del(&control_type->node); 659 mutex_unlock(&powercap_cntrl_list_lock); 660 device_unregister(&control_type->dev); 661 return 0; 662 } 663 } 664 mutex_unlock(&powercap_cntrl_list_lock); 665 666 return -ENODEV; 667} 668EXPORT_SYMBOL_GPL(powercap_unregister_control_type); 669 670static int __init powercap_init(void) 671{ 672 int result; 673 674 result = seed_constraint_attributes(); 675 if (result) 676 return result; 677 678 return class_register(&powercap_class); 679} 680 681fs_initcall(powercap_init); 682 683MODULE_DESCRIPTION("PowerCap sysfs Driver"); 684MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");