CPUFreq驅動

来源:https://www.cnblogs.com/linhaostudy/archive/2018/07/23/9355595.html
-Advertisement-
Play Games

CPUFreq子系統位於 drivers/cpufreq目錄下,負責進行運行過程中CPU頻率和電壓的動態調整,即DvFS( Dynamic Voltage Frequency Scaling,動態電壓頻率調整)。運行時進行CPU電壓和頻率調整的原因是:CMOS電路中的功耗與電壓的平方成正比、與頻率成 ...


CPUFreq子系統位於 drivers/cpufreq目錄下,負責進行運行過程中CPU頻率和電壓的動態調整,即DvFS( Dynamic Voltage Frequency Scaling,動態電壓頻率調整)。運行時進行CPU電壓和頻率調整的原因是:CMOS電路中的功耗與電壓的平方成正比、與頻率成正比(P∝fV2)因此降低電壓和頻率可降低功耗。
CPUFreq的核心層位於drivers/cpufreq/cpufreq,c下,它為各個SoC的CPUFreq驅動的實現提供了一套統一的介面,並實現了一套notifier機制,可以在 CPUFreq的策略和頻率改變的時候向其他模塊發出通知。另外,在CPU運行頻率發生變化的時候,內核的 loops perify常數也會發生相應變化。

SOC的CPUFreq驅動實現

每個SoC的具體CPUFreq驅動實例只需要實現電壓、頻率表,以及從硬體層面完成這些變化。
CPUFreq核心層提供瞭如下API以供SoC註冊自身的CPUFreq驅動:
int cpufreq_register_driver(struct cpufreq_driver *driver_data)
其參數為一個cpufreq_driver結構體指針,實際上,cpufreq_driver封裝了一個具體的SoC的CPUFreq驅動的主體,該結構體形如代碼如下所示。

struct cpufreq_driver {
    char            name[CPUFREQ_NAME_LEN];
    u8          flags;

    /* needed by all drivers */
    int (*init)     (struct cpufreq_policy *policy);
    int (*verify)   (struct cpufreq_policy *policy);

    /* define one out of two */
    int (*setpolicy)    (struct cpufreq_policy *policy);
    int (*target)   (struct cpufreq_policy *policy, /* Deprecated */
                 unsigned int target_freq,
                 unsigned int relation);
    int (*target_index) (struct cpufreq_policy *policy,
                 unsigned int index);

    /* should be defined, if possible */
    unsigned int    (*get)  (unsigned int cpu);

    /* optional */
    int (*bios_limit)   (int cpu, unsigned int *limit);

    int (*exit)     (struct cpufreq_policy *policy);
    int (*suspend)  (struct cpufreq_policy *policy);
    int (*resume)   (struct cpufreq_policy *policy);
    struct freq_attr    **attr;
};

其中的 owner成員一般被設置為 THIS MODULE;name成員是CPUFreq驅動的名字,如drivers/cpufreq/s5pv210-cpufreq.c設置name為s5pv210, drivers/cpufreq/omap-cpufreq.c設置name為omap;falgs是一些暗示性的標誌,譬如,若設置了 CPUFREQ_CONST_LOOPS,則是告訴內核loops_per_jiffy不會因為CPU頻率的變化而變化。
init()成員是一個per-CPU初始化函數指針,每當一個新的CPU被註冊進系統的時候,該函數就被調用,該函數接受一個cpufreq_policy的指針參數,在init()成員函數中,可進行如下設置:

policy->cpuinfo.min_freq
policy->cpuinfo.max_freq

上述代碼描述的是該CPU支持的最小頻率和最大頻率(單位是kHz)。

policy->cur

上述代碼描述的是CPU的當前頻率;

policy->policy
policy->governor
policy->min
policy->max

