contributed by <Dennis40816
>
依據 N02:ideas 之要求觀摩 Linux2024q1 期末專題、錄影上和錄影下。
雖然先前有接觸過嵌入式系統使用者層級的程式,但對於其核心函式庫及其核心都缺乏認知,想藉由這次的專題累積驅動裝置或嵌入式 Linux 核心相關經驗。
在開始前,我認為需要好好釐清在裝置驅動中使用到的術語 - 虛擬化相關的名詞。參考 KVM: Linux 虛擬化基礎建設。
What is hypervisor
Hypervisor 是一個軟體,讓您可以用來在單一實體機器上執行多個虛擬機器。
從 AWS 的介紹網頁不難得知,hypervisor 身負管理虛擬化硬體資源的重要任務。而 hypervisor 又可以分為 Type 1 和 Type 2。其中 Type 1 不依賴作業系統,可以直接在裸機上面「存活」,相較下,Type 2 就比較嬌貴了,它只能運行在作業系統核心中,或者是 user space 中,一個有名的例子就是 VirtualBox。另一個 Type 2 的例子則是 QEMU + KVM 的組合啦,這裡後面會在說明。至於為什麼 Type 1 沒有例子,因為 Type 1 通常不會出現在大眾的視野,只會出現在特定場合,例如資料中心等要求高效能的地方。
What is KVM
KVM 是 Kernel-based Virtual Machine 的縮寫,可以讓運行 kernel 的 host machine 化身成 hypervisor,值得注意的是,KVM 聚焦在硬體虛擬化技術上,即在核心軟體層面支援 Intel VT-x、AMD-V、記憶體等硬體虛擬化技術的所需。而 KVM 沒囊括 I/O 裝置 (網路卡…) 的虛擬化以及虛擬機管理等功能。究其原因,是當時已經有一位厲害的老師傅 ── QEMU 了。
What is QEMU?
QEMU 是一款 user space 的 hypervisor,可以在沒有硬體虛擬化技術(e.g., AMD-V) 下完整模擬電腦,這使得它具備跨平台模擬的功能。不過它也能結合已經實作了硬體虛擬化功能的作業系統核心,例如上一節提到的 KVM,藉由硬體的虛擬化加速 (e.g., 藉由 AMD-V ) 來提昇虛擬機器的速度。當然,QEMU 也可以再有上一層的管理框架libvirt,但不是這邊的重點。總而言之,QEMU 是 hypervisor 很好的選擇。
What is VirtIO?
我們常常在驅動裝置中看到 Virtio-xxx,那 VirtIO 究竟是什麼呢?
VirtIO 是一種開放的標準界面,定義了在虛擬機器內的虛擬設備驅動程式(前端),應該如何和不同類型的虛擬設備(後端,例如由 QEMU 產生的虛擬網路卡) 溝通。沒錯,虛擬設備驅動程式是位於虛擬機器的 OS 內。
VirtIO 的起源可追朔至 QEMU 的社群,由於當時的虛擬機器也模擬 (emulate) 硬體,這造成一定程度的開銷,所以 VirtIO 提出, Rusty Russell 是該協定之所以成功的重要推手。
VirtIO 以準虛擬化 (paravirtualization) 的方式使虛擬機器的 IO 速度更接近原生機器上的速度。後來 Linux 也引入 VirtIO 成為實現虛擬設備的標準界面。
而虛擬設備的 driver 和虛擬設備後端傳遞方式是在共享記憶體以 Virtqueues
數據結構進行雙向通訊。舉個例子,當虛擬機器想要獲取網頁時,會通知虛擬網卡 driver,driver 透過 Virtqueue 傳封包通知 QEMU ( driver 認為的虛擬網卡),而 QEMU 接收到 Virtqueue 有新訊息後,就會請求他的宿主機上真正的網路卡去丟封包向網頁伺服器請求內容。
QEMU 模擬出來的設備通常被看作是 PCI (Peripheral Component Interconnect) 匯流排上的一個設備。(錄影下 - 2:45:34)
What is Virtqueue?
Virtqueue 可以分為 split 和 packed 兩個版本,是一種由虛擬裝置前後端共享的一種數據結構。split 類型中分成三種區域,Descriptor, Available, Used。其中,在 Descriptor area 中,紀錄了虛擬機器內的 driver 申請了多少緩衝區。Available area 由 driver 維護,driver 會負責申請緩衝區,並將可以用的緩衝區 index 紀錄在 available area 中,供虛擬裝置使用。Used area 則由虛擬裝置維護,會將使用過的緩衝區紀錄在該 area,同時也紀錄用了多少。而 packed 則將三個區域合而為一,以增加 cache 友善程度。
須注意,Virtqueue 並沒有合併 tx 和 rx 為同個 Virtqueue,相反應該會有 tx Virtqueue 和 rx Virtqueue。
結合上述的說明,對於如何在虛擬機器上實作 VirtIO driver 已有更深的認識。而該專題作者 jimmylu890303 也提到更多的細節,例如在 Linux 中,如果你要註冊的設備是 VirtIO based 的,則 vendor ID 需要設定成 0x1AF4。
還有當 Virtqueue 的 descriptor area flags field 的 VRING_DESC_F_WRITE
被設定時,tx Virtqueue 就只能由設備端來寫入。
而在驗證時則用到了 TUN (Guest OS driver) 以及 TAP (on Host OS)。在查看時,我看到作者使用 /dev/net/tun
在 Host OS 開啟一個 TAP,一開始還納悶為什麼是對 TUN 而不是 TAP,後來知道 Linux 要啟動 TUN 或 TAP 統一是對開啟的 /dev/net/tun
以 ioctl
旗標的方式 (e.g., .ifr_flags = IFF_TAP
) 去控制的。
可努力的方向
該專案基於 vwifi,開發了無線網路裝置驅動。
既然講到無線網路,便不能不提 cfg80211 在 Linux 中扮演的角色。
IEEE 802.11 是制定無線網路規定的委員會,所以無線網路協議通常以 802.11x 來描述不同版本。cfg80211 是 Linux 內核提供配置無線網路卡的 API,用來管理配置如頻道設定、加密方式等參數。一端連接著 Netlink (後面說明),一端連接著無線網路卡 driver (e.g., Realtek 等公司開發的 driver )。由於 cfg80211 提供了上層軟體一個統一的界面,所以不管下面的網路卡和網路卡驅動怎麼換,上層程式都不用再改了。
那我們平常在圖形使用者界面 (Graphical User Interface) 中設定 Wi-Fi 的調整,是怎麼被傳遞到內核中呢? 答案是透過 nl80211 提供的使用者界面。上層的軟體 (e.g., hostapd or wpa_supplicant) 會呼叫 nl80211 提供的 API,藉由 Netlink 這個通訊協議傳輸到 cfg80211 中。
其實在一開始,user space 不是透過 nl80211 提供的 API 搭配 cfg80211 與驅動程式交互,而是使用名為 wext 的接口,其底層使用 ioctl 直接與驅動程式交互。不過這種界面逐漸被淘汰,因為不適合傳輸大量數據,加上發起端須是 user space,當要反向傳輸缺乏相對機制,故 user space 須持續 polling,影響效能[1]。
而 Netlink 則採用 socket 並基於 RFC 3549,Netlink 協議的架構可用下圖描述[1]:
圖中,Routing subsystem 在使用者使用會被通知:
ip route add ...
而 Generic Netlink 連結的其中一個便是 cfg80211 模組。
而 cfg80211 再往下就是各個公司的無線網路卡驅動程式,可以區分為 Full-MAC driver 以及 Soft-MAC driver,這取決於 MAC 管理層 MLME (Media Access Control Layer Management Entity) 是在硬體實作還是在 driver 實作。Linux 提供 mac80211 框架,讓要在 driver 實作 MLME 者有統一的界面。
使用 Full-MAC driver 者,必須使用支援 Full-MAC WNIC (Wireless Network Interface Controller),WNIC 就是物理上無線網路控制器。
上述流程可以透過下圖說明[2]:
在該專案中,走的是左邊 Full-MAC 的路線。
接著讓我們來看看 Wi-Fi 領域中的專業術語:
其中,
回到該專題上,vwifi 透過 namespace 隔離虛擬的無線網路裝置,使他們能夠獨立產生 IP 並模擬 Wi-Fi 傳輸機制。
能努力的方向
Roaming?
Indexed Reference
[1] nl80211
[2] The Linux Process Journey — cfg80211 (Wireless Configuration)
Other Reference
What is V4L2?
V4L2 是 Video for Linux version 2。
在該專題中,作者 HenryChaing 在 Milk-V Duo,一異質多核平台上進行通訊機制驗證。異質多核系統指單一系統中整合了多種特性不同的 CPU 核心,例如 Arm 的 big.LITTLE 系列 (下圖為 A53 / A57 大小核 CPU ),參考自維基百科:
亦或是專案中使用的 Milk-V Duo SoC (System on Chip),具有兩顆 C906。分別是大核 C906B (RISC-V 64-bits) 和 C906L,前者支援最高 1G 的時脈,後者也高達 700M。不僅如此,也配置 0.5TOPS@INT8 的 TPU,而其售價居然只有台幣 300 多元 ! 對於能執行 Linux 還有實時作業系統 (RTOS) 硬體而言,300 多元非常划算。新的 Duo 256M 多添加了一個 Arm A53 的核心,可以進行 Arm 和 RISC-V 的架構切換,不過那是後話了,有興趣可以查看他們的官網 以及範例程式。
既然不同核心就能運行不同系統,而不同系統間為何要進行通訊呢? 我們可以試想一個情況,如果小核持續量測多個關鍵感測器數據,而使用者會透過無線網路定時來取回。然而,小核無法控制板載天線,透過無線的方式發送,而大核則具有完整的 TCP/IP 功能和無線網路模組,這樣的情況下,我們如果能將數據藉由大核發送往使用者,問題是不是就解決了呢? 所以當遇到數據蒐集、計算、收發,尤其是影音數據可能要對原始數據進行後處理和壓縮,若沒有一個手段通知計算能力更強的大核來做,就浪費了大核的能力了 ! 那如何進行異質核心間的通訊呢?
ㄧ般異構多核間的通訊可以分為以下幾種類別:
而作者選擇的 RPMsg 則屬於更高階的 Message Passing Framework (e.g., RPMsg),通常底層使用了其他通訊方式,其協議層如下[1]:
在 RPMsg Linux 的說明中 提到,RPMsg 基於 VirtIO,使用 RPMsg 需要曝露介面給 user space 務必要審慎考量,並最小化 user space 能夠編輯的內容,因為 RPMsg 相當於允許通常運行 RTOS 的受控端核心 (remote core) 存取主要核心的記憶體內容,未妥善設計可能導致安全疑慮。
因此,通常會讓使用者僅能存取特定的 RPMsg 設備(也稱為通道 (channal)),而 RPMsg 是使用文本名稱識別,這點在 open-amp repo 中可見。
下面的兩張圖呈現出 RPMsg 在 Linux 中運行時的 components 有哪些[1]:
對於 Duo 而言,Mailbox、Shared Memory、RPMsg (through RPMsg-Lite) 都是可選的選項。然而 Mailbox 僅提供一次 8 bytes 的傳輸 payload,雖然能使用 32-bit 的 address 和 32-bit 的長度告知共用記憶體位置的方法,終究沒有統一規格,而 RPMsg 可以很好地解決這個問題。
此外,HenryChaing 也提到可以透過 ADB 對 Milk-V IO,這樣可以很方便的傳輸檔案。
另外,Milk-V Duo 也具有非常活躍的開源社區:
我認為在 Milk-V Duo 還存在許多可嘗試的空間,例如:
註: 我還沒想出一個能與 Linux Kernel 深度整合,並實際應用於該硬體裝置上的題目。
Reference