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

PM: QoS: Introduce frequency QoS

Introduce frequency QoS, based on the "raw" low-level PM QoS, to
represent min and max frequency requests and aggregate constraints.

The min and max frequency requests are to be represented by
struct freq_qos_request objects and the aggregate constraints are to
be represented by struct freq_constraints objects. The latter are
expected to be initialized with the help of freq_constraints_init().

The freq_qos_read_value() helper is defined to retrieve the aggregate
constraints values from a given struct freq_constraints object and
there are the freq_qos_add_request(), freq_qos_update_request() and
freq_qos_remove_request() helpers to manipulate the min and max
frequency requests. It is assumed that the the helpers will not
run concurrently with each other for the same struct freq_qos_request
object, so if that may be the case, their uses must ensure proper
synchronization between them (e.g. through locking).

In addition, freq_qos_add_notifier() and freq_qos_remove_notifier()
are provided to add and remove notifiers that will trigger on aggregate
constraint changes to and from a given struct freq_constraints object,
respectively.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>

+284
+44
include/linux/pm_qos.h
··· 267 267 } 268 268 #endif 269 269 270 + #define FREQ_QOS_MIN_DEFAULT_VALUE 0 271 + #define FREQ_QOS_MAX_DEFAULT_VALUE (-1) 272 + 273 + enum freq_qos_req_type { 274 + FREQ_QOS_MIN = 1, 275 + FREQ_QOS_MAX, 276 + }; 277 + 278 + struct freq_constraints { 279 + struct pm_qos_constraints min_freq; 280 + struct blocking_notifier_head min_freq_notifiers; 281 + struct pm_qos_constraints max_freq; 282 + struct blocking_notifier_head max_freq_notifiers; 283 + }; 284 + 285 + struct freq_qos_request { 286 + enum freq_qos_req_type type; 287 + struct plist_node pnode; 288 + struct freq_constraints *qos; 289 + }; 290 + 291 + static inline int freq_qos_request_active(struct freq_qos_request *req) 292 + { 293 + return !IS_ERR_OR_NULL(req->qos); 294 + } 295 + 296 + void freq_constraints_init(struct freq_constraints *qos); 297 + 298 + s32 freq_qos_read_value(struct freq_constraints *qos, 299 + enum freq_qos_req_type type); 300 + 301 + int freq_qos_add_request(struct freq_constraints *qos, 302 + struct freq_qos_request *req, 303 + enum freq_qos_req_type type, s32 value); 304 + int freq_qos_update_request(struct freq_qos_request *req, s32 new_value); 305 + int freq_qos_remove_request(struct freq_qos_request *req); 306 + 307 + int freq_qos_add_notifier(struct freq_constraints *qos, 308 + enum freq_qos_req_type type, 309 + struct notifier_block *notifier); 310 + int freq_qos_remove_notifier(struct freq_constraints *qos, 311 + enum freq_qos_req_type type, 312 + struct notifier_block *notifier); 313 + 270 314 #endif
+240
kernel/power/qos.c
··· 650 650 } 651 651 652 652 late_initcall(pm_qos_power_init); 653 + 654 + /* Definitions related to the frequency QoS below. */ 655 + 656 + /** 657 + * freq_constraints_init - Initialize frequency QoS constraints. 658 + * @qos: Frequency QoS constraints to initialize. 659 + */ 660 + void freq_constraints_init(struct freq_constraints *qos) 661 + { 662 + struct pm_qos_constraints *c; 663 + 664 + c = &qos->min_freq; 665 + plist_head_init(&c->list); 666 + c->target_value = FREQ_QOS_MIN_DEFAULT_VALUE; 667 + c->default_value = FREQ_QOS_MIN_DEFAULT_VALUE; 668 + c->no_constraint_value = FREQ_QOS_MIN_DEFAULT_VALUE; 669 + c->type = PM_QOS_MAX; 670 + c->notifiers = &qos->min_freq_notifiers; 671 + BLOCKING_INIT_NOTIFIER_HEAD(c->notifiers); 672 + 673 + c = &qos->max_freq; 674 + plist_head_init(&c->list); 675 + c->target_value = FREQ_QOS_MAX_DEFAULT_VALUE; 676 + c->default_value = FREQ_QOS_MAX_DEFAULT_VALUE; 677 + c->no_constraint_value = FREQ_QOS_MAX_DEFAULT_VALUE; 678 + c->type = PM_QOS_MIN; 679 + c->notifiers = &qos->max_freq_notifiers; 680 + BLOCKING_INIT_NOTIFIER_HEAD(c->notifiers); 681 + } 682 + 683 + /** 684 + * freq_qos_read_value - Get frequency QoS constraint for a given list. 685 + * @qos: Constraints to evaluate. 686 + * @type: QoS request type. 687 + */ 688 + s32 freq_qos_read_value(struct freq_constraints *qos, 689 + enum freq_qos_req_type type) 690 + { 691 + s32 ret; 692 + 693 + switch (type) { 694 + case FREQ_QOS_MIN: 695 + ret = IS_ERR_OR_NULL(qos) ? 696 + FREQ_QOS_MIN_DEFAULT_VALUE : 697 + pm_qos_read_value(&qos->min_freq); 698 + break; 699 + case FREQ_QOS_MAX: 700 + ret = IS_ERR_OR_NULL(qos) ? 701 + FREQ_QOS_MAX_DEFAULT_VALUE : 702 + pm_qos_read_value(&qos->max_freq); 703 + break; 704 + default: 705 + WARN_ON(1); 706 + ret = 0; 707 + } 708 + 709 + return ret; 710 + } 711 + 712 + /** 713 + * freq_qos_apply - Add/modify/remove frequency QoS request. 714 + * @req: Constraint request to apply. 715 + * @action: Action to perform (add/update/remove). 716 + * @value: Value to assign to the QoS request. 717 + */ 718 + static int freq_qos_apply(struct freq_qos_request *req, 719 + enum pm_qos_req_action action, s32 value) 720 + { 721 + int ret; 722 + 723 + switch(req->type) { 724 + case FREQ_QOS_MIN: 725 + ret = pm_qos_update_target(&req->qos->min_freq, &req->pnode, 726 + action, value); 727 + break; 728 + case FREQ_QOS_MAX: 729 + ret = pm_qos_update_target(&req->qos->max_freq, &req->pnode, 730 + action, value); 731 + break; 732 + default: 733 + ret = -EINVAL; 734 + } 735 + 736 + return ret; 737 + } 738 + 739 + /** 740 + * freq_qos_add_request - Insert new frequency QoS request into a given list. 741 + * @qos: Constraints to update. 742 + * @req: Preallocated request object. 743 + * @type: Request type. 744 + * @value: Request value. 745 + * 746 + * Insert a new entry into the @qos list of requests, recompute the effective 747 + * QoS constraint value for that list and initialize the @req object. The 748 + * caller needs to save that object for later use in updates and removal. 749 + * 750 + * Return 1 if the effective constraint value has changed, 0 if the effective 751 + * constraint value has not changed, or a negative error code on failures. 752 + */ 753 + int freq_qos_add_request(struct freq_constraints *qos, 754 + struct freq_qos_request *req, 755 + enum freq_qos_req_type type, s32 value) 756 + { 757 + int ret; 758 + 759 + if (IS_ERR_OR_NULL(qos) || !req) 760 + return -EINVAL; 761 + 762 + if (WARN(freq_qos_request_active(req), 763 + "%s() called for active request\n", __func__)) 764 + return -EINVAL; 765 + 766 + req->qos = qos; 767 + req->type = type; 768 + ret = freq_qos_apply(req, PM_QOS_ADD_REQ, value); 769 + if (ret < 0) { 770 + req->qos = NULL; 771 + req->type = 0; 772 + } 773 + 774 + return ret; 775 + } 776 + EXPORT_SYMBOL_GPL(freq_qos_add_request); 777 + 778 + /** 779 + * freq_qos_update_request - Modify existing frequency QoS request. 780 + * @req: Request to modify. 781 + * @new_value: New request value. 782 + * 783 + * Update an existing frequency QoS request along with the effective constraint 784 + * value for the list of requests it belongs to. 785 + * 786 + * Return 1 if the effective constraint value has changed, 0 if the effective 787 + * constraint value has not changed, or a negative error code on failures. 788 + */ 789 + int freq_qos_update_request(struct freq_qos_request *req, s32 new_value) 790 + { 791 + if (!req) 792 + return -EINVAL; 793 + 794 + if (WARN(!freq_qos_request_active(req), 795 + "%s() called for unknown object\n", __func__)) 796 + return -EINVAL; 797 + 798 + if (req->pnode.prio == new_value) 799 + return 0; 800 + 801 + return freq_qos_apply(req, PM_QOS_UPDATE_REQ, new_value); 802 + } 803 + EXPORT_SYMBOL_GPL(freq_qos_update_request); 804 + 805 + /** 806 + * freq_qos_remove_request - Remove frequency QoS request from its list. 807 + * @req: Request to remove. 808 + * 809 + * Remove the given frequency QoS request from the list of constraints it 810 + * belongs to and recompute the effective constraint value for that list. 811 + * 812 + * Return 1 if the effective constraint value has changed, 0 if the effective 813 + * constraint value has not changed, or a negative error code on failures. 814 + */ 815 + int freq_qos_remove_request(struct freq_qos_request *req) 816 + { 817 + if (!req) 818 + return -EINVAL; 819 + 820 + if (WARN(!freq_qos_request_active(req), 821 + "%s() called for unknown object\n", __func__)) 822 + return -EINVAL; 823 + 824 + return freq_qos_apply(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); 825 + } 826 + EXPORT_SYMBOL_GPL(freq_qos_remove_request); 827 + 828 + /** 829 + * freq_qos_add_notifier - Add frequency QoS change notifier. 830 + * @qos: List of requests to add the notifier to. 831 + * @type: Request type. 832 + * @notifier: Notifier block to add. 833 + */ 834 + int freq_qos_add_notifier(struct freq_constraints *qos, 835 + enum freq_qos_req_type type, 836 + struct notifier_block *notifier) 837 + { 838 + int ret; 839 + 840 + if (IS_ERR_OR_NULL(qos) || !notifier) 841 + return -EINVAL; 842 + 843 + switch (type) { 844 + case FREQ_QOS_MIN: 845 + ret = blocking_notifier_chain_register(qos->min_freq.notifiers, 846 + notifier); 847 + break; 848 + case FREQ_QOS_MAX: 849 + ret = blocking_notifier_chain_register(qos->max_freq.notifiers, 850 + notifier); 851 + break; 852 + default: 853 + WARN_ON(1); 854 + ret = -EINVAL; 855 + } 856 + 857 + return ret; 858 + } 859 + EXPORT_SYMBOL_GPL(freq_qos_add_notifier); 860 + 861 + /** 862 + * freq_qos_remove_notifier - Remove frequency QoS change notifier. 863 + * @qos: List of requests to remove the notifier from. 864 + * @type: Request type. 865 + * @notifier: Notifier block to remove. 866 + */ 867 + int freq_qos_remove_notifier(struct freq_constraints *qos, 868 + enum freq_qos_req_type type, 869 + struct notifier_block *notifier) 870 + { 871 + int ret; 872 + 873 + if (IS_ERR_OR_NULL(qos) || !notifier) 874 + return -EINVAL; 875 + 876 + switch (type) { 877 + case FREQ_QOS_MIN: 878 + ret = blocking_notifier_chain_unregister(qos->min_freq.notifiers, 879 + notifier); 880 + break; 881 + case FREQ_QOS_MAX: 882 + ret = blocking_notifier_chain_unregister(qos->max_freq.notifiers, 883 + notifier); 884 + break; 885 + default: 886 + WARN_ON(1); 887 + ret = -EINVAL; 888 + } 889 + 890 + return ret; 891 + } 892 + EXPORT_SYMBOL_GPL(freq_qos_remove_notifier);