| @@ -181,6 +181,7 @@ static void group_init(struct psi_group *group) · 180 unmodified lines |
| 181 | 181 | { |
| 182 | 182 | int cpu; |
| 183 | 183 | |
| 184 | + group->enabled = true; |
| 184 | 185 | for_each_possible_cpu(cpu) |
| 185 | 186 | seqcount_init(&per_cpu_ptr(group->pcpu, cpu)->seq); |
| 186 | 187 | group->avg_last_update = sched_clock(); |
| @@ -201,6 +202,7 @@ void __init psi_init(void) · 14 unmodified lines |
| 201 | 202 | { |
| 202 | 203 | if (!psi_enable) { |
| 203 | 204 | static_branch_enable(&psi_disabled); |
| 205 | + static_branch_disable(&psi_cgroups_enabled); |
| 204 | 206 | return; |
| 205 | 207 | } |
| 206 | 208 | |
| @@ -211,7 +213,7 @@ void __init psi_init(void) · 4 unmodified lines |
| 211 | 213 | group_init(&psi_system); |
| 212 | 214 | } |
| 213 | 215 | |
| 214 | | −static bool test_state(unsigned int *tasks, enum psi_states state) |
| 216 | +static bool test_state(unsigned int *tasks, enum psi_states state, bool oncpu) |
| 215 | 217 | { |
| 216 | 218 | switch (state) { |
| 217 | 219 | case PSI_IO_SOME: |
| @@ -224,9 +226,9 @@ static bool test_state(unsigned int *tasks, enum psi_states state) · 6 unmodified lines |
| 224 | 226 | return unlikely(tasks[NR_MEMSTALL] && |
| 225 | 227 | tasks[NR_RUNNING] == tasks[NR_MEMSTALL_RUNNING]); |
| 226 | 228 | case PSI_CPU_SOME: |
| 227 | | − return unlikely(tasks[NR_RUNNING] > tasks[NR_ONCPU]); |
| 229 | + return unlikely(tasks[NR_RUNNING] > oncpu); |
| 228 | 230 | case PSI_CPU_FULL: |
| 229 | | − return unlikely(tasks[NR_RUNNING] && !tasks[NR_ONCPU]); |
| 231 | + return unlikely(tasks[NR_RUNNING] && !oncpu); |
| 230 | 232 | case PSI_NONIDLE: |
| 231 | 233 | return tasks[NR_IOWAIT] || tasks[NR_MEMSTALL] || |
| 232 | 234 | tasks[NR_RUNNING]; |
| @@ -688,35 +690,53 @@ static void psi_group_change(struct psi_group *group, int cpu, · 455 unmodified lines |
| 688 | 690 | bool wake_clock) |
| 689 | 691 | { |
| 690 | 692 | struct psi_group_cpu *groupc; |
| 691 | | − u32 state_mask = 0; |
| 692 | 693 | unsigned int t, m; |
| 693 | 694 | enum psi_states s; |
| 695 | + u32 state_mask; |
| 694 | 696 | |
| 695 | 697 | groupc = per_cpu_ptr(group->pcpu, cpu); |
| 696 | 698 | |
| 697 | 699 | /* |
| 698 | | − * First we assess the aggregate resource states this CPU's |
| 699 | | − * tasks have been in since the last change, and account any |
| 700 | | − * SOME and FULL time these may have resulted in. |
| 701 | | − * |
| 702 | | − * Then we update the task counts according to the state |
| 700 | + * First we update the task counts according to the state |
| 703 | 701 | * change requested through the @clear and @set bits. |
| 702 | + * |
| 703 | + * Then if the cgroup PSI stats accounting enabled, we |
| 704 | + * assess the aggregate resource states this CPU's tasks |
| 705 | + * have been in since the last change, and account any |
| 706 | + * SOME and FULL time these may have resulted in. |
| 704 | 707 | */ |
| 705 | 708 | write_seqcount_begin(&groupc->seq); |
| 706 | 709 | |
| 707 | | − record_times(groupc, now); |
| 710 | + /* |
| 711 | + * Start with TSK_ONCPU, which doesn't have a corresponding |
| 712 | + * task count - it's just a boolean flag directly encoded in |
| 713 | + * the state mask. Clear, set, or carry the current state if |
| 714 | + * no changes are requested. |
| 715 | + */ |
| 716 | + if (unlikely(clear & TSK_ONCPU)) { |
| 717 | + state_mask = 0; |
| 718 | + clear &= ~TSK_ONCPU; |
| 719 | + } else if (unlikely(set & TSK_ONCPU)) { |
| 720 | + state_mask = PSI_ONCPU; |
| 721 | + set &= ~TSK_ONCPU; |
| 722 | + } else { |
| 723 | + state_mask = groupc->state_mask & PSI_ONCPU; |
| 724 | + } |
| 708 | 725 | |
| 726 | + /* |
| 727 | + * The rest of the state mask is calculated based on the task |
| 728 | + * counts. Update those first, then construct the mask. |
| 729 | + */ |
| 709 | 730 | for (t = 0, m = clear; m; m &= ~(1 << t), t++) { |
| 710 | 731 | if (!(m & (1 << t))) |
| 711 | 732 | continue; |
| 712 | 733 | if (groupc->tasks[t]) { |
| 713 | 734 | groupc->tasks[t]--; |
| 714 | 735 | } else if (!psi_bug) { |
| 715 | | − printk_deferred(KERN_ERR "psi: task underflow! cpu=%d t=%d tasks=[%u %u %u %u %u] clear=%x set=%x\n", |
| 736 | + printk_deferred(KERN_ERR "psi: task underflow! cpu=%d t=%d tasks=[%u %u %u %u] clear=%x set=%x\n", |
| 716 | 737 | cpu, t, groupc->tasks[0], |
| 717 | 738 | groupc->tasks[1], groupc->tasks[2], |
| 718 | | − groupc->tasks[3], groupc->tasks[4], |
| 719 | | − clear, set); |
| 739 | + groupc->tasks[3], clear, set); |
| 720 | 740 | psi_bug = 1; |
| 721 | 741 | } |
| 722 | 742 | } |
| @@ -725,9 +745,25 @@ static void psi_group_change(struct psi_group *group, int cpu, · 2 unmodified lines |
| 725 | 745 | if (set & (1 << t)) |
| 726 | 746 | groupc->tasks[t]++; |
| 727 | 747 | |
| 728 | | − /* Calculate state mask representing active states */ |
| 748 | + if (!group->enabled) { |
| 749 | + /* |
| 750 | + * On the first group change after disabling PSI, conclude |
| 751 | + * the current state and flush its time. This is unlikely |
| 752 | + * to matter to the user, but aggregation (get_recent_times) |
| 753 | + * may have already incorporated the live state into times_prev; |
| 754 | + * avoid a delta sample underflow when PSI is later re-enabled. |
| 755 | + */ |
| 756 | + if (unlikely(groupc->state_mask & (1 << PSI_NONIDLE))) |
| 757 | + record_times(groupc, now); |
| 758 | + |
| 759 | + groupc->state_mask = state_mask; |
| 760 | + |
| 761 | + write_seqcount_end(&groupc->seq); |
| 762 | + return; |
| 763 | + } |
| 764 | + |
| 729 | 765 | for (s = 0; s < NR_PSI_STATES; s++) { |
| 730 | | − if (test_state(groupc->tasks, s)) |
| 766 | + if (test_state(groupc->tasks, s, state_mask & PSI_ONCPU)) |
| 731 | 767 | state_mask |= (1 << s); |
| 732 | 768 | } |
| 733 | 769 | |
| @@ -739,9 +775,11 @@ static void psi_group_change(struct psi_group *group, int cpu, · 5 unmodified lines |
| 739 | 775 | * task in a cgroup is in_memstall, the corresponding groupc |
| 740 | 776 | * on that cpu is in PSI_MEM_FULL state. |
| 741 | 777 | */ |
| 742 | | − if (unlikely(groupc->tasks[NR_ONCPU] && cpu_curr(cpu)->in_memstall)) |
| 778 | + if (unlikely((state_mask & PSI_ONCPU) && cpu_curr(cpu)->in_memstall)) |
| 743 | 779 | state_mask |= (1 << PSI_MEM_FULL); |
| 744 | 780 | |
| 781 | + record_times(groupc, now); |
| 782 | + |
| 745 | 783 | groupc->state_mask = state_mask; |
| 746 | 784 | |
| 747 | 785 | write_seqcount_end(&groupc->seq); |
| @@ -753,27 +791,12 @@ static void psi_group_change(struct psi_group *group, int cpu, · 5 unmodified lines |
| 753 | 791 | schedule_delayed_work(&group->avgs_work, PSI_FREQ); |
| 754 | 792 | } |
| 755 | 793 | |
| 756 | | −static struct psi_group *iterate_groups(struct task_struct *task, void **iter) |
| 794 | +static inline struct psi_group *task_psi_group(struct task_struct *task) |
| 757 | 795 | { |
| 758 | | − if (*iter == &psi_system) |
| 759 | | − return NULL; |
| 760 | | − |
| 761 | 796 | #ifdef CONFIG_CGROUPS |
| 762 | | − if (static_branch_likely(&psi_cgroups_enabled)) { |
| 763 | | − struct cgroup *cgroup = NULL; |
| 764 | | − |
| 765 | | − if (!*iter) |
| 766 | | − cgroup = task->cgroups->dfl_cgrp; |
| 767 | | − else |
| 768 | | − cgroup = cgroup_parent(*iter); |
| 769 | | − |
| 770 | | − if (cgroup && cgroup_parent(cgroup)) { |
| 771 | | − *iter = cgroup; |
| 772 | | − return cgroup_psi(cgroup); |
| 773 | | − } |
| 774 | | − } |
| 797 | + if (static_branch_likely(&psi_cgroups_enabled)) |
| 798 | + return cgroup_psi(task_dfl_cgroup(task)); |
| 775 | 799 | #endif |
| 776 | | − *iter = &psi_system; |
| 777 | 800 | return &psi_system; |
| 778 | 801 | } |
| 779 | 802 | |
| @@ -796,8 +819,6 @@ void psi_task_change(struct task_struct *task, int clear, int set) · 16 unmodified lines |
| 796 | 819 | { |
| 797 | 820 | int cpu = task_cpu(task); |
| 798 | 821 | struct psi_group *group; |
| 799 | | − bool wake_clock = true; |
| 800 | | − void *iter = NULL; |
| 801 | 822 | u64 now; |
| 802 | 823 | |
| 803 | 824 | if (!task->pid) |
| @@ -806,19 +827,11 @@ void psi_task_change(struct task_struct *task, int clear, int set) · 2 unmodified lines |
| 806 | 827 | psi_flags_change(task, clear, set); |
| 807 | 828 | |
| 808 | 829 | now = cpu_clock(cpu); |
| 809 | | − /* |
| 810 | | − * Periodic aggregation shuts off if there is a period of no |
| 811 | | − * task changes, so we wake it back up if necessary. However, |
| 812 | | − * don't do this if the task change is the aggregation worker |
| 813 | | − * itself going to sleep, or we'll ping-pong forever. |
| 814 | | − */ |
| 815 | | − if (unlikely((clear & TSK_RUNNING) && |
| 816 | | − (task->flags & PF_WQ_WORKER) && |
| 817 | | − wq_worker_last_func(task) == psi_avgs_work)) |
| 818 | | − wake_clock = false; |
| 819 | 830 | |
| 820 | | − while ((group = iterate_groups(task, &iter))) |
| 821 | | − psi_group_change(group, cpu, clear, set, now, wake_clock); |
| 831 | + group = task_psi_group(task); |
| 832 | + do { |
| 833 | + psi_group_change(group, cpu, clear, set, now, true); |
| 834 | + } while ((group = group->parent)); |
| 822 | 835 | } |
| 823 | 836 | |
| 824 | 837 | void psi_task_switch(struct task_struct *prev, struct task_struct *next, |
| @@ -826,34 +839,30 @@ void psi_task_switch(struct task_struct *prev, struct task_struct *next, · 1 unmodified line |
| 826 | 839 | { |
| 827 | 840 | struct psi_group *group, *common = NULL; |
| 828 | 841 | int cpu = task_cpu(prev); |
| 829 | | − void *iter; |
| 830 | 842 | u64 now = cpu_clock(cpu); |
| 831 | 843 | |
| 832 | 844 | if (next->pid) { |
| 833 | | − bool identical_state; |
| 834 | | − |
| 835 | 845 | psi_flags_change(next, 0, TSK_ONCPU); |
| 836 | 846 | /* |
| 837 | | − * When switching between tasks that have an identical |
| 838 | | − * runtime state, the cgroup that contains both tasks |
| 839 | | − * we reach the first common ancestor. Iterate @next's |
| 840 | | − * ancestors only until we encounter @prev's ONCPU. |
| 847 | + * Set TSK_ONCPU on @next's cgroups. If @next shares any |
| 848 | + * ancestors with @prev, those will already have @prev's |
| 849 | + * TSK_ONCPU bit set, and we can stop the iteration there. |
| 841 | 850 | */ |
| 842 | | − identical_state = prev->psi_flags == next->psi_flags; |
| 843 | | − iter = NULL; |
| 844 | | − while ((group = iterate_groups(next, &iter))) { |
| 845 | | − if (identical_state && |
| 846 | | − per_cpu_ptr(group->pcpu, cpu)->tasks[NR_ONCPU]) { |
| 851 | + group = task_psi_group(next); |
| 852 | + do { |
| 853 | + if (per_cpu_ptr(group->pcpu, cpu)->state_mask & |
| 854 | + PSI_ONCPU) { |
| 847 | 855 | common = group; |
| 848 | 856 | break; |
| 849 | 857 | } |
| 850 | 858 | |
| 851 | 859 | psi_group_change(group, cpu, 0, TSK_ONCPU, now, true); |
| 852 | | − } |
| 860 | + } while ((group = group->parent)); |
| 853 | 861 | } |
| 854 | 862 | |
| 855 | 863 | if (prev->pid) { |
| 856 | 864 | int clear = TSK_ONCPU, set = 0; |
| 865 | + bool wake_clock = true; |
| 857 | 866 | |
| 858 | 867 | /* |
| 859 | 868 | * When we're going to sleep, psi_dequeue() lets us |
| @@ -867,26 +876,74 @@ void psi_task_switch(struct task_struct *prev, struct task_struct *next, · 7 unmodified lines |
| 867 | 876 | clear |= TSK_MEMSTALL_RUNNING; |
| 868 | 877 | if (prev->in_iowait) |
| 869 | 878 | set |= TSK_IOWAIT; |
| 879 | + |
| 880 | + /* |
| 881 | + * Periodic aggregation shuts off if there is a period of no |
| 882 | + * task changes, so we wake it back up if necessary. However, |
| 883 | + * don't do this if the task change is the aggregation worker |
| 884 | + * itself going to sleep, or we'll ping-pong forever. |
| 885 | + */ |
| 886 | + if (unlikely((prev->flags & PF_WQ_WORKER) && |
| 887 | + wq_worker_last_func(prev) == psi_avgs_work)) |
| 888 | + wake_clock = false; |
| 870 | 889 | } |
| 871 | 890 | |
| 872 | 891 | psi_flags_change(prev, clear, set); |
| 873 | 892 | |
| 874 | | − iter = NULL; |
| 875 | | − while ((group = iterate_groups(prev, &iter)) && group != common) |
| 876 | | − psi_group_change(group, cpu, clear, set, now, true); |
| 893 | + group = task_psi_group(prev); |
| 894 | + do { |
| 895 | + if (group == common) |
| 896 | + break; |
| 897 | + psi_group_change(group, cpu, clear, set, now, wake_clock); |
| 898 | + } while ((group = group->parent)); |
| 877 | 899 | |
| 878 | 900 | /* |
| 879 | | − * TSK_ONCPU is handled up to the common ancestor. If we're tasked |
| 880 | | − * with dequeuing too, finish that for the rest of the hierarchy. |
| 901 | + * TSK_ONCPU is handled up to the common ancestor. If there are |
| 902 | + * any other differences between the two tasks (e.g. prev goes |
| 903 | + * to sleep, or only one task is memstall), finish propagating |
| 904 | + * those differences all the way up to the root. |
| 881 | 905 | */ |
| 882 | | − if (sleep) { |
| 906 | + if ((prev->psi_flags ^ next->psi_flags) & ~TSK_ONCPU) { |
| 883 | 907 | clear &= ~TSK_ONCPU; |
| 884 | | − for (; group; group = iterate_groups(prev, &iter)) |
| 885 | | − psi_group_change(group, cpu, clear, set, now, true); |
| 908 | + for (; group; group = group->parent) |
| 909 | + psi_group_change(group, cpu, clear, set, now, wake_clock); |
| 886 | 910 | } |
| 887 | 911 | } |
| 888 | 912 | } |
| 889 | 913 | |
| 914 | +#ifdef CONFIG_IRQ_TIME_ACCOUNTING |
| 915 | +void psi_account_irqtime(struct task_struct *task, u32 delta) |
| 916 | +{ |
| 917 | + int cpu = task_cpu(task); |
| 918 | + struct psi_group *group; |
| 919 | + struct psi_group_cpu *groupc; |
| 920 | + u64 now; |
| 921 | + |
| 922 | + if (!task->pid) |
| 923 | + return; |
| 924 | + |
| 925 | + now = cpu_clock(cpu); |
| 926 | + |
| 927 | + group = task_psi_group(task); |
| 928 | + do { |
| 929 | + if (!group->enabled) |
| 930 | + continue; |
| 931 | + |
| 932 | + groupc = per_cpu_ptr(group->pcpu, cpu); |
| 933 | + |
| 934 | + write_seqcount_begin(&groupc->seq); |
| 935 | + |
| 936 | + record_times(groupc, now); |
| 937 | + groupc->times[PSI_IRQ_FULL] += delta; |
| 938 | + |
| 939 | + write_seqcount_end(&groupc->seq); |
| 940 | + |
| 941 | + if (group->poll_states & (1 << PSI_IRQ_FULL)) |
| 942 | + psi_schedule_poll_work(group, 1); |
| 943 | + } while ((group = group->parent)); |
| 944 | +} |
| 945 | +#endif |
| 946 | + |
| 890 | 947 | /** |
| 891 | 948 | * psi_memstall_enter - mark the beginning of a memory stall section |
| 892 | 949 | * @flags: flags to handle nested sections |
| @@ -952,7 +1009,7 @@ EXPORT_SYMBOL_GPL(psi_memstall_leave); · 59 unmodified lines |
| 952 | 1009 | #ifdef CONFIG_CGROUPS |
| 953 | 1010 | int psi_cgroup_alloc(struct cgroup *cgroup) |
| 954 | 1011 | { |
| 955 | | − if (static_branch_likely(&psi_disabled)) |
| 1012 | + if (!static_branch_likely(&psi_cgroups_enabled)) |
| 956 | 1013 | return 0; |
| 957 | 1014 | |
| 958 | 1015 | cgroup->psi = kzalloc(sizeof(struct psi_group), GFP_KERNEL); |
| @@ -965,12 +1022,13 @@ int psi_cgroup_alloc(struct cgroup *cgroup) · 6 unmodified lines |
| 965 | 1022 | return -ENOMEM; |
| 966 | 1023 | } |
| 967 | 1024 | group_init(cgroup->psi); |
| 1025 | + cgroup->psi->parent = cgroup_psi(cgroup_parent(cgroup)); |
| 968 | 1026 | return 0; |
| 969 | 1027 | } |
| 970 | 1028 | |
| 971 | 1029 | void psi_cgroup_free(struct cgroup *cgroup) |
| 972 | 1030 | { |
| 973 | | − if (static_branch_likely(&psi_disabled)) |
| 1031 | + if (!static_branch_likely(&psi_cgroups_enabled)) |
| 974 | 1032 | return; |
| 975 | 1033 | |
| 976 | 1034 | cancel_delayed_work_sync(&cgroup->psi->avgs_work); |
| @@ -998,7 +1056,7 @@ void cgroup_move_task(struct task_struct *task, struct css_set *to) · 21 unmodified lines |
| 998 | 1056 | struct rq_flags rf; |
| 999 | 1057 | struct rq *rq; |
| 1000 | 1058 | |
| 1001 | | − if (static_branch_likely(&psi_disabled)) { |
| 1059 | + if (!static_branch_likely(&psi_cgroups_enabled)) { |
| 1002 | 1060 | /* |
| 1003 | 1061 | * Lame to do this here, but the scheduler cannot be locked |
| 1004 | 1062 | * from the outside, so we move cgroups from inside sched/. |
| @@ -1046,10 +1104,45 @@ void cgroup_move_task(struct task_struct *task, struct css_set *to) · 41 unmodified lines |
| 1046 | 1104 | |
| 1047 | 1105 | task_rq_unlock(rq, task, &rf); |
| 1048 | 1106 | } |
| 1107 | + |
| 1108 | +void psi_cgroup_restart(struct psi_group *group) |
| 1109 | +{ |
| 1110 | + int cpu; |
| 1111 | + |
| 1112 | + /* |
| 1113 | + * After we disable psi_group->enabled, we don't actually |
| 1114 | + * stop percpu tasks accounting in each psi_group_cpu, |
| 1115 | + * instead only stop test_state() loop, record_times() |
| 1116 | + * and averaging worker, see psi_group_change() for details. |
| 1117 | + * |
| 1118 | + * When disable cgroup PSI, this function has nothing to sync |
| 1119 | + * since cgroup pressure files are hidden and percpu psi_group_cpu |
| 1120 | + * would see !psi_group->enabled and only do task accounting. |
| 1121 | + * |
| 1122 | + * When re-enable cgroup PSI, this function use psi_group_change() |
| 1123 | + * to get correct state mask from test_state() loop on tasks[], |
| 1124 | + * and restart groupc->state_start from now, use .clear = .set = 0 |
| 1125 | + * here since no task status really changed. |
| 1126 | + */ |
| 1127 | + if (!group->enabled) |
| 1128 | + return; |
| 1129 | + |
| 1130 | + for_each_possible_cpu(cpu) { |
| 1131 | + struct rq *rq = cpu_rq(cpu); |
| 1132 | + struct rq_flags rf; |
| 1133 | + u64 now; |
| 1134 | + |
| 1135 | + rq_lock_irq(rq, &rf); |
| 1136 | + now = cpu_clock(cpu); |
| 1137 | + psi_group_change(group, cpu, 0, 0, now, true); |
| 1138 | + rq_unlock_irq(rq, &rf); |
| 1139 | + } |
| 1140 | +} |
| 1049 | 1141 | #endif /* CONFIG_CGROUPS */ |
| 1050 | 1142 | |
| 1051 | 1143 | int psi_show(struct seq_file *m, struct psi_group *group, enum psi_res res) |
| 1052 | 1144 | { |
| 1145 | + bool only_full = false; |
| 1053 | 1146 | int full; |
| 1054 | 1147 | u64 now; |
| 1055 | 1148 | |
| @@ -1064,7 +1157,11 @@ int psi_show(struct seq_file *m, struct psi_group *group, enum psi_res res) · 8 unmodified lines |
| 1064 | 1157 | group->avg_next_update = update_averages(group, now); |
| 1065 | 1158 | mutex_unlock(&group->avgs_lock); |
| 1066 | 1159 | |
| 1067 | | − for (full = 0; full < 2; full++) { |
| 1160 | +#ifdef CONFIG_IRQ_TIME_ACCOUNTING |
| 1161 | + only_full = res == PSI_IRQ; |
| 1162 | +#endif |
| 1163 | + |
| 1164 | + for (full = 0; full < 2 - only_full; full++) { |
| 1068 | 1165 | unsigned long avg[3] = { 0, }; |
| 1069 | 1166 | u64 total = 0; |
| 1070 | 1167 | int w; |
| @@ -1078,7 +1175,7 @@ int psi_show(struct seq_file *m, struct psi_group *group, enum psi_res res) · 7 unmodified lines |
| 1078 | 1175 | } |
| 1079 | 1176 | |
| 1080 | 1177 | seq_printf(m, "%s avg10=%lu.%02lu avg60=%lu.%02lu avg300=%lu.%02lu total=%llu\n", |
| 1081 | | − full ? "full" : "some", |
| 1178 | + full || only_full ? "full" : "some", |
| 1082 | 1179 | LOAD_INT(avg[0]), LOAD_FRAC(avg[0]), |
| 1083 | 1180 | LOAD_INT(avg[1]), LOAD_FRAC(avg[1]), |
| 1084 | 1181 | LOAD_INT(avg[2]), LOAD_FRAC(avg[2]), |
| @@ -1106,6 +1203,11 @@ struct psi_trigger *psi_trigger_create(struct psi_group *group, · 21 unmodified lines |
| 1106 | 1203 | else |
| 1107 | 1204 | return ERR_PTR(-EINVAL); |
| 1108 | 1205 | |
| 1206 | +#ifdef CONFIG_IRQ_TIME_ACCOUNTING |
| 1207 | + if (res == PSI_IRQ && --state != PSI_IRQ_FULL) |
| 1208 | + return ERR_PTR(-EINVAL); |
| 1209 | +#endif |
| 1210 | + |
| 1109 | 1211 | if (state >= PSI_NONIDLE) |
| 1110 | 1212 | return ERR_PTR(-EINVAL); |
| 1111 | 1213 | |
| @@ -1390,6 +1492,33 @@ static const struct proc_ops psi_cpu_proc_ops = { · 278 unmodified lines |
| 1390 | 1492 | .proc_release = psi_fop_release, |
| 1391 | 1493 | }; |
| 1392 | 1494 | |
| 1495 | +#ifdef CONFIG_IRQ_TIME_ACCOUNTING |
| 1496 | +static int psi_irq_show(struct seq_file *m, void *v) |
| 1497 | +{ |
| 1498 | + return psi_show(m, &psi_system, PSI_IRQ); |
| 1499 | +} |
| 1500 | + |
| 1501 | +static int psi_irq_open(struct inode *inode, struct file *file) |
| 1502 | +{ |
| 1503 | + return psi_open(file, psi_irq_show); |
| 1504 | +} |
| 1505 | + |
| 1506 | +static ssize_t psi_irq_write(struct file *file, const char __user *user_buf, |
| 1507 | + size_t nbytes, loff_t *ppos) |
| 1508 | +{ |
| 1509 | + return psi_write(file, user_buf, nbytes, PSI_IRQ); |
| 1510 | +} |
| 1511 | + |
| 1512 | +static const struct proc_ops psi_irq_proc_ops = { |
| 1513 | + .proc_open = psi_irq_open, |
| 1514 | + .proc_read = seq_read, |
| 1515 | + .proc_lseek = seq_lseek, |
| 1516 | + .proc_write = psi_irq_write, |
| 1517 | + .proc_poll = psi_fop_poll, |
| 1518 | + .proc_release = psi_fop_release, |
| 1519 | +}; |
| 1520 | +#endif |
| 1521 | + |
| 1393 | 1522 | static int __init psi_proc_init(void) |
| 1394 | 1523 | { |
| 1395 | 1524 | if (psi_enable) { |
| @@ -1397,6 +1526,9 @@ static int __init psi_proc_init(void) · 1 unmodified line |
| 1397 | 1526 | proc_create("pressure/io", 0666, NULL, &psi_io_proc_ops); |
| 1398 | 1527 | proc_create("pressure/memory", 0666, NULL, &psi_memory_proc_ops); |
| 1399 | 1528 | proc_create("pressure/cpu", 0666, NULL, &psi_cpu_proc_ops); |
| 1529 | +#ifdef CONFIG_IRQ_TIME_ACCOUNTING |
| 1530 | + proc_create("pressure/irq", 0666, NULL, &psi_irq_proc_ops); |
| 1531 | +#endif |
| 1400 | 1532 | } |
| 1401 | 1533 | return 0; |
| 1402 | 1534 | } |