上述代碼定義該CPU的預設策略,以及在預設策略情況下,該策略支持的最小、最大CPU頻率。
verify成員函數用於對用戶的 CPUFreq策略設置進行有效性驗證和數據修正。每當用戶設定一個新策略時,該函數根據老的策略和新的策略,檢驗新策略設置的有效性並對無效設置進行必要的修正。在該成員函數的具體實現中,常用到如下輔助函數:

static inline void
cpufreq_verify_within_cpu_limits(struct cpufreq_policy *policy)
{
    cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
    policy->cpuinfo.max_freq);
}

setpolicyo成員函數接受一個policy參數(包含policy->policypolicy->minpolicy->max等成員),實現了這個成員函數的CPU一般具備在一個範圍(limit,從policy->minpolicy->max)里自動調整頻率的能力。目前只有少數驅動(如intel_pstate.c和longrun.c)包含這樣的成員函數,而絕大多數CPU都不會實現此函數,一般只實現target()成員函數,target()的參數直接就是一個指定的頻率。
target()成員函數用於將頻率調整到一個指定的值,接受3個參數: policy、 target_freq和relation, target freq是目標頻率,實際驅動總是要設定真實的CPU頻率到最接近於 target_feq,並且設定的頻率必須位於 policy->min到 policy->max之間。在設定頻率接近 target_feq的情況下, relation若為 CPUFREQ REL I,則暗示設置的頻率應該大於或等於 target_freq; relation若為 CPUFREQ_REL_H,則暗示設置的頻率應該小於或等於 target_freq。
表19.1描述了 setpolicy()和 target()所針對的CPU以及調用方式上的區別。

setpolicy() target()
CPU有在一定範圍內獨立調整頻率的能力 CPU只能指定頻率
CPU freq policy 調用到setpolicy(),由CPU獨立在一個範圍內調整頻率 由CPU Freq核心層根據系統負載和策略綜合決定目標頻率

根據晶元內部PLL和分頻器的關係, ARM SOC一般不具備獨立調整頻率的能力,往往SoC的 CPUFreq驅動會提供一個頻率表,頻率在該表的範圍內進行變更,因此一般實現target()成員函數。
CPUFreq核心層提供了一組與頻率表相關的輔助API。

int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
                    struct cpufreq_frequency_table *table)
{
    unsigned int min_freq = ~0;
    unsigned int max_freq = 0;
    unsigned int i;

    for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
        unsigned int freq = table[i].frequency;
        if (freq == CPUFREQ_ENTRY_INVALID) {
            pr_debug("table entry %u is invalid, skipping\n", i);

            continue;
        }
        pr_debug("table entry %u: %u kHz, %u driver_data\n",
                    i, freq, table[i].driver_data);
        if (freq < min_freq)
            min_freq = freq;
        if (freq > max_freq)
            max_freq = freq;
    }

    policy->min = policy->cpuinfo.min_freq = min_freq;
    policy->max = policy->cpuinfo.max_freq = max_freq;

    if (policy->min == ~0)
        return -EINVAL;
    else
        return 0;
}

它是 cpufreq driver的init成員函數的助手,用於將policy->min和 policy->max設置為與 cpuinfo->min_freq和 cpuinfo.max_freq相同的值。

int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
                   struct cpufreq_frequency_table *table)

它是 cpufreq driver的verify成員函數的助手,確保至少有1個有效的CPU頻率位於policy->min到 policy->max的範圍內。

int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
                   struct cpufreq_frequency_table *table,
                   unsigned int target_freq,
                   unsigned int relation,
                   unsigned int *index)

CPUFreq的策略

SoCCPUFreq驅動只是設定了CPU的頻率參數,以及提供了設置頻率的途徑,但是它並不會管CPU自身究竟應該運行在哪種頻率上。究竟頻率依據的是哪種標準,進行何種變化而這些完全由 CPUFreq的策略( policy)決定,這些策略如表19.2所示。

