Linux電源管理(11)_Runtime PM之功能描述

来源:https://www.cnblogs.com/linhaostudy/archive/2019/12/12/12029151.html
-Advertisement-
Play Games

1. 前言 終於可以寫Runtime PM(後面簡稱RPM)了,說實話,蝸蝸有點小激動。因為從個人的角度講,我很推崇使用RPM進行日常的動態電源管理,而不是suspend機制。 軟體工程的基本思想就是模塊化:高內聚和低耦合。通俗地講呢,就是“各人自掃門前雪”,儘量掃好自己的(高內聚),儘量不和別人交 ...


1. 前言

終於可以寫Runtime PM(後面簡稱RPM)了,說實話,蝸蝸有點小激動。因為從個人的角度講,我很推崇使用RPM進行日常的動態電源管理,而不是suspend機制。

軟體工程的基本思想就是模塊化:高內聚和低耦合。通俗地講呢,就是“各人自掃門前雪”,儘量掃好自己的(高內聚),儘量不和別人交互(低耦合)。而RPM正體現了這一思想:每個設備(包括CPU)都處理好自身的電源管理工作,儘量以最低的能耗完成交代的任務,儘量在不需要工作的時候進入低功耗狀態,儘量不和其它模塊有過多耦合。每個設備都是最節省的話,整個系統一定是最節省的,最終達到無所謂睡、無所謂醒的天人合一狀態。

講到這裡想到自己的一則趣事:大學時,蝸蝸是寢室長,但不愛打掃衛生,於是就提出一個口號,“不污染,不治理;誰污染,誰治理”。結果呢,大家猜就是了,呵呵。言歸正傳,開始吧。

2. Runtime PM的軟體框架

聽多了RPM的傳說,有種莫名的恐懼,覺的會很複雜。但看代碼,也就是“drivers/base/power/runtime.c”中1400行而已。

從設計思路上講,它確實簡單。下麵是一個大概的軟體框架:

image

device driver(或者driver所在的bus、class等)需要提供3個回調函數,runtime_suspend、runtime_resume和runtime_idle,分別用於suspend device、resume device和idle device。它們一般由RPM core在合適的時機調用,以便降低device的power consumption。

而調用的時機,最終是由device driver決定的。driver會在適當的操作點,調用RPM core提供的put和get系列的helper function,彙報device的當前狀態。RPM core會為每個device維護一個引用計數,get時增加計數值,put時減少計數值,當計數為0時,表明device不再被使用,可以立即或一段時間後suspend,以節省功耗。

好吧,說總是簡單,那做呢?很不幸,到目前為止,linux kernel的runtime PM還是很複雜。這裡的複雜,不是從實現的角度,而是從對外的角度。在“include\linux\pm_runtime.h”中,RPM提供了將近50個介面。軟體模塊化的設計理念中,最重要的一個原則就是提供簡潔的介面。很顯然,RPM沒有做到!

無論RPM面對的問題有多麼複雜,無論理由有多麼充分,它也應堅守“簡潔性”這一原則。否則,結果只有一個----無人敢用。這就是當前Linux kernel電源管理中“Opportunistic suspend”和RPM兩種機制並存的原因。但是,就算現狀不理想,也不能否認RPM的先進性,在當前以及未來很長的一段時間內,它會是kernel電源管理更新比較活躍的部分,因為可以做的還很多。

鑒於這個現狀,本文以及後續RPM有關的文章,會選取最新的kernel(當前為linux-3.17),以便及時同步相關的更新。

3. Runtime PM的運行機制

3.1 核心機制

RPM的核心機制是這樣的:

1)為每個設備維護一個引用計數(device->power.usage_count),用於指示該設備的使用狀態。

2)需要使用設備時,device driver調用pm_runtime_get(或pm_runtime_get_sync)介面,增加引用計數;不再使用設備時,device driver調用pm_runtime_put(或pm_runtime_put_sync)介面,減少引用計數。

