├── screenshots ├── 00.jpg ├── 01.jpg └── 02.jpg ├── root ├── etc │ ├── config │ │ └── cpu-perf │ └── init.d │ │ └── cpu-perf └── usr │ └── share │ ├── luci │ └── menu.d │ │ └── luci-app-cpu-perf.json │ └── rpcd │ ├── acl.d │ └── luci-app-cpu-perf.json │ └── ucode │ └── luci.cpu-perf ├── Makefile ├── LICENSE ├── README.md ├── htdocs └── luci-static │ └── resources │ └── view │ ├── status │ └── include │ │ └── 19_cpu-perf.js │ └── cpu-perf.js └── po ├── zh_Hans └── cpu-perf.po ├── templates └── cpu-perf.pot └── ru └── cpu-perf.po /screenshots/00.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gSpotx2f/luci-app-cpu-perf/HEAD/screenshots/00.jpg -------------------------------------------------------------------------------- /screenshots/01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gSpotx2f/luci-app-cpu-perf/HEAD/screenshots/01.jpg -------------------------------------------------------------------------------- /screenshots/02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gSpotx2f/luci-app-cpu-perf/HEAD/screenshots/02.jpg -------------------------------------------------------------------------------- /root/etc/config/cpu-perf: -------------------------------------------------------------------------------- 1 | 2 | config main 'config' 3 | option enabled '1' 4 | 5 | config kernel 'eas' 6 | 7 | config module 'pcie_aspm' 8 | 9 | config governor 'ondemand' 10 | 11 | config governor 'conservative' 12 | -------------------------------------------------------------------------------- /root/usr/share/luci/menu.d/luci-app-cpu-perf.json: -------------------------------------------------------------------------------- 1 | { 2 | "admin/services/cpu-perf": { 3 | "title": "CPU Performance", 4 | "order": 10, 5 | "action": { 6 | "type": "view", 7 | "path": "cpu-perf" 8 | }, 9 | "depends": { 10 | "acl": [ "luci-app-cpu-perf" ], 11 | "uci": { "cpu-perf": true } 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /root/usr/share/rpcd/acl.d/luci-app-cpu-perf.json: -------------------------------------------------------------------------------- 1 | { 2 | "luci-app-cpu-perf": { 3 | "description": "Grant access to cpu-perf procedures", 4 | "read": { 5 | "uci": [ "cpu-perf" ], 6 | "ubus": { 7 | "luci": [ "getInitList", "setInitAction" ], 8 | "luci.cpu-perf": [ "getCpuPerf" ] 9 | } 10 | }, 11 | "write": { 12 | "uci": [ "cpu-perf" ] 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2025 gSpot (https://github.com/gSpotx2f/luci-app-cpu-perf) 3 | # 4 | # This is free software, licensed under the MIT License. 5 | # 6 | 7 | include $(TOPDIR)/rules.mk 8 | 9 | PKG_NAME:=luci-app-cpu-perf 10 | PKG_VERSION:=0.6.1 11 | PKG_RELEASE:=1 12 | LUCI_TITLE:=CPU performance information and management for LuCI 13 | LUCI_DEPENDS:=+ucode +ucode-mod-fs 14 | LUCI_PKGARCH:=all 15 | PKG_LICENSE:=MIT 16 | 17 | #include ../../luci.mk 18 | include $(TOPDIR)/feeds/luci/luci.mk 19 | 20 | # call BuildPackage - OpenWrt buildroot signature 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 gSpotx2f 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # luci-app-cpu-perf 2 | CPU performance management for LuCI (OpenWrt webUI). 3 | 4 | OpenWrt >= 22.03. 5 | 6 | **Dependences:** ucode, ucode-mod-fs. 7 | 8 | ## Installation notes 9 | 10 | wget --no-check-certificate -O /tmp/luci-app-cpu-perf_0.6.1-r1_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/luci-app-cpu-perf_0.6.1-r1_all.ipk 11 | opkg install /tmp/luci-app-cpu-perf_0.6.1-r1_all.ipk 12 | rm /tmp/luci-app-cpu-perf_0.6.1-r1_all.ipk 13 | service rpcd restart 14 | service cpu-perf start 15 | 16 | i18n-ru: 17 | 18 | wget --no-check-certificate -O /tmp/luci-i18n-cpu-perf-ru_0.6.1-r1_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/luci-i18n-cpu-perf-ru_0.6.1-r1_all.ipk 19 | opkg install /tmp/luci-i18n-cpu-perf-ru_0.6.1-r1_all.ipk 20 | rm /tmp/luci-i18n-cpu-perf-ru_0.6.1-r1_all.ipk 21 | 22 | ## Screenshots: 23 | 24 | ![](https://github.com/gSpotx2f/luci-app-cpu-perf/blob/master/screenshots/00.jpg) 25 | ![](https://github.com/gSpotx2f/luci-app-cpu-perf/blob/master/screenshots/01.jpg) 26 | ![](https://github.com/gSpotx2f/luci-app-cpu-perf/blob/master/screenshots/02.jpg) 27 | 28 | ## Related LuCI applications: 29 | 30 | CPU load: [https://github.com/gSpotx2f/luci-app-cpu-status](https://github.com/gSpotx2f/luci-app-cpu-status), [https://github.com/gSpotx2f/luci-app-cpu-status-mini](https://github.com/gSpotx2f/luci-app-cpu-status-mini). 31 | Temperature: [https://github.com/gSpotx2f/luci-app-temp-status](https://github.com/gSpotx2f/luci-app-temp-status). 32 | -------------------------------------------------------------------------------- /htdocs/luci-static/resources/view/status/include/19_cpu-perf.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 'require baseclass'; 3 | 'require rpc'; 4 | 5 | document.head.append(E('style', {'type': 'text/css'}, 6 | ` 7 | .cpu-perf-area { 8 | margin-bottom: 1em; 9 | } 10 | .cpu-perf-label { 11 | display: inline-block; 12 | word-wrap: break-word; 13 | padding: 4px 8px; 14 | width: 8em; 15 | } 16 | .cpu-perf-label-title { 17 | display: block; 18 | word-wrap: break-word; 19 | width: 100%; 20 | font-weight: bold; 21 | } 22 | .cpu-perf-label-value { 23 | display: block; 24 | word-wrap: break-word; 25 | width: 100%; 26 | } 27 | .cpu-perf-empty-area { 28 | width: 100%; 29 | text-align: center; 30 | } 31 | 32 | `)) 33 | 34 | return baseclass.extend({ 35 | title : _('CPU Frequency'), 36 | 37 | callCpuPerf: rpc.declare({ 38 | object: 'luci.cpu-perf', 39 | method: 'getCpuPerf', 40 | expect: { '': {} } 41 | }), 42 | 43 | freqFormat(freq) { 44 | if(!freq) { 45 | return '-'; 46 | }; 47 | return (freq >= 1e6) ? 48 | Number((freq / 1e6).toFixed(3)) + ' ' + _('GHz') 49 | : 50 | Number((freq / 1e3).toFixed(3)) + ' ' + _('MHz'); 51 | }, 52 | 53 | load() { 54 | return L.resolveDefault(this.callCpuPerf(), null); 55 | }, 56 | 57 | render(data) { 58 | if(!data) { 59 | return; 60 | }; 61 | 62 | let cpuFreqArea = E('div', { 'class': 'cpu-perf-area' }); 63 | 64 | if(data.cpus && data.freqPolicies) { 65 | for(let i of Object.values(data.cpus)) { 66 | let policy = data.freqPolicies[String(i.policy)]; 67 | if(policy) { 68 | cpuFreqArea.append( 69 | E('div', { 'class': 'cpu-perf-label' }, [ 70 | E('span', { 'class': 'cpu-perf-label-title' }, 71 | _('CPU') + ' ' + i.number + ':' 72 | ), 73 | E('span', { 'class': 'cpu-perf-label-value' }, 74 | (policy.sCurFreq) ? 75 | this.freqFormat(policy.sCurFreq) 76 | : 77 | this.freqFormat(policy.curFreq) 78 | ), 79 | ]) 80 | ); 81 | }; 82 | }; 83 | }; 84 | 85 | if(cpuFreqArea.childNodes.length == 0){ 86 | cpuFreqArea.append( 87 | E('div', { 'class': 'cpu-perf-empty-area' }, 88 | E('em', {}, _('No CPU frequency data available...')) 89 | ) 90 | ); 91 | }; 92 | 93 | return cpuFreqArea; 94 | }, 95 | }); 96 | -------------------------------------------------------------------------------- /po/zh_Hans/cpu-perf.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "Content-Type: text/plain; charset=UTF-8" 3 | 4 | msgid "An error has occurred" 5 | msgstr "发生错误" 6 | 7 | msgid "CPU" 8 | msgstr "CPU" 9 | 10 | msgid "CPU Performance" 11 | msgstr "CPU 性能" 12 | 13 | msgid "CPU performance management." 14 | msgstr "CPU性能管理" 15 | 16 | msgid "CPU performance scaling not available..." 17 | msgstr "CPU 性能扩展不可用.." 18 | 19 | msgid "Command failed" 20 | msgstr "命令失败" 21 | 22 | msgid "Conservative governor" 23 | msgstr "Conservative调控器" 24 | 25 | msgid "Conservative tunables" 26 | msgstr "保守的可调参数" 27 | 28 | msgid "Current frequency" 29 | msgstr "当前频率" 30 | 31 | msgid "Disabled" 32 | msgstr "已禁用" 33 | 34 | msgid "Enable" 35 | msgstr "启用" 36 | 37 | msgid "Enabled" 38 | msgstr "已启用" 39 | 40 | msgid "Failed to get %s init status: %s" 41 | msgstr "获取初始状态失败%s:%s" 42 | 43 | msgid "Frequency decrease deferral factor." 44 | msgstr "频率降低延迟因数。" 45 | 46 | msgid "Frequency step in percent of the maximum frequency the governor is allowed to set." 47 | msgstr "频率步进,单位为调速器允许设置的最大频率的百分比。" 48 | 49 | msgid "Frequency value must not be" 50 | msgstr "频率值不得超过" 51 | 52 | msgid "GHz" 53 | msgstr "GHz" 54 | 55 | msgid "If checked, it will cause the CPU load estimation code to treat the CPU time spent on executing tasks with \"nice\" levels greater than 0 as CPU idle time." 56 | msgstr "如果启用,CPU 使用率估计代码会将执行高于 0 的“nice”级别的任务所花费的 CPU 时间视为 CPU 空闲时间。" 57 | 58 | msgid "If the estimated CPU load is above this value (in percent), the governor will set the frequency to the maximum value." 59 | msgstr "如果估计的 CPU 负载高于此值(百分比),调节器将把频率设置为最大值。" 60 | 61 | msgid "KHz" 62 | msgstr "KHz" 63 | 64 | msgid "MHz" 65 | msgstr "MHz" 66 | 67 | msgid "Maximum frequency" 68 | msgstr "最高频率" 69 | 70 | msgid "Maximum frequency the CPU is allowed to be running." 71 | msgstr "允许 CPU 运行的最高频率。" 72 | 73 | msgid "Maximum scaling frequency" 74 | msgstr "最大扩展频率" 75 | 76 | msgid "Minimum frequency" 77 | msgstr "最低频率" 78 | 79 | msgid "Minimum frequency the CPU is allowed to be running." 80 | msgstr "允许CPU运行的最低频率。" 81 | 82 | msgid "Minimum scaling frequency" 83 | msgstr "最小扩展频率" 84 | 85 | msgid "No performance data..." 86 | msgstr "没有性能数据..." 87 | 88 | msgid "Ondemand governor" 89 | msgstr "按需调节器" 90 | 91 | msgid "Ondemand tunables" 92 | msgstr "按需调节参数" 93 | 94 | msgid "Performance managment" 95 | msgstr "性能管理" 96 | 97 | msgid "Performance scaling not available for this CPU..." 98 | msgstr "性能缩放不适用于该CPU..." 99 | 100 | msgid "Run at startup" 101 | msgstr "在系统启动时运行" 102 | 103 | msgid "Scaling governor" 104 | msgstr "缩放控制" 105 | 106 | msgid "Scaling governors implement algorithms to estimate the required CPU capacity." 107 | msgstr "缩放调节器实现算法来估计所需的 CPU 容量。" 108 | 109 | msgid "Service action failed \"%s %s\": %s" 110 | msgstr "服务操作失败 \"%s %s\": %s" 111 | 112 | msgid "Threshold value (in percent) used to determine the frequency change direction." 113 | msgstr "用于确定频率变化方向的阈值(以百分比表示)。" 114 | 115 | msgid "default value:" 116 | msgstr "默认值:" 117 | 118 | msgid "higher" 119 | msgstr "更高" 120 | 121 | msgid "lower" 122 | msgstr "降低" 123 | 124 | msgid "than the" 125 | msgstr "比" 126 | -------------------------------------------------------------------------------- /po/templates/cpu-perf.pot: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "Content-Type: text/plain; charset=UTF-8" 3 | 4 | msgid "Active-State Power Management (ASPM) policy for PCIe links, which saves power by putting unused PCIe links into a low-power state." 5 | msgstr "" 6 | 7 | msgid "An error has occurred" 8 | msgstr "" 9 | 10 | msgid "CPU" 11 | msgstr "" 12 | 13 | msgid "CPU Frequency" 14 | msgstr "" 15 | 16 | msgid "CPU Performance" 17 | msgstr "" 18 | 19 | msgid "CPU performance management." 20 | msgstr "" 21 | 22 | msgid "CPU performance scaling is not available..." 23 | msgstr "" 24 | 25 | msgid "Command failed" 26 | msgstr "" 27 | 28 | msgid "Conservative governor" 29 | msgstr "" 30 | 31 | msgid "Current frequency" 32 | msgstr "" 33 | 34 | msgid "Current values" 35 | msgstr "" 36 | 37 | msgid "Disabled" 38 | msgstr "" 39 | 40 | msgid "Enable" 41 | msgstr "" 42 | 43 | msgid "Enabled" 44 | msgstr "" 45 | 46 | msgid "Enable/disable Energy Aware Scheduling (EAS)." 47 | msgstr "" 48 | 49 | msgid "Failed to get %s init status: %s" 50 | msgstr "" 51 | 52 | msgid "Frequency decrease deferral factor." 53 | msgstr "" 54 | 55 | msgid "Frequency step in percent of the maximum frequency the governor is allowed to set." 56 | msgstr "" 57 | 58 | msgid "Frequency value must not be" 59 | msgstr "" 60 | 61 | msgid "GHz" 62 | msgstr "" 63 | 64 | msgid "If checked, it will cause the CPU load estimation code to treat the CPU time spent on executing tasks with \"nice\" levels greater than 0 as CPU idle time." 65 | msgstr "" 66 | 67 | msgid "If the estimated CPU load is above this value (in percent), the governor will set the frequency to the maximum value." 68 | msgstr "" 69 | 70 | msgid "KHz" 71 | msgstr "" 72 | 73 | msgid "MHz" 74 | msgstr "" 75 | 76 | msgid "Maximum frequency" 77 | msgstr "" 78 | 79 | msgid "Maximum frequency the CPU is allowed to be running." 80 | msgstr "" 81 | 82 | msgid "Maximum scaling frequency" 83 | msgstr "" 84 | 85 | msgid "Minimum frequency" 86 | msgstr "" 87 | 88 | msgid "Minimum frequency the CPU is allowed to be running." 89 | msgstr "" 90 | 91 | msgid "Minimum scaling frequency" 92 | msgstr "" 93 | 94 | msgid "No CPU frequency data available..." 95 | msgstr "" 96 | 97 | msgid "No performance data..." 98 | msgstr "" 99 | 100 | msgid "Ondemand governor" 101 | msgstr "" 102 | 103 | msgid "Performance managment" 104 | msgstr "" 105 | 106 | msgid "Performance scaling is not available for this policy..." 107 | msgstr "" 108 | 109 | msgid "Policy" 110 | msgstr "" 111 | 112 | msgid "Reduction factor to apply to the original frequency target of the governor." 113 | msgstr "" 114 | 115 | msgid "Run at startup" 116 | msgstr "" 117 | 118 | msgid "Scaling governor" 119 | msgstr "" 120 | 121 | msgid "Scaling governors implement algorithms to estimate the required CPU capacity." 122 | msgstr "" 123 | 124 | msgid "Service action failed \"%s %s\": %s" 125 | msgstr "" 126 | 127 | msgid "Threshold value (in percent) used to determine the frequency change direction." 128 | msgstr "" 129 | 130 | msgid "default value:" 131 | msgstr "" 132 | 133 | msgid "higher" 134 | msgstr "" 135 | 136 | msgid "lower" 137 | msgstr "" 138 | 139 | msgid "than the" 140 | msgstr "" 141 | -------------------------------------------------------------------------------- /po/ru/cpu-perf.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Content-Type: text/plain; charset=UTF-8\n" 4 | "Project-Id-Version: \n" 5 | "POT-Creation-Date: \n" 6 | "PO-Revision-Date: \n" 7 | "Language-Team: \n" 8 | "MIME-Version: 1.0\n" 9 | "Content-Transfer-Encoding: 8bit\n" 10 | "X-Generator: Poedit 3.0\n" 11 | "Last-Translator: \n" 12 | "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n" 13 | "Language: ru\n" 14 | 15 | msgid "Active-State Power Management (ASPM) policy for PCIe links, which saves power by putting unused PCIe links into a low-power state." 16 | msgstr "Политика управления питанием в активном состоянии (ASPM) для каналов PCIe, которая экономит электроэнергию, переводя неиспользуемые каналы PCIe в состояние пониженного энергопотребления." 17 | 18 | msgid "An error has occurred" 19 | msgstr "Произошла ошибка" 20 | 21 | msgid "CPU" 22 | msgstr "ЦПУ" 23 | 24 | msgid "CPU Frequency" 25 | msgstr "Частота ЦПУ" 26 | 27 | msgid "CPU Performance" 28 | msgstr "Производительность ЦПУ" 29 | 30 | msgid "CPU performance management." 31 | msgstr "Управление производительностью процессора." 32 | 33 | msgid "CPU performance scaling is not available..." 34 | msgstr "Масштабирование производительности ЦПУ недоступно..." 35 | 36 | msgid "Command failed" 37 | msgstr "Команда не выполнена" 38 | 39 | msgid "Conservative governor" 40 | msgstr "Регулятор conservative" 41 | 42 | msgid "Current frequency" 43 | msgstr "Текущая частота" 44 | 45 | msgid "Current values" 46 | msgstr "Текущие значения" 47 | 48 | msgid "Disabled" 49 | msgstr "Отключено" 50 | 51 | msgid "Enable" 52 | msgstr "Включить" 53 | 54 | msgid "Enabled" 55 | msgstr "Включено" 56 | 57 | msgid "Enable/disable Energy Aware Scheduling (EAS)." 58 | msgstr "Включить/отключить Energy Aware Scheduling (EAS)." 59 | 60 | msgid "Failed to get %s init status: %s" 61 | msgstr "Не удалось получить статус инициализации %s: %s" 62 | 63 | msgid "Frequency decrease deferral factor." 64 | msgstr "Коэффициент отсрочки уменьшения частоты." 65 | 66 | msgid "Frequency step in percent of the maximum frequency the governor is allowed to set." 67 | msgstr "Шаг частоты в процентах от максимальной частоты, которую может установить регулятор." 68 | 69 | msgid "Frequency value must not be" 70 | msgstr "Значение частоты не должно быть" 71 | 72 | msgid "GHz" 73 | msgstr "ГГц" 74 | 75 | msgid "If checked, it will cause the CPU load estimation code to treat the CPU time spent on executing tasks with \"nice\" levels greater than 0 as CPU idle time." 76 | msgstr "Если включено, код оценки загрузки ЦПУ будет рассматривать время ЦПУ, затрачиваемое на выполнение задач с уровнями \"nice\" выше 0, как время простоя ЦПУ." 77 | 78 | msgid "If the estimated CPU load is above this value (in percent), the governor will set the frequency to the maximum value." 79 | msgstr "Если расчетная загрузка ЦПУ выше этого значения (в процентах), регулятор установит максимальное значение частоты." 80 | 81 | msgid "KHz" 82 | msgstr "КГц" 83 | 84 | msgid "MHz" 85 | msgstr "МГц" 86 | 87 | msgid "Maximum frequency" 88 | msgstr "Максимальная частота" 89 | 90 | msgid "Maximum frequency the CPU is allowed to be running." 91 | msgstr "Максимальная частота, на которой может работать ЦПУ." 92 | 93 | msgid "Maximum scaling frequency" 94 | msgstr "Максимальное масштабирование частоты" 95 | 96 | msgid "Minimum frequency" 97 | msgstr "Минимальная частота" 98 | 99 | msgid "Minimum frequency the CPU is allowed to be running." 100 | msgstr "Минимальная частота, на которой может работать ЦПУ." 101 | 102 | msgid "Minimum scaling frequency" 103 | msgstr "Минимальное масштабирование частоты" 104 | 105 | msgid "No CPU frequency data available..." 106 | msgstr "Нет данных о данных о частоте процессора..." 107 | 108 | msgid "No performance data..." 109 | msgstr "Нет данных о производительности..." 110 | 111 | msgid "Ondemand governor" 112 | msgstr "Регулятор ondemand" 113 | 114 | msgid "Performance managment" 115 | msgstr "Управление производительностью" 116 | 117 | msgid "Performance scaling is not available for this policy..." 118 | msgstr "Масштабирование производительности недоступно для этой политики..." 119 | 120 | msgid "Policy" 121 | msgstr "Политика" 122 | 123 | msgid "Reduction factor to apply to the original frequency target of the governor." 124 | msgstr "Коэффициент снижения, применяемый к исходной целевой частоте регулятора." 125 | 126 | msgid "Run at startup" 127 | msgstr "Запуск при старте системы" 128 | 129 | msgid "Scaling governor" 130 | msgstr "Регулятор масштабирования" 131 | 132 | msgid "Scaling governors implement algorithms to estimate the required CPU capacity." 133 | msgstr "Регуляторы масштабирования реализуют алгоритмы для оценки требуемой мощности ЦПУ." 134 | 135 | msgid "Service action failed \"%s %s\": %s" 136 | msgstr "Не удалось выполнить действие службы \"%s %s\": %s" 137 | 138 | msgid "Threshold value (in percent) used to determine the frequency change direction." 139 | msgstr "Пороговое значение (в процентах), используемое для определения направления изменения частоты." 140 | 141 | msgid "default value:" 142 | msgstr "стандартное значение:" 143 | 144 | msgid "higher" 145 | msgstr "выше" 146 | 147 | msgid "lower" 148 | msgstr "ниже" 149 | 150 | msgid "than the" 151 | msgstr "чем" 152 | -------------------------------------------------------------------------------- /root/usr/share/rpcd/ucode/luci.cpu-perf: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | import { basename, lsdir, readfile, readlink, stat } from 'fs'; 5 | 6 | const sysCpuDir = '/sys/devices/system/cpu'; 7 | const sysFreqPolicyDir = sysCpuDir + '/cpufreq'; 8 | const sysOndemandDir = sysFreqPolicyDir + '/ondemand'; 9 | const sysConservativeDir = sysFreqPolicyDir + '/conservative'; 10 | const sysPcieAspmPolicyFile = '/sys/module/pcie_aspm/parameters/policy'; 11 | const sysEasFile = '/proc/sys/kernel/sched_energy_aware'; 12 | 13 | function readFile(path) { 14 | let r = readfile(path); 15 | return r && trim(r); 16 | } 17 | 18 | function parseMultiValue(s, number) { 19 | if(!s) { 20 | return null; 21 | } 22 | let t = []; 23 | s = replace(s, /[\]\[]/g, ''); 24 | for(let i in split(s, /[[:space:]]+/)) { 25 | push(t, number ? int(i) : i); 26 | } 27 | return t; 28 | } 29 | 30 | const methods = { 31 | getCpuPerf: { 32 | call: function() { 33 | let cpuPerf = {}; 34 | let eas = {}; 35 | let pcieAspm = {}; 36 | let ondemand = {}; 37 | let conservative = {}; 38 | let freqPolicies = {}; 39 | let cpus = {}; 40 | if(stat(sysEasFile)?.type == 'file') { 41 | let schedEnergyAware = readFile(sysEasFile); 42 | if(schedEnergyAware) { 43 | eas['schedEnergyAware'] = int(schedEnergyAware); 44 | } 45 | cpuPerf['eas'] = eas; 46 | } 47 | if(stat(sysPcieAspmPolicyFile)?.type == 'file') { 48 | let availPolicies = readFile(sysPcieAspmPolicyFile); 49 | if(availPolicies) { 50 | pcieAspm['availPolicies'] = parseMultiValue(availPolicies); 51 | } 52 | let currentPolicy = match(availPolicies, /\[[a-z]+\]/); 53 | pcieAspm['currentPolicy'] = type(currentPolicy) == 'array' ? trim(currentPolicy[0], '[]') : ''; 54 | cpuPerf['pcieAspm'] = pcieAspm; 55 | } 56 | if(stat(sysOndemandDir)?.type == 'directory') { 57 | let upThreshold = readFile(sysOndemandDir + '/up_threshold'); 58 | let ignNiceLoad = readFile(sysOndemandDir + '/ignore_nice_load'); 59 | let smpDownFactor = readFile(sysOndemandDir + '/sampling_down_factor'); 60 | let powersaveBias = readFile(sysOndemandDir + '/powersave_bias'); 61 | if(upThreshold) { 62 | ondemand['upThreshold'] = int(upThreshold); 63 | } 64 | if(ignNiceLoad) { 65 | ondemand['ignNiceLoad'] = int(ignNiceLoad); 66 | } 67 | if(smpDownFactor) { 68 | ondemand['smpDownFactor'] = int(smpDownFactor); 69 | } 70 | if(powersaveBias) { 71 | ondemand['powersaveBias'] = int(powersaveBias); 72 | } 73 | cpuPerf['ondemand'] = ondemand; 74 | } 75 | if(stat(sysConservativeDir)?.type == 'directory') { 76 | let freqStep = readFile(sysConservativeDir + '/freq_step'); 77 | let downThreshold = readFile(sysConservativeDir + '/down_threshold'); 78 | let smpDownFactor = readFile(sysConservativeDir + '/sampling_down_factor'); 79 | if(freqStep) { 80 | conservative['freqStep'] = int(freqStep); 81 | } 82 | if(downThreshold) { 83 | conservative['downThreshold'] = int(downThreshold); 84 | } 85 | if(smpDownFactor) { 86 | conservative['smpDownFactor'] = int(smpDownFactor); 87 | } 88 | cpuPerf['conservative'] = conservative; 89 | } 90 | if(stat(sysFreqPolicyDir)?.type == 'directory') { 91 | for(let item in lsdir(sysFreqPolicyDir)) { 92 | if(match(item, /^policy[0-9]+$/)) { 93 | let policyDirPath = sprintf('%s/%s', sysFreqPolicyDir, item); 94 | if(stat(policyDirPath)?.type == 'directory') { 95 | let m = match(item, /[0-9]+/); 96 | let dNumber = m && m[0]; 97 | if(dNumber) { 98 | let sCurFreq = readFile(policyDirPath + '/scaling_cur_freq'); 99 | let curFreq = readFile(policyDirPath + '/cpuinfo_cur_freq'); 100 | let sMinFreq = readFile(policyDirPath + '/scaling_min_freq'); 101 | let minFreq = readFile(policyDirPath + '/cpuinfo_min_freq'); 102 | let sMaxFreq = readFile(policyDirPath + '/scaling_max_freq'); 103 | let maxFreq = readFile(policyDirPath + '/cpuinfo_max_freq'); 104 | let governor = readFile(policyDirPath + '/scaling_governor'); 105 | let sAvailFreqs = readFile(policyDirPath + '/scaling_available_frequencies'); 106 | if(sAvailFreqs) { 107 | sAvailFreqs = parseMultiValue(sAvailFreqs, true); 108 | } 109 | let sAvailGovernors = readFile(policyDirPath + '/scaling_available_governors'); 110 | if(sAvailGovernors) { 111 | sAvailGovernors = parseMultiValue(sAvailGovernors); 112 | } 113 | let d = { number: int(dNumber) }; 114 | if(sCurFreq) { 115 | d['sCurFreq'] = int(sCurFreq); 116 | } 117 | if(curFreq) { 118 | d['curFreq'] = int(curFreq); 119 | } 120 | if(sMinFreq) { 121 | d['sMinFreq'] = int(sMinFreq); 122 | } 123 | if(minFreq) { 124 | d['minFreq'] = int(minFreq); 125 | } 126 | if(sMaxFreq) { 127 | d['sMaxFreq'] = int(sMaxFreq); 128 | } 129 | if(maxFreq) { 130 | d['maxFreq'] = int(maxFreq); 131 | } 132 | if(governor) { 133 | d['governor'] = governor; 134 | } 135 | if(sAvailFreqs) { 136 | d['sAvailFreqs'] = sAvailFreqs; 137 | } 138 | if(sAvailGovernors) { 139 | d['sAvailGovernors'] = sAvailGovernors; 140 | } 141 | d['cpu'] = []; 142 | freqPolicies[dNumber] = d; 143 | } 144 | } 145 | } 146 | } 147 | cpuPerf['freqPolicies'] = freqPolicies; 148 | } 149 | if(stat(sysCpuDir)?.type == 'directory') { 150 | for(let item in lsdir(sysCpuDir)) { 151 | if(match(item, /^cpu[0-9]+$/)) { 152 | let deviceDirPath = sprintf('%s/%s/cpufreq', sysCpuDir, item); 153 | if(stat(deviceDirPath)?.type == 'directory') { 154 | let m = match(item, /[0-9]+/); 155 | let dNumber = m && m[0]; 156 | if(dNumber) { 157 | let cpuFreqPath = readlink(deviceDirPath); 158 | if(cpuFreqPath) { 159 | let policy = basename(cpuFreqPath); 160 | let m = match(policy, /[0-9]+/); 161 | policy = m && m[0]; 162 | if(policy) { 163 | push(freqPolicies[policy].cpu, int(dNumber)); 164 | cpus[dNumber] = { 165 | number: int(dNumber), 166 | policy: int(policy), 167 | }; 168 | } 169 | } 170 | } 171 | } 172 | } 173 | } 174 | cpuPerf['cpus'] = cpus; 175 | } 176 | return cpuPerf; 177 | }, 178 | }, 179 | }; 180 | 181 | return { 'luci.cpu-perf': methods }; 182 | -------------------------------------------------------------------------------- /root/etc/init.d/cpu-perf: -------------------------------------------------------------------------------- 1 | #!/bin/sh /etc/rc.common 2 | 3 | START=13 4 | 5 | APP_NAME="cpu-perf" 6 | SYSFS_CPU_DIR="/sys/devices/system/cpu" 7 | SYSFS_CPU_FREQ_DIR="${SYSFS_CPU_DIR}/cpufreq" 8 | 9 | OD_CPU_FREQ_DIR="${SYSFS_CPU_DIR}/cpufreq/ondemand" 10 | OD_UP_THRESHOLD_FILE="${OD_CPU_FREQ_DIR}/up_threshold" 11 | OD_IGN_NICE_LOAD_FILE="${OD_CPU_FREQ_DIR}/ignore_nice_load" 12 | OD_SAMPLING_DOWN_FACTOR_FILE="${OD_CPU_FREQ_DIR}/sampling_down_factor" 13 | OD_POWERSAVE_BIAS_FILE="${OD_CPU_FREQ_DIR}/powersave_bias" 14 | OD_UP_THRESHOLD_DEF=95 15 | OD_IGN_NICE_LOAD_DEF=0 16 | OD_SAMPLING_DOWN_FACTOR_DEF=1 17 | OD_POWERSAVE_BIAS_DEF=0 18 | 19 | CO_CPU_FREQ_DIR="${SYSFS_CPU_DIR}/cpufreq/conservative" 20 | CO_FREQ_STEP_FILE="${CO_CPU_FREQ_DIR}/freq_step" 21 | CO_DOWN_THRESHOLD_FILE="${CO_CPU_FREQ_DIR}/down_threshold" 22 | CO_SAMPLING_DOWN_FACTOR_FILE="${CO_CPU_FREQ_DIR}/sampling_down_factor" 23 | CO_FREQ_STEP_DEF=5 24 | CO_DOWN_THRESHOLD_DEF=20 25 | CO_SAMPLING_DOWN_FACTOR_DEF=1 26 | 27 | PA_POLICY_FILE="/sys/module/pcie_aspm/parameters/policy" 28 | PA_POLICY_DEF="default" 29 | 30 | EAS_FILE="/proc/sys/kernel/sched_energy_aware" 31 | EAS_DEF=1 32 | 33 | 34 | config_load $APP_NAME 35 | 36 | get_cpu_freq_policies() { 37 | echo $(ls "$SYSFS_CPU_FREQ_DIR" | grep "^policy[0-9]*$") 38 | 39 | } 40 | 41 | reset_cpu_freq_policy_config() { 42 | while uci -q delete ${APP_NAME}.@cpu_freq_policy[0]; do :; done 43 | for i in $(get_cpu_freq_policies) 44 | do 45 | uci set ${APP_NAME}.${i}="cpu_freq_policy" 46 | done 47 | uci commit "$APP_NAME" 48 | } 49 | 50 | CPU_FREQ_POLICY_UCI_SECTIONS=0 51 | CPU_FREQ_POLICIES=$(get_cpu_freq_policies | wc -w) 52 | 53 | cpu_freq_policy_section_counter() { 54 | CPU_FREQ_POLICY_UCI_SECTIONS=$(($CPU_FREQ_POLICY_UCI_SECTIONS + 1)) 55 | } 56 | 57 | check_cpu_freq_policy_sections() { 58 | CPU_FREQ_POLICY_UCI_SECTIONS=0 59 | config_foreach cpu_freq_policy_section_counter cpu_freq_policy 60 | if [ $CPU_FREQ_POLICY_UCI_SECTIONS -ne $CPU_FREQ_POLICIES ]; then 61 | reset_cpu_freq_policy_config 62 | fi 63 | } 64 | 65 | set_cpu_freq_policy() { 66 | local policy="$1" 67 | local scaling_min_freq scaling_max_freq scaling_governor 68 | local policy_dir="${SYSFS_CPU_FREQ_DIR}/${policy}" 69 | 70 | [ ! -d $policy_dir ] && return 71 | 72 | local scaling_min_freq_file="${policy_dir}/scaling_min_freq" 73 | local scaling_max_freq_file="${policy_dir}/scaling_max_freq" 74 | local scaling_governor_file="${policy_dir}/scaling_governor" 75 | 76 | config_get scaling_min_freq $policy scaling_min_freq 77 | config_get scaling_max_freq $policy scaling_max_freq 78 | config_get scaling_governor $policy scaling_governor 79 | 80 | if [ -n "$scaling_min_freq" -a -w "$scaling_min_freq_file" ]; then 81 | echo "$scaling_min_freq" > "$scaling_min_freq_file" 82 | fi 83 | if [ -n "$scaling_max_freq" -a -w "$scaling_max_freq_file" ]; then 84 | echo "$scaling_max_freq" > "$scaling_max_freq_file" 85 | fi 86 | if [ -n "$scaling_governor" -a -w "$scaling_governor_file" ]; then 87 | echo "$scaling_governor" > "$scaling_governor_file" 88 | fi 89 | } 90 | 91 | reset_cpu_freq_policy() { 92 | local policy="$1" 93 | local policy_dir="${SYSFS_CPU_FREQ_DIR}/${policy}" 94 | 95 | [ ! -d $policy_dir ] && return 96 | 97 | local min_freq_file="${policy_dir}/cpuinfo_min_freq" 98 | local max_freq_file="${policy_dir}/cpuinfo_max_freq" 99 | local scaling_min_freq_file="${policy_dir}/scaling_min_freq" 100 | local scaling_max_freq_file="${policy_dir}/scaling_max_freq" 101 | local scaling_governor_file="${policy_dir}/scaling_governor" 102 | 103 | if [ -r "$min_freq_file" -a -w "$scaling_min_freq_file" ]; then 104 | echo "$(cat $min_freq_file)" > "$scaling_min_freq_file" 105 | fi 106 | if [ -r "$max_freq_file" -a -w "$scaling_max_freq_file" ]; then 107 | echo "$(cat $max_freq_file)" > "$scaling_max_freq_file" 108 | fi 109 | if [ -w "$scaling_governor_file" ]; then 110 | echo "schedutil" > "$scaling_governor_file" 2> /dev/null 111 | if [ $? -ne 0 ]; then 112 | echo "ondemand" > "$scaling_governor_file" 2> /dev/null 113 | if [ $? -ne 0 ]; then 114 | echo "conservative" > "$scaling_governor_file" 2> /dev/null 115 | if [ $? -ne 0 ]; then 116 | echo "userspace" > "$scaling_governor_file" 2> /dev/null 117 | fi 118 | fi 119 | fi 120 | fi 121 | } 122 | 123 | set_ondemand_tunables() { 124 | local up_threshold ignore_nice_load sampling_down_factor powersave_bias 125 | 126 | [ ! -d "$OD_CPU_FREQ_DIR" ] && return 0 127 | 128 | config_get up_threshold ondemand up_threshold 129 | config_get ignore_nice_load ondemand ignore_nice_load 130 | config_get sampling_down_factor ondemand sampling_down_factor 131 | config_get powersave_bias ondemand powersave_bias 132 | 133 | if [ -n "$up_threshold" -a -w "$OD_UP_THRESHOLD_FILE" ]; then 134 | echo "$up_threshold" > "$OD_UP_THRESHOLD_FILE" 135 | fi 136 | if [ -n "$ignore_nice_load" -a -w "$OD_IGN_NICE_LOAD_FILE" ]; then 137 | echo "$ignore_nice_load" > "$OD_IGN_NICE_LOAD_FILE" 138 | fi 139 | if [ -n "$sampling_down_factor" -a -w "$OD_SAMPLING_DOWN_FACTOR_FILE" ]; then 140 | echo "$sampling_down_factor" > "$OD_SAMPLING_DOWN_FACTOR_FILE" 141 | fi 142 | if [ -n "$powersave_bias" -a -w "$OD_POWERSAVE_BIAS_FILE" ]; then 143 | echo "$powersave_bias" > "$OD_POWERSAVE_BIAS_FILE" 144 | fi 145 | } 146 | 147 | reset_ondemand_tunables() { 148 | [ ! -d "$OD_CPU_FREQ_DIR" ] && return 0 149 | 150 | if [ -w "$OD_UP_THRESHOLD_FILE" ]; then 151 | echo "$OD_UP_THRESHOLD_DEF" > "$OD_UP_THRESHOLD_FILE" 152 | fi 153 | if [ -w "$OD_IGN_NICE_LOAD_FILE" ]; then 154 | echo "$OD_IGN_NICE_LOAD_DEF" > "$OD_IGN_NICE_LOAD_FILE" 155 | fi 156 | if [ -w "$OD_SAMPLING_DOWN_FACTOR_FILE" ]; then 157 | echo "$OD_SAMPLING_DOWN_FACTOR_DEF" > "$OD_SAMPLING_DOWN_FACTOR_FILE" 158 | fi 159 | if [ -w "$OD_POWERSAVE_BIAS_FILE" ]; then 160 | echo "$OD_POWERSAVE_BIAS_DEF" > "$OD_POWERSAVE_BIAS_FILE" 161 | fi 162 | } 163 | 164 | set_conservative_tunables() { 165 | local freq_step down_threshold sampling_down_factor 166 | 167 | [ ! -d "$CO_CPU_FREQ_DIR" ] && return 0 168 | 169 | config_get freq_step conservative freq_step 170 | config_get down_threshold conservative down_threshold 171 | config_get sampling_down_factor conservative sampling_down_factor 172 | 173 | if [ -n "$freq_step" -a -w "$CO_FREQ_STEP_FILE" ]; then 174 | echo "$freq_step" > "$CO_FREQ_STEP_FILE" 175 | fi 176 | if [ -n "$down_threshold" -a -w "$CO_DOWN_THRESHOLD_FILE" ]; then 177 | echo "$down_threshold" > "$CO_DOWN_THRESHOLD_FILE" 178 | fi 179 | if [ -n "$sampling_down_factor" -a -w "$CO_SAMPLING_DOWN_FACTOR_FILE" ]; then 180 | echo "$sampling_down_factor" > "$CO_SAMPLING_DOWN_FACTOR_FILE" 181 | fi 182 | } 183 | 184 | reset_conservative_tunables() { 185 | [ ! -d "$CO_CPU_FREQ_DIR" ] && return 0 186 | 187 | if [ -w "$CO_FREQ_STEP_FILE" ]; then 188 | echo "$CO_FREQ_STEP_DEF" > "$CO_FREQ_STEP_FILE" 189 | fi 190 | if [ -w "$CO_DOWN_THRESHOLD_FILE" ]; then 191 | echo "$CO_DOWN_THRESHOLD_DEF" > "$CO_DOWN_THRESHOLD_FILE" 192 | fi 193 | if [ -w "$CO_SAMPLING_DOWN_FACTOR_FILE" ]; then 194 | echo "$CO_SAMPLING_DOWN_FACTOR_DEF" > "$CO_SAMPLING_DOWN_FACTOR_FILE" 195 | fi 196 | } 197 | 198 | set_eas() { 199 | config_get sched_energy_aware eas sched_energy_aware 200 | 201 | if [ -n "$sched_energy_aware" -a -w "$EAS_FILE" ]; then 202 | echo "$sched_energy_aware" > "$EAS_FILE" 2> /dev/null 203 | fi 204 | } 205 | 206 | reset_eas() { 207 | if [ -w "$EAS_FILE" ]; then 208 | echo "$EAS_DEF" > "$EAS_FILE" 2> /dev/null 209 | fi 210 | } 211 | 212 | set_pcie_aspm_policy() { 213 | config_get policy pcie_aspm policy 214 | 215 | if [ -n "$policy" -a -w "$PA_POLICY_FILE" ]; then 216 | echo "$policy" > "$PA_POLICY_FILE" 2> /dev/null 217 | fi 218 | } 219 | 220 | reset_pcie_aspm_policy() { 221 | if [ -w "$PA_POLICY_FILE" ]; then 222 | echo "$PA_POLICY_DEF" > "$PA_POLICY_FILE" 2> /dev/null 223 | fi 224 | } 225 | 226 | start() { 227 | local enabled 228 | if [ -r "/etc/config/${APP_NAME}" ]; then 229 | check_cpu_freq_policy_sections 230 | enabled=$(uci get ${APP_NAME}.config.enabled) 231 | if [ "$enabled" = "1" ]; then 232 | set_eas 233 | config_foreach set_cpu_freq_policy cpu_freq_policy 234 | set_ondemand_tunables 235 | set_conservative_tunables 236 | set_pcie_aspm_policy 237 | fi 238 | else 239 | exit 1 240 | fi 241 | } 242 | 243 | stop() { 244 | reset_eas 245 | config_foreach reset_cpu_freq_policy cpu_freq_policy 246 | reset_ondemand_tunables 247 | reset_conservative_tunables 248 | reset_pcie_aspm_policy 249 | } 250 | -------------------------------------------------------------------------------- /htdocs/luci-static/resources/view/cpu-perf.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 'require form'; 3 | 'require poll'; 4 | 'require rpc'; 5 | 'require uci'; 6 | 'require ui'; 7 | 'require view'; 8 | 9 | const btnStyleEnabled = 'btn cbi-button-save'; 10 | const btnStyleDisabled = 'btn cbi-button-reset'; 11 | 12 | return view.extend({ 13 | appName : 'cpu-perf', 14 | initStatus: null, 15 | 16 | callCpuPerf: rpc.declare({ 17 | object: 'luci.cpu-perf', 18 | method: 'getCpuPerf', 19 | expect: { '': {} } 20 | }), 21 | 22 | callInitStatus: rpc.declare({ 23 | object: 'luci', 24 | method: 'getInitList', 25 | params: [ 'name' ], 26 | expect: { '': {} } 27 | }), 28 | 29 | callInitAction: rpc.declare({ 30 | object: 'luci', 31 | method: 'setInitAction', 32 | params: [ 'name', 'action' ], 33 | expect: { result: false } 34 | }), 35 | 36 | getInitStatus() { 37 | return this.callInitStatus(this.appName).then(res => { 38 | if(res) { 39 | return res[this.appName].enabled; 40 | } else { 41 | throw _('Command failed'); 42 | } 43 | }).catch(e => { 44 | ui.addNotification(null, 45 | E('p', _('Failed to get %s init status: %s').format(this.appName, e))); 46 | }); 47 | }, 48 | 49 | handleServiceAction(action) { 50 | return this.callInitAction(this.appName, action).then(success => { 51 | if(!success) { 52 | throw _('Command failed'); 53 | }; 54 | return true; 55 | }).catch(e => { 56 | ui.addNotification(null, 57 | E('p', _('Service action failed "%s %s": %s').format(this.appName, action, e))); 58 | }); 59 | }, 60 | 61 | serviceRestart(ev) { 62 | poll.stop(); 63 | return this.handleServiceAction('restart').then(() => { 64 | poll.start(); 65 | }); 66 | }, 67 | 68 | freqFormat(freq) { 69 | if(!freq) { 70 | return '-'; 71 | }; 72 | return (freq >= 1000000) ? 73 | (freq / 1000000) + ' ' + _('GHz') 74 | : 75 | (freq / 1000) + ' ' + _('MHz'); 76 | }, 77 | 78 | updateCpuPerfData() { 79 | this.callCpuPerf().then((data) => { 80 | if(data.cpus) { 81 | for(let i of Object.values(data.cpus)) { 82 | let policy = data.freqPolicies[String(i.policy)]; 83 | if(policy) { 84 | document.getElementById('cpu' + i.number + 'number').textContent = 85 | _('CPU') + ' ' + i.number; 86 | document.getElementById('cpu' + i.number + 'curFreq').textContent = 87 | (policy.sCurFreq) ? 88 | this.freqFormat(policy.sCurFreq) 89 | : 90 | this.freqFormat(policy.curFreq); 91 | document.getElementById('cpu' + i.number + 'minFreq').textContent = 92 | (policy.sMinFreq) ? 93 | this.freqFormat(policy.sMinFreq) 94 | : 95 | this.freqFormat(policy.minFreq) 96 | document.getElementById('cpu' + i.number + 'maxFreq').textContent = 97 | (policy.sMaxFreq) ? 98 | this.freqFormat(policy.sMaxFreq) 99 | : 100 | this.freqFormat(policy.maxFreq); 101 | document.getElementById('cpu' + i.number + 'governor').textContent = 102 | policy.governor || '-'; 103 | }; 104 | }; 105 | }; 106 | if(data.ondemand) { 107 | document.getElementById('OdUpThreshold').textContent = 108 | data.ondemand.upThreshold || '-'; 109 | document.getElementById('OdIgnNiceLoad').textContent = 110 | (data.ondemand.ignNiceLoad !== undefined) ? 111 | data.ondemand.ignNiceLoad : '-'; 112 | document.getElementById('OdSmpDownFactor').textContent = 113 | data.ondemand.smpDownFactor || '-'; 114 | document.getElementById('OdPowersaveBias').textContent = 115 | (data.ondemand.powersaveBias !== undefined) ? 116 | data.ondemand.powersaveBias : '-'; 117 | }; 118 | if(data.conservative) { 119 | document.getElementById('CoFreqStep').textContent = 120 | (data.conservative.freqStep !== undefined) ? 121 | data.conservative.freqStep : '-'; 122 | document.getElementById('CoDownThreshold').textContent = 123 | data.conservative.downThreshold || '-'; 124 | document.getElementById('CoSmpDownFactor').textContent = 125 | data.conservative.smpDownFactor || '-'; 126 | }; 127 | if(data.eas) { 128 | document.getElementById('EasSchedEnergyAware').textContent = 129 | (data.eas.schedEnergyAware !== undefined) ? 130 | data.eas.schedEnergyAware : '-'; 131 | }; 132 | if(data.pcieAspm) { 133 | document.getElementById('PaCurrentPolicy').textContent = 134 | data.pcieAspm.currentPolicy || '-'; 135 | }; 136 | }).catch(e => {}); 137 | }, 138 | 139 | CBIBlockPerf: form.Value.extend({ 140 | __name__ : 'CBI.BlockPerf', 141 | 142 | __init__(map, section, ctx, perfData) { 143 | this.map = map; 144 | this.section = section; 145 | this.ctx = ctx; 146 | this.perfData = perfData; 147 | this.optional = true; 148 | this.rmempty = true; 149 | }, 150 | 151 | renderWidget(section_id, option_index, cfgvalue) { 152 | let cpuTableTitles = [ 153 | _('CPU'), 154 | _('Current frequency'), 155 | _('Minimum frequency'), 156 | _('Maximum frequency'), 157 | _('Scaling governor'), 158 | ]; 159 | 160 | let cpuTable = E('table', { 'class': 'table' }, 161 | E('tr', { 'class': 'tr table-titles' }, [ 162 | E('th', { 'class': 'th left' }, cpuTableTitles[0]), 163 | E('th', { 'class': 'th left' }, cpuTableTitles[1]), 164 | E('th', { 'class': 'th left' }, cpuTableTitles[2]), 165 | E('th', { 'class': 'th left' }, cpuTableTitles[3]), 166 | E('th', { 'class': 'th left' }, cpuTableTitles[4]), 167 | ]) 168 | ); 169 | 170 | let ondemandTable, conservativeTable, easTable, pcieAspmTable; 171 | 172 | if(this.perfData) { 173 | if(this.perfData.cpus) { 174 | for(let i of Object.values(this.perfData.cpus)) { 175 | let policy = this.perfData.freqPolicies[String(i.policy)]; 176 | if(policy) { 177 | cpuTable.append( 178 | E('tr', { 'class': 'tr' }, [ 179 | E('td', { 180 | 'id': 'cpu' + i.number + 'number', 181 | 'class': 'td left', 182 | 'data-title': cpuTableTitles[0], 183 | }, _('CPU') + ' ' + i.number), 184 | E('td', { 185 | 'id': 'cpu' + i.number + 'curFreq', 186 | 'class': 'td left', 187 | 'data-title': cpuTableTitles[1], 188 | }, (policy.sCurFreq) ? 189 | this.ctx.freqFormat(policy.sCurFreq) 190 | : 191 | this.ctx.freqFormat(policy.curFreq) 192 | ), 193 | E('td', { 194 | 'id': 'cpu' + i.number + 'minFreq', 195 | 'class': 'td left', 196 | 'data-title': cpuTableTitles[2], 197 | }, (policy.sMinFreq) ? 198 | this.ctx.freqFormat(policy.sMinFreq) 199 | : 200 | this.ctx.freqFormat(policy.minFreq) 201 | ), 202 | E('td', { 203 | 'id': 'cpu' + i.number + 'maxFreq', 204 | 'class': 'td left', 205 | 'data-title': cpuTableTitles[3], 206 | }, (policy.sMaxFreq) ? 207 | this.ctx.freqFormat(policy.sMaxFreq) 208 | : 209 | this.ctx.freqFormat(policy.maxFreq) 210 | ), 211 | E('td', { 212 | 'id': 'cpu' + i.number + 'governor', 213 | 'class': 'td left', 214 | 'data-title': cpuTableTitles[4], 215 | }, policy.governor || '-'), 216 | ]) 217 | ); 218 | }; 219 | }; 220 | }; 221 | if(this.perfData.ondemand) { 222 | ondemandTable = E('table', { 'class': 'table' }, [ 223 | E('tr', { 'class': 'tr' }, [ 224 | E('td', { 'class': 'td left', 'style':'width:33%' }, "up_threshold"), 225 | E('td', { 'id': 'OdUpThreshold', 'class': 'td left' }, 226 | this.perfData.ondemand.upThreshold || '-'), 227 | ]), 228 | E('tr', { 'class': 'tr' }, [ 229 | E('td', { 'class': 'td left' }, "ignore_nice_load"), 230 | E('td', { 'id': 'OdIgnNiceLoad', 'class': 'td left' }, 231 | (this.perfData.ondemand.ignNiceLoad !== undefined) ? 232 | this.perfData.ondemand.ignNiceLoad : '-'), 233 | ]), 234 | E('tr', { 'class': 'tr' }, [ 235 | E('td', { 'class': 'td left' }, "sampling_down_factor"), 236 | E('td', { 'id': 'OdSmpDownFactor', 'class': 'td left' }, 237 | this.perfData.ondemand.smpDownFactor || '-'), 238 | ]), 239 | E('tr', { 'class': 'tr' }, [ 240 | E('td', { 'class': 'td left' }, "powersave_bias"), 241 | E('td', { 'id': 'OdPowersaveBias', 'class': 'td left' }, 242 | (this.perfData.ondemand.powersaveBias !== undefined) ? 243 | this.perfData.ondemand.powersaveBias : '-'), 244 | ]), 245 | ]); 246 | }; 247 | if(this.perfData.conservative) { 248 | conservativeTable = E('table', { 'class': 'table' }, [ 249 | E('tr', { 'class': 'tr' }, [ 250 | E('td', { 'class': 'td left', 'style':'width:33%' }, "freq_step"), 251 | E('td', { 'id': 'CoFreqStep', 'class': 'td left' }, 252 | (this.perfData.conservative.freqStep !== undefined) ? 253 | this.perfData.conservative.freqStep : '-'), 254 | ]), 255 | E('tr', { 'class': 'tr' }, [ 256 | E('td', { 'class': 'td left' }, "down_threshold"), 257 | E('td', { 'id': 'CoDownThreshold', 'class': 'td left' }, 258 | this.perfData.conservative.downThreshold || '-'), 259 | ]), 260 | E('tr', { 'class': 'tr' }, [ 261 | E('td', { 'class': 'td left' }, "sampling_down_factor"), 262 | E('td', { 'id': 'CoSmpDownFactor', 'class': 'td left' }, 263 | this.perfData.conservative.smpDownFactor || '-'), 264 | ]), 265 | ]); 266 | }; 267 | if(this.perfData.pcieAspm) { 268 | pcieAspmTable = E('table', { 'class': 'table' }, 269 | E('tr', { 'class': 'tr' }, [ 270 | E('td', { 'class': 'td left', 'style':'width:33%' }, "policy"), 271 | E('td', { 'id': 'PaCurrentPolicy', 'class': 'td left' }, 272 | this.perfData.pcieAspm.currentPolicy || '-'), 273 | ]) 274 | ); 275 | }; 276 | if(this.perfData.eas) { 277 | easTable = E('table', { 'class': 'table' }, 278 | E('tr', { 'class': 'tr' }, [ 279 | E('td', { 'class': 'td left', 'style':'width:33%' }, "sched_energy_aware"), 280 | E('td', { 'id': 'EasSchedEnergyAware', 'class': 'td left' }, 281 | (this.perfData.eas.schedEnergyAware !== undefined) ? 282 | this.perfData.eas.schedEnergyAware : '-'), 283 | ]) 284 | ); 285 | }; 286 | }; 287 | if(cpuTable.childNodes.length === 1){ 288 | cpuTable.append( 289 | E('tr', { 'class': 'tr placeholder' }, 290 | E('td', { 'class': 'td' }, 291 | E('em', {}, _('No performance data...')) 292 | ) 293 | ) 294 | ); 295 | }; 296 | let tables = [ cpuTable ]; 297 | if(ondemandTable) { 298 | tables.push( 299 | E('h3', {}, _("Ondemand governor")), 300 | ondemandTable 301 | ); 302 | }; 303 | if(conservativeTable) { 304 | tables.push( 305 | E('h3', {}, _("Conservative governor")), 306 | conservativeTable, 307 | ); 308 | }; 309 | if(easTable) { 310 | tables.push( 311 | E('h3', {}, _("EAS")), 312 | easTable, 313 | ); 314 | }; 315 | if(pcieAspmTable) { 316 | tables.push( 317 | E('h3', {}, _("PCIe ASPM")), 318 | pcieAspmTable, 319 | ); 320 | }; 321 | return E(tables); 322 | }, 323 | }), 324 | 325 | CBIBlockInitButton: form.Value.extend({ 326 | __name__ : 'CBI.BlockInitButton', 327 | 328 | __init__(map, section, ctx) { 329 | this.map = map; 330 | this.section = section; 331 | this.ctx = ctx; 332 | this.optional = true; 333 | this.rmempty = true; 334 | }, 335 | 336 | renderWidget(section_id, option_index, cfgvalue) { 337 | this.ctx.initButton = E('button', { 338 | 'class': (!this.ctx.initStatus) ? btnStyleDisabled : btnStyleEnabled, 339 | 'click': ui.createHandlerFn(this, () => { 340 | return this.ctx.handleServiceAction( 341 | (!this.ctx.initStatus) ? 'enable' : 'disable' 342 | ).then(success => { 343 | if(!success) { 344 | return; 345 | }; 346 | if(!this.ctx.initStatus) { 347 | this.ctx.initButton.textContent = _('Enabled'); 348 | this.ctx.initButton.className = btnStyleEnabled; 349 | this.ctx.initStatus = true; 350 | } else { 351 | this.ctx.initButton.textContent = _('Disabled'); 352 | this.ctx.initButton.className = btnStyleDisabled; 353 | this.ctx.initStatus = false; 354 | }; 355 | }); 356 | }), 357 | }, (!this.ctx.initStatus) ? _('Disabled') : _('Enabled')); 358 | 359 | return E([ 360 | E('label', { 'class': 'cbi-value-title', 'for': 'initButton' }, 361 | _('Run at startup') 362 | ), 363 | E('div', { 'class': 'cbi-value-field' }, [ 364 | E('div', {}, this.ctx.initButton), 365 | E('input', { 366 | 'id' : 'initButton', 367 | 'type': 'hidden', 368 | }), 369 | ]), 370 | ]); 371 | }, 372 | }), 373 | 374 | freqValidate(section_id, value, slave_elem, max=false) { 375 | let slaveValue = slave_elem.formvalue(section_id); 376 | if(value === '' || slaveValue === '') { 377 | return true; 378 | }; 379 | value = Number(value); 380 | slaveValue = Number(slaveValue); 381 | if((max && value >= slaveValue) || (!max && value <= slaveValue)) { 382 | return true; 383 | }; 384 | return `${_('Frequency value must not be')} ${max ? _('lower') : _('higher')} ${_('than the')} "${slave_elem.title}"!`; 385 | }, 386 | 387 | load() { 388 | return Promise.all([ 389 | this.getInitStatus(), 390 | this.callCpuPerf(), 391 | uci.load(this.appName), 392 | ]).catch(e => { 393 | ui.addNotification(null, E('p', _('An error has occurred') + ': %s'.format(e.message))); 394 | }); 395 | }, 396 | 397 | render(data) { 398 | if(!data) { 399 | return; 400 | }; 401 | 402 | this.initStatus = data[0]; 403 | let cpuPerfDataArray = data[1]; 404 | let freqPolicyArray = cpuPerfDataArray.freqPolicies || {}; 405 | let pcieAspmPolicies; 406 | if(cpuPerfDataArray.pcieAspm) { 407 | pcieAspmPolicies = cpuPerfDataArray.pcieAspm.availPolicies; 408 | }; 409 | let easValue; 410 | if(cpuPerfDataArray.eas) { 411 | easValue = cpuPerfDataArray.eas.schedEnergyAware; 412 | }; 413 | 414 | let s, o, ss; 415 | let m = new form.Map(this.appName, 416 | _('CPU Performance'), 417 | _('CPU performance management.')); 418 | 419 | s = m.section(form.NamedSection, 'config', 'main'); 420 | 421 | /* Status */ 422 | 423 | s.tab('values', _('Current values')); 424 | 425 | o = s.taboption('values', this.CBIBlockPerf, this, cpuPerfDataArray); 426 | 427 | /* Performance managment */ 428 | 429 | s.tab('performance', _('Performance managment')); 430 | 431 | // enabled 432 | o = s.taboption('performance', form.Flag, 'enabled', 433 | _('Enable')); 434 | o.rmempty = false; 435 | 436 | // init button 437 | o = s.taboption('performance', this.CBIBlockInitButton, this); 438 | 439 | /* CPU frequency policies */ 440 | 441 | let sections = uci.sections(this.appName, 'cpu_freq_policy'); 442 | 443 | if(sections.length == 0) { 444 | o = s.taboption('performance', form.DummyValue, '_dummy'); 445 | o.rawhtml = true; 446 | o.default = '
' + 447 | _('CPU performance scaling is not available...') + 448 | '
'; 449 | } else { 450 | for(let section of sections) { 451 | let sectionName = section['.name']; 452 | let policyNum = Number(sectionName.replace('policy', '')); 453 | let cpuString = ''; 454 | let cpus = freqPolicyArray[policyNum].cpu; 455 | if(cpus && cpus.length > 0) { 456 | let cpuArr = []; 457 | cpus.forEach(e => { 458 | cpuArr.push(_('CPU') + ' ' + e); 459 | }); 460 | if(cpuArr.length > 0) { 461 | cpuString = cpuArr.join(', '); 462 | }; 463 | }; 464 | 465 | let o; 466 | o = s.taboption('performance', form.SectionValue, 467 | 'cpu_freq_policy', form.NamedSection, sectionName, 'cpu_freq_policy'); 468 | ss = o.subsection; 469 | ss.title = _('Policy') + ' ' + policyNum + ' ( ' + cpuString + ' )'; 470 | 471 | if(freqPolicyArray[policyNum]) { 472 | if(freqPolicyArray[policyNum].sAvailGovernors && 473 | freqPolicyArray[policyNum].sAvailGovernors.length > 0) { 474 | 475 | // scaling_governor 476 | o = ss.option(form.ListValue, 477 | 'scaling_governor', _('Scaling governor'), 478 | _('Scaling governors implement algorithms to estimate the required CPU capacity.') 479 | ); 480 | o.rmempty = true; 481 | o.optional = true; 482 | freqPolicyArray[policyNum].sAvailGovernors.forEach(e => o.value(e)); 483 | }; 484 | 485 | if(freqPolicyArray[policyNum].sMinFreq && freqPolicyArray[policyNum].sMaxFreq && 486 | freqPolicyArray[policyNum].minFreq && freqPolicyArray[policyNum].maxFreq) { 487 | 488 | let minFreq, maxFreq; 489 | 490 | if(freqPolicyArray[policyNum].sAvailFreqs && 491 | freqPolicyArray[policyNum].sAvailFreqs.length > 0) { 492 | let availFreqs = freqPolicyArray[policyNum].sAvailFreqs.map(e => 493 | [ e, this.freqFormat(e) ] 494 | ); 495 | 496 | // scaling_min_freq 497 | minFreq = ss.option(form.ListValue, 498 | 'scaling_min_freq', _('Minimum scaling frequency'), 499 | _('Minimum frequency the CPU is allowed to be running.') + 500 | ' (' + _('default value:') + ' ' + 501 | this.freqFormat(freqPolicyArray[policyNum].minFreq) + ').' 502 | ); 503 | minFreq.rmempty = true; 504 | minFreq.optional = true; 505 | 506 | // scaling_max_freq 507 | maxFreq = ss.option(form.ListValue, 508 | 'scaling_max_freq', _('Maximum scaling frequency'), 509 | _('Maximum frequency the CPU is allowed to be running.') + 510 | ' (' + _('default value:') + ' ' + 511 | this.freqFormat(freqPolicyArray[policyNum].maxFreq) + ').' 512 | ); 513 | maxFreq.rmempty = true; 514 | maxFreq.optional = true; 515 | 516 | availFreqs.forEach(e => { 517 | minFreq.value(e[0], e[1]); 518 | maxFreq.value(e[0], e[1]); 519 | }); 520 | 521 | } else { 522 | 523 | // scaling_min_freq 524 | minFreq = ss.option(form.Value, 525 | 'scaling_min_freq', `${_('Minimum scaling frequency')} (${_('KHz')})`, 526 | _('Minimum frequency the CPU is allowed to be running.') + 527 | ' (' + _('default value:') + ' ' + 528 | freqPolicyArray[policyNum].minFreq + ').' 529 | ); 530 | minFreq.rmempty = true; 531 | minFreq.optional = true; 532 | minFreq.datatype = `and(integer,range(${freqPolicyArray[policyNum].minFreq},${freqPolicyArray[policyNum].maxFreq}))`; 533 | minFreq.placeholder = `${freqPolicyArray[policyNum].minFreq}-${freqPolicyArray[policyNum].maxFreq} ${_('KHz')}`; 534 | 535 | // scaling_max_freq 536 | maxFreq = ss.option(form.Value, 537 | 'scaling_max_freq', `${_('Maximum scaling frequency')} (${_('KHz')})`, 538 | _('Maximum frequency the CPU is allowed to be running.') + 539 | ' (' + _('default value:') + ' ' + 540 | freqPolicyArray[policyNum].maxFreq + ').' 541 | ); 542 | maxFreq.rmempty = true; 543 | maxFreq.optional = true; 544 | maxFreq.datatype = `and(integer,range(${freqPolicyArray[policyNum].minFreq},${freqPolicyArray[policyNum].maxFreq}))`; 545 | maxFreq.placeholder = `${freqPolicyArray[policyNum].minFreq}-${freqPolicyArray[policyNum].maxFreq} ${_('KHz')}`; 546 | }; 547 | 548 | minFreq.validate = L.bind( 549 | function(section_id, value) { 550 | return this.freqValidate(section_id, value, maxFreq, false); 551 | }, 552 | this 553 | ); 554 | maxFreq.validate = L.bind( 555 | function(section_id, value) { 556 | return this.freqValidate(section_id, value, minFreq, true); 557 | }, 558 | this 559 | ); 560 | }; 561 | }; 562 | 563 | if(!freqPolicyArray[policyNum] || 564 | !(freqPolicyArray[policyNum].sAvailGovernors && 565 | freqPolicyArray[policyNum].sAvailGovernors.length > 0) && 566 | !(freqPolicyArray[policyNum].sMinFreq && freqPolicyArray[policyNum].sMaxFreq && 567 | freqPolicyArray[policyNum].minFreq && freqPolicyArray[policyNum].maxFreq)) { 568 | o = ss.option(form.DummyValue, '_dummy'); 569 | o.rawhtml = true; 570 | o.default = '
' + 571 | _('Performance scaling is not available for this policy...') + 572 | '
'; 573 | }; 574 | }; 575 | }; 576 | 577 | /* Ondemand governor */ 578 | 579 | o = s.taboption('performance', form.SectionValue, 580 | 'ondemand', form.NamedSection, 'ondemand', 'governor'); 581 | ss = o.subsection; 582 | ss.title = _("Ondemand governor"); 583 | 584 | // up_threshold 585 | o = ss.option(form.Value, 586 | 'up_threshold', 'up_threshold', 587 | _('If the estimated CPU load is above this value (in percent), the governor will set the frequency to the maximum value.') 588 | ); 589 | o.rmempty = true; 590 | o.optional = true; 591 | o.placeholder = '1-100'; 592 | o.datatype = 'and(integer,range(1,100))'; 593 | 594 | // ignore_nice_load 595 | o = ss.option(form.Flag, 596 | 'ignore_nice_load', 'ignore_nice_load', 597 | _('If checked, it will cause the CPU load estimation code to treat the CPU time spent on executing tasks with "nice" levels greater than 0 as CPU idle time.') 598 | ); 599 | o.rmempty = true; 600 | o.optional = true; 601 | 602 | // sampling_down_factor 603 | o = ss.option(form.Value, 604 | 'sampling_down_factor', 'sampling_down_factor', 605 | _('Frequency decrease deferral factor.') 606 | ); 607 | o.rmempty = true; 608 | o.optional = true; 609 | o.placeholder = '1-100'; 610 | o.datatype = 'and(integer,range(1,100))'; 611 | 612 | // powersave_bias 613 | o = ss.option(form.Value, 614 | 'powersave_bias', 'powersave_bias', 615 | _('Reduction factor to apply to the original frequency target of the governor.') 616 | ); 617 | o.rmempty = true; 618 | o.optional = true; 619 | o.placeholder = '0-1000'; 620 | o.datatype = 'and(integer,range(0,1000))'; 621 | 622 | /* Conservative governor */ 623 | 624 | o = s.taboption('performance', form.SectionValue, 625 | 'conservative', form.NamedSection, 'conservative', 'governor'); 626 | ss = o.subsection; 627 | ss.title = _("Conservative governor"); 628 | 629 | // freq_step 630 | o = ss.option(form.Value, 631 | 'freq_step', 'freq_step', 632 | _('Frequency step in percent of the maximum frequency the governor is allowed to set.') 633 | ); 634 | o.rmempty = true; 635 | o.optional = true; 636 | o.placeholder = '1-100'; 637 | o.datatype = 'and(integer,range(1,100))'; 638 | 639 | // down_threshold 640 | o = ss.option(form.Value, 641 | 'down_threshold', 'down_threshold', 642 | _('Threshold value (in percent) used to determine the frequency change direction.') 643 | ); 644 | o.rmempty = true; 645 | o.optional = true; 646 | o.placeholder = '1-100'; 647 | o.datatype = 'and(integer,range(1,100))'; 648 | 649 | // sampling_down_factor 650 | o = ss.option(form.Value, 651 | 'sampling_down_factor', 'sampling_down_factor', 652 | _('Frequency decrease deferral factor.') 653 | ); 654 | o.rmempty = true; 655 | o.optional = true; 656 | o.placeholder = '1-10'; 657 | o.datatype = 'and(integer,range(1,10))'; 658 | 659 | /* EAS */ 660 | 661 | if(easValue !== undefined ) { 662 | o = s.taboption('performance', form.SectionValue, 663 | 'eas', form.NamedSection, 'eas', 'kernel'); 664 | ss = o.subsection; 665 | ss.title = _("EAS"); 666 | 667 | // sched_energy_aware 668 | o = ss.option(form.Flag, 'sched_energy_aware', 'sched_energy_aware', 669 | _('Enable/disable Energy Aware Scheduling (EAS).') 670 | ); 671 | o.rmempty = false; 672 | o.default = '1'; 673 | }; 674 | 675 | /* PCIe ASPM */ 676 | 677 | if(pcieAspmPolicies && pcieAspmPolicies.length > 0) { 678 | o = s.taboption('performance', form.SectionValue, 679 | 'pcie_aspm', form.NamedSection, 'pcie_aspm', 'module'); 680 | ss = o.subsection; 681 | ss.title = _("PCIe ASPM"); 682 | 683 | // policy 684 | o = ss.option(form.ListValue, 'policy', 'policy', 685 | _('Active-State Power Management (ASPM) policy for PCIe links, which saves power by putting unused PCIe links into a low-power state.') 686 | ); 687 | o.rmempty = true; 688 | o.optional = true; 689 | pcieAspmPolicies.forEach(e => o.value(e)); 690 | }; 691 | 692 | let mapPromise = m.render(); 693 | mapPromise.then(node => { 694 | node.classList.add('fade-in'); 695 | poll.add(L.bind(this.updateCpuPerfData, this)); 696 | }); 697 | return mapPromise; 698 | }, 699 | 700 | handleSaveApply(ev, mode) { 701 | return this.handleSave(ev).then(() => { 702 | ui.changes.apply(mode == '0'); 703 | window.setTimeout(() => this.serviceRestart(), 3000); 704 | }); 705 | }, 706 | }); 707 | --------------------------------------------------------------------------------