CPUFreq的策略 策略實現的方式
cpufreq_ondemand 平時以低速的方式運行,當系統負載提高需自動提高頻率
cpufreq_performance cpu以最高頻率運行,即scaling_max_freq
cpufreq_consevative 字面含義是傳統的、保守的,跟ondemand相似,區別在於動態頻率在變更的時候採用漸進的方式
cpufreq_powersave cpu以最低頻率運行,即scaling_min_freq
cpufreq_userspace 讓根用戶通過sys節點scaling_setspeed設置頻率

在 Android系統中,則增加了1個交互策略,該策略適合於對延遲敏感的U1交互任務,當有UI交互任務的時候,該策略會更加激進並及時地調整CPU頻率。
總而言之,系統的狀態以及CPUFreq的策略共同決定了CPU頻率跳變的目標, CPUFreq核心層並將目標頻率傳遞給底層具體SoC的 CPUFreq驅動,該驅動修改硬體,完成頻率的變換,如圖19.2所示。

用戶空間一般可通過 /sys/devices/system/cpu/cpux/cpufreq節點來設置 CPUFreq。譬如,我們要設置 CPUFreq到700Mhz,採用 userspace策略,則運行如下命令:

echo userspace >/sys/devices/system/cpu/cpu0/cpufreq/scaling governor
echo 700000>/sys/devices/system/cpu/cpu/cpufreq/scaling set speed

CPUFreq的性能測試和調優

使用cpufreq-bench工具可以幫助工程師分析採用CPUFreq後對系統性能的影響;

CPUFreq通知

CPUFreq子系統會發出通知的情況有兩種:CPUFreq的策略變化或者CPU運行頻率變化。
在策略變化的過程中,會發送3次通知:

  • CPUFREQ ADJUST:所有註冊的 notifier可以根據硬體或者溫度的情況去修改範圍(即 policy->min和 policy->max);
  • CPUFREQ INCOMPATIBLE:除非前面的策略設定可能會導致硬體出錯,否則被註冊的notifier不能改變範圍等設定;
  • CPUFREQ NOTIFY:所有註冊的notifier都會被告知新的策略已經被設置。在頻率變化的過程中,會發送2次通知;
  • CPUFREQ PRECHANGE:準備進行頻率變更;
  • CPUFREQ POSTCHANGE:已經完成頻率變更。

notifier中的第3個參數是一個 cpufreq_freqs的結構體,包含cpu(CPU號)、old(過去的頻率)和new(現在的頻率)這3個成員。發送 CPUFREQ_PRECHANGE和 CPUFREQ_POSTCHANGE的代碼如下:

int srcu_notifier_call_chain(struct srcu_notifier_head *nh,
        unsigned long val, void *v)
    

如果某模塊關心 CPUFREQ_PRECHANGE或 CPUFREQ_POSTCHANGE事件,可簡單地使用 Linux notifier機制監控。譬如, drivers/video/sallo0fbc在CPU頻率變化過程中需對自身硬體進行相關設置,因此它註冊了 notifier併在 CPUFREQ _PRECHANGE和CPUFREQ_POSTCHANGE情況下分別進行不同的處理,如代碼清單19.3所示。

#ifdef CONFIG_CPU_FREQ
    fbi->freq_transition.notifier_call = sa1100fb_freq_transition;
    fbi->freq_policy.notifier_call = sa1100fb_freq_policy;
    cpufreq_register_notifier(&fbi->freq_transition, CPUFREQ_TRANSITION_NOTIFIER);
    cpufreq_register_notifier(&fbi->freq_policy, CPUFREQ_POLICY_NOTIFIER);
#endif


/*
 * CPU clock speed change handler.  We need to adjust the LCD timing
 * parameters when the CPU clock is adjusted by the power management
 * subsystem.
 */