3)每一次put,RPM core都會判斷引用計數的值。如果為零,表示該設備不再使用(idle)了,則使用非同步(ASYNC)或同步(SYNC)的方式,調用設備的.runtime_idle回調函數。

4).runtime_idle的存在,是為了在idle和suspend之間加一個緩衝,避免頻繁的suspend/resume操作。因此它的職責是:判斷設備是否具備suspend的條件,如果具備,在合適的時機,suspend設備。

可以不提供,RPM core會使用非同步(ASYNC)或同步(SYNC)的方式,調用設備的.runtime_suspend回調函數,suspend設備,同時記錄設備的PM狀態;

可以調用RPM core提供helper函數(pm_runtime_autosuspend_expiration、pm_runtime_autosuspend、pm_request_autosuspend),要求在指定的時間後,suspend設備。

5)pm_runtime_autosuspend、pm_request_autosuspend等介面,會起一個timer,併在timer到期後,使用非同步(ASYNC)或同步(SYNC)的方式,調用設備的.runtime_suspend回調函數,suspend設備,同時記錄設備的PM狀態。

6)每一次get,RPM core都會判斷設備的PM狀態,如果不是active,則會使用非同步(ASYNC)或同步(SYNC)的方式,調用設備的.runtime_resume回調函數,resume設備。

註1:Runtime PM中的“suspend”,不一定要求設備必須進入低功耗狀態,而是要求設備在suspend後,不再處理數據,不再和CPUs、RAM進行任何的交互,直到設備的.runtime_resume被調用。因為此時設備的parent(如bus controller)、CPU是、RAM等,都有可能因為suspend而不再工作,如果設備再有任何動作,都會造成不可預期的異常。下麵是“Documentation\power\runtime_pm.txt”中的解釋,供大家參考:

  • Once the subsystem-level suspend callback (or the driver suspend callback,
    if invoked directly) has completed successfully for the given device, the PM
    core regards the device as suspended, which need not mean that it has been
    put into a low power state. It is supposed to mean, however, that the
    device will not process data and will not communicate with the CPU(s) and
    RAM until the appropriate resume callback is executed for it. The runtime
    PM status of a device after successful execution of the suspend callback is
    'suspended'.

註2:回憶一下wakeup events和wakeup lock,Runtime PM和它們在本質上是一樣的,都是實時的向PM core報告“我不工作了,可以睡了”、“我要工作了,不能睡(或醒來吧)”。不同的是:wakeup events和RPM的報告者是內核空間drivers,而wakeup lock是用戶空間進程;wakeup events和wakelock涉及的睡眠對象是整個系統,包括CPU和所有的devices,而RPM是一個一個獨立的device(CPU除外,它由cpu idle模塊處理,可看作RPM的特例)。

3.2 get和put的時機

這個話題的本質是:device idle的判斷標準是什麼?

再回憶一下“autosleep”中有關“Opportunistic suspend”的討論,對“Opportunistic suspend”而言,suspend時機的判斷是相當困難的,因為整機的運行環境比較複雜。而每一個具體設備的idle,就容易多了,這就是Runtime PM的優勢。回到這個話題上,對device而言,什麼是idle?

device是通過用戶程式為用戶提供服務的,而服務的方式分為兩種:接受指令,做事情(被動);上報事件(主動,一般通過中斷的方式)。因此,設備active的時間段,包括【接受指令,完成指令】和【事件到達,由driver記錄下來】兩個。除此之外的時間,包括driver從用戶程式獲得指令(以及相關的數據)、driver將事件(以及相關的數據)交給應用程式,都是idle時間。

那idle時間是否應立即suspend以節省功耗?不一定,要具體場景具體對待:例如網路傳輸,如果網路連接正常,那麼在可預期的、很短的時間內,設備又會active(傳輸網路數據),如果頻繁suspend,會降低性能,且不會省電;比如用戶按鍵,具有突發性,因而可以考慮suspend;等等。