static int
sa1100fb_freq_transition(struct notifier_block *nb, unsigned long val,
             void *data)
{
    struct sa1100fb_info *fbi = TO_INF(nb, freq_transition);
    struct cpufreq_freqs *f = data;
    u_int pcd;

    switch (val) {
    case CPUFREQ_PRECHANGE:
        set_ctrlr_state(fbi, C_DISABLE_CLKCHANGE);
        break;

    case CPUFREQ_POSTCHANGE:
        pcd = get_pcd(fbi->fb.var.pixclock, f->new);
        fbi->reg_lccr3 = (fbi->reg_lccr3 & ~0xff) | LCCR3_PixClkDiv(pcd);
        set_ctrlr_state(fbi, C_ENABLE_CLKCHANGE);
        break;
    }
    return 0;
}

此外,如果在系統掛起/恢復的過程中CPU頻率會發生變化,則 CPUFreq子系統也會發出CPUFREQ_SUSPENDCHANGE和 CPUFREQ _RESUMECHANGE這兩個通知。

值得一提的是,除了CPU以外,一些非CPU設備也支持多個操作頻率和電壓,存在多個OPP。Linux3.2之後的內核也支持針對這種非CPU設備的DVFS,該套子系統為Devfreq。與CPUFreq存在一個drivers/cpufreq目錄相似,在內核中也存在一個drivers/devore的目錄。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 決定認真從零開始寫一個Linux的學習過程,像我這麼偷懶的人能寫文字記錄已經很不容易了,希望不要半途而廢吧(拖走) 用多了Linux其實發現,要是哪天Linux和Windows能結合下就好了,簡單粗暴的Linux命令行加上Windows萬能的UI以及相容才是夢想中的系統啊,瓶瓶罐罐的東西真是討厭~~ ...
  • 基於Red Hat Enterprise Linux 7.5 Linux中的root就是存在於所有類UNIX系統中的超級用戶,持有最高管理許可權,能添加/刪除用戶、開關機、關閉或開啟硬體或者系統服務等,並且root的操作無法被阻擋。 Linux中root為什麼叫root,不是因為名字叫root,而是U ...
  • 我的 Ubuntu 鏡像是在「清華大學開源軟體鏡像網」下載的,版本號是16.04 下載一個ios文件,裝在虛擬機中就行了。 虛擬機用的 VMware Workstation 12, 註冊密鑰: 5A02H-AU243-TZJ49-GTC7K-3C61N AA39H-61W50-H8DCY-1MM79 ...
  • 什麼是netstat     在Linux系統中輸入 man netstat ,顯示的結果如下所示:     從上面可以看出netstat命令的主要功能為: 顯示網路連接信息 顯示路由表信息 顯示網卡統計信息 顯示無效的連接信息 顯示組播成員信息   ...
  • df命令用於查看磁碟的分區,磁碟已使用的空間,剩餘的空間 1、用法 df [選項] [文件..] 2、命令選項 -a,--all 全部文件系統-h,--human-readable 以以合適的單位來顯示信息-H,--si 與-h參數相同,但在計算時是以1000 Bytes為換算單位而非1024 By ...
  • 配置號IP地址後,使用ifconfig命令,沒有顯示eth0,重啟網卡(service network restart)又遇到以下報錯信息 報錯信息: 解決辦法: 首先使用ifconfig -a命令,記錄下eth1的mac地址:00:0C:29:C5:AA:CF 然後打開/etc/sysconfig ...
  • 卸載已經安裝的Docker 添加阿裡鏡像 查看可以安裝的docker版本 安裝18.03.1 啟動 Docker 設置Docker阿裡雲加速器 sudo mkdir p /etc/docker sudo tee /etc/docker/daemon.json 註意:需要替換 為你的阿裡雲加速器地址, ...
  • chmod用於管理文件或目錄的許可權,文件或目錄許可權的控制分別以讀取(r)、寫入(w)、執行(x)3種 可讀可寫可執行,抽象的用二進位來表示 1 代表擁有該許可權,0 代表沒有該許可權,這樣我們就可以看到 具有全部許可權二進位可理解為 “111” 即 十進位的 “7”,只有讀寫許可權二進位可理解為 “100” ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...