由於get和put正是設備idle狀態的切換點,因此get和put的時機就容易把握了:

1)主動訪問設備時,如寫寄存器、發起數據傳輸等等,get,增加引用計數,告訴RPM core設備active;訪問結束後,put,減小引用計數,告訴RPM core設備可能idle。

2)設備有事件通知時,get(可能在中斷處理中);driver處理完事件後,put。

註3:以上只是理論場景,實際可以放寬,以減小設計的複雜度。

3.3 非同步(ASYNC)和同步(SYNC)

設備驅動代碼可在進程和中斷兩種上下文執行,因此put和get等介面,要麼是由用戶進程調用,要麼是由中斷處理函數調用。由於這些介面可能會執行device的.runtime_xxx回調函數,而這些介面的執行時間是不確定的,有些可能還會睡眠等待。這對用戶進程或者中斷處理函數來說,是不能接受的。

因此,RPM core提供的預設介面(pm_runtime_get/pm_runtime_put等),採用非同步調用的方式(由ASYNC flag表示),啟動一個work queue,在單獨的線程中,調用.runtime_xxx回調函數,這可以保證設備驅動之外的其它模塊正常運行。

另外,如果設備驅動清楚地知道自己要做什麼,也可以使用同步介面(pm_runtime_get_sync/pm_runtime_put_sync等),它們會直接調用.runtime_xxx回調函數,不過,後果自負!

3.4 Runtime PM過程中的同步問題

由於.runtime_xxx回調函數可能採用非同步的形式調用,以及Generic PM suspend和RPM並存的現狀,要求RPM要小心處理同步問題,包括:

多個.runtime_suspend請求之間的同步;

多個.runtime_resume請求之間的同步;

多個.runtime_idle請求之間的同步;

.runtime_suspend請求和.runtime_resume請求之間的同步;

.runtime_suspend請求和system suspend之間的同步;

.runtime_resume請求和system resume之間的同步;
等等。

3.5 級聯設備之間的Runtime PM

struct device結構中,有一個parent指針,指向該設備的父設備(沒有的話為空)。父設備通常是Bus、host controller,設備的正常工作,依賴父設備。體現在RPM中,就是如下的行為:

1)parent設備下任何一個設備處於active狀態,parent必須active。

2)parent設備下任何一個設備idle了,要通知parent,parent以此記錄處於active狀態的child設備個數。

3)parent設備下所有子設備都idle了,parent才可以idle。

以上行為RPM core會自動處理,不需要驅動工程師太過操心。

3.6 device的runtime status及其初始狀態

在Runtime Power Management的過程中,device可處於四種狀態:RPM_ACTIVE、RPM_RESUMING、RPM_SUSPENDED和RPM_SUSPENDING。

RPM_ACTIVE,設備處於正常工作的狀態,表示設備的.runtime_resume回調函數執行成功;

RPM_SUSPENDED,設備處於suspend狀態,表示設備.runtime_suspend回調函數執行成功;

RPM_RESUMING,設備的.runtime_resume正在被執行;

RPM_SUSPENDING,設備的.runtime_suspend正在被執行。

註4:前面說過,.runtime_idle只是suspend前的過渡,因此runtime status和idle無關。

device註冊時,設備模型代碼會調用pm_runtime_init介面,將設備的runtime status初始化為RPM_SUSPENDED,而kernel並不知道某個設備初始化時的真正狀態,因此設備驅動需要根據實際情況,調用RPM的helper函數,將自身的status設置正確。

4. runtime PM的API彙整

RPM提供的API位於“include/linux/pm_runtime.h”中,在這裡先瀏覽一下,目的有二:一是對前面描述的RPM運行機制有一個感性的認識;二是為後面分析RPM的運行機製做準備。

註5:我會把和驅動編寫相關度較高的API加粗,其它的能不用就不用、能不看就不看!

extern int __pm_runtime_idle(struct device *dev, int rpmflags); 
extern int __pm_runtime_suspend(struct device *dev, int rpmflags); 
extern int __pm_runtime_resume(struct device *dev, int rpmflags);

這三個函數是RPM的idle、put/suspend、get/resume等操作的基礎,根據rpmflag,有著不同的操作邏輯。後續很多API,都是基於它們三個。一般不會在設備驅動中直接使用。

extern int pm_schedule_suspend(struct device *dev, unsigned int delay);

在指定的時間後(delay,單位是ms),suspend設備。該介面為非同步調用,不會更改設備的引用計數,可在driver的.rpm_idle中調用,免去driver自己再啟一個timer的煩惱。

extern void pm_runtime_enable(struct device dev);
extern void pm_runtime_disable(struct device
dev);

設備RPM功能的enable/disable,可嵌套調用,會使用一個變數(dev->power.disable_depth)記錄disable的深度。只要disable_depth大於零,就意味著RPM功能不可使用,很多的API調用(如suspend/reesume/put/get等)會返回失敗。

RPM初始化時,會將所有設備的disable_depth置為1,也就是disable狀態,driver初始化完畢後,要根據設備的時機狀態,調用這兩個函數,將RPM狀態設置正確。

extern void pm_runtime_allow(struct device dev);
extern void pm_runtime_forbid(struct device
dev);

RPM core通過sysfs(drivers/base/power/sysfs.c),為每個設備提供一個“/sys/devices/.../power/control”文件,通過該文件可讓用戶空間程式直接訪問device的RPM功能。這兩個函數用來控制是否開啟該功能(預設開啟)。

extern int pm_runtime_barrier(struct device *dev);

這名字起的!!!

由3.3的描述可知,很多RPM請求都是非同步的,這些請求會掛到一個名稱為“pm_wq”的工作隊列上,這個函數的目的,就是清空這個隊列,另外如果有resume請求,同步等待resume完成。好複雜,希望driver永遠不要用到它!!

extern int pm_generic_runtime_idle(struct device dev);
extern int pm_generic_runtime_suspend(struct device
dev);
extern int pm_generic_runtime_resume(struct device *dev);

幾個通用的函數,一般給subsystem的RPM driver使用,直接調用devie driver的相應的callback函數。

extern void pm_runtime_no_callbacks(struct device *dev);

告訴RPM core自己沒有回調函數,不用再調用了(或者調用都是成功的),真啰嗦。

extern void pm_runtime_irq_safe(struct device *dev);

告訴RPM core,如下函數可以在中斷上下文調用:
pm_runtime_idle()
pm_runtime_suspend()
pm_runtime_autosuspend()
pm_runtime_resume()
pm_runtime_get_sync()
pm_runtime_put_sync()
pm_runtime_put_sync_suspend()
pm_runtime_put_sync_autosuspend()

static inline int pm_runtime_idle(struct device dev)
static inline int pm_runtime_suspend(struct device
dev)
static inline int pm_runtime_resume(struct device *dev)

直接使用同步的方式,嘗試idle/suspend/resume設備,如果條件許可,就會執行相應的callback函數。driver儘量不要使用它們。

static inline int pm_request_idle(struct device dev)
static inline int pm_request_resume(struct device
dev)

和上面類似,不過調用方式為非同步。儘量不要使用它們。

static inline int pm_runtime_get(struct device dev)
static inline int pm_runtime_put(struct device
dev)

增加/減少設備的使用計數,並判斷是否為0,如果為零,嘗試調用設備的idle callback,如果不為零,嘗試調用設備的resume callback。

這兩個介面是RPM的正統介面啊,多多使用!

static inline int pm_runtime_get_sync(struct device dev)
static inline int pm_runtime_put_sync(struct device
dev)
static inline int pm_runtime_put_sync_suspend(struct device *dev)

和上面類似,只不過為同步調用。另外提供了一個可直接調用suspend的put介面,何必的!

static inline int pm_runtime_autosuspend(struct device dev)
static inline int pm_request_autosuspend(struct device
dev)
static inline int pm_runtime_put_autosuspend(struct device dev)
static inline int pm_runtime_put_sync_autosuspend(struct device
dev)

autosuspend相關介面。所謂的autosuspend,就是在suspend的基礎上,增加一個timer,還是覺得有點啰嗦。不說了。

static inline void pm_runtime_use_autosuspend(struct device dev)
static inline void pm_runtime_dont_use_autosuspend(struct device
dev)
extern void pm_runtime_set_autosuspend_delay(struct device dev, int delay);
extern unsigned long pm_runtime_autosuspend_expiration(struct device
dev);

控制是否使用autosuspend功能,以及設置/獲取autosuspend的超時值。

總結一下:總覺得這些API所提供的功能有些重疊,重疊的有點啰嗦。可能設計者為了提供更多的便利,可過渡的便利和自由,反而是一種束縛和煩惱!

5. runtime PM的使用步驟

覺得上面已經講了,就不再重覆了。


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

-Advertisement-
Play Games
更多相關文章
  • Aspose.PDF for .NET是一種高級PDF處理和解析API,用於在跨平臺應用程式中執行文檔管理和操作任務。API可以輕鬆用於生成,修改,轉換,渲染,保護和列印PDF文檔,而無需使用Adobe Acrobat。此外,還提供PDF壓縮選項,表格創建和操作,圖形和圖像功能,廣泛的超鏈接功能,印 ...
  • 1. 需求 在應用退出時(點擊右上角的關閉按鈕)彈出一個確認按鈕可以說是一個最常見的操作了,例如記事本的“你是否保存”: 但這個功能在UWP上居然有點小複雜。這篇文章將解釋如何實現這個功能。 2. CloseRequested 為了監視應用退出事件,我本來使用了 "CoreApplication.E ...
  • 前言 ASP.NET Core 後我們的配置變得更加輕量級了,在ASP.NET Core中,配置模型得到了顯著的擴展和增強,應用程式配置可以存儲在多環境變數配置中,appsettings.json用戶機密等 並可以通過應用程式中的相同界面輕鬆訪問,除此之外,ASP.NET中的新配置系統允許使用Opt ...
  • 這兩天簡單看了 sshfs 緩存相關的內容,下麵對一些好的鏈接進行索引,防止以後忘了: OpenSSH: Difference between internal-sftp and sftp-server:https://serverfault.com/questions/660160/openssh ...
  • 一.Htop的使用簡介 大家可能對top監控軟體比較熟悉,今天我為大家介紹另外一個監控軟體Htop,姑且稱之為top的增強版,相比top其有著很多自身的優勢。如下: 兩者相比起來,top比較繁瑣 預設支持圖形界面的滑鼠操作 可以橫向或縱向滾動瀏覽進程列表,以便看到所有的進程和完整的命令行 殺進程時不 ...
  • 這篇筆記是一篇安裝教程,沒有什麼實際的意義,僅為了記錄一下……距離上次弄這東西不知道多長時間了,以至於這次再次使用時很是生疏,於是就想著把過程記錄下來方便之後查看。 這裡不涉及VMware Workstation 15 Pro的安裝。僅為如何在其中安裝ubuntu以及實現與物理主機之間的複製粘貼等。 ...
  • vim vim的三種常見模式 一般模式、編輯模式、命令模式 # yum install -y vim-enhanced 安裝vim 一般模式 # vim 使用編輯文件時,預設進入該文件的一般模式 可以上下移動游標、刪除某個字元、刪除某行以及複製或粘貼一行或者多行 編輯模式 在一般模式下 i 在當前字 ...
  • 這篇文章主要介紹了Linux 使用grep篩選多個條件及grep常用過濾命令,需要的朋友可以參考下 cat log.txt | grep 條件; cat log.txt | grep 條件一 | grep 條件二; cat log.txt | grep 條件一 | grep 條件二 | grep 條 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...