Linux ubi子系統原理分析

廣漠飄羽發表於2019-05-16

本文思維導圖總綱:
思維導圖總綱

綜述

關於ubi子系統,早已有比較正式的介紹,也提供非常形象的介紹ubi子系統ppt
國內的前輩 alloysystem 不辭辛勞為我們提供了部分正式介紹的中文譯文,以及找不到原文的轉載譯文

感謝這些資料讓我迅速入門ubi,進而整理出這博文

此博文是對上文的總結以及中文譯文的補充

在閱讀本文之前,建議先學習PPT中文譯文

概念對比

本節思維導圖總綱:
概念對比章節大綱

UBI Vs. MTD

UBI層次

上圖非常形象地描述了從Flash到UBIFS的各個層次。從上圖我們發現,MTD子系統在實際的Flash驅動之上 ,而UBI子系統則在MTD子系統之上。

要對比UBI和MTD的概念,我們不妨問自己一個問題,UBI和MTD兩個不同的層次的"使命"分別是什麼?

Flash驅動直接操作裝置,而MTD在Flash驅動之上,向上呈現統一的操作介面。所以MTD的"使命"是 遮蔽不同Flash的操作差異,向上提供統一的操作介面

UBI基於MTD,那麼UBI的目的是什麼呢? 在MTD上實現nand特性的管理邏輯,向上遮蔽nand的特性

nand有什麼特性呢?
(下文描述的 Nand驅動,是廣義上的操作Nand的集合,包括fs/ubi/mtd的層次,而非純粹的nand驅動)

1. 操作最小單元為頁(Page)/塊(Block)
    Nand不同於Nor,Nor可以以位元組為單位操作Flash,但Nand的讀寫最小單元是頁,擦除最小單元是塊。
    對常見的1Gbit的spinand而言,其頁大小2KBytes,塊大小是128K,表示一個塊有64個頁。
2. 擦除壽命限制
    Nand的物理性質決定了其每個塊都有擦除壽命的限制,SLC約10W次,MLC約5000次,TLC約1000次。
    因此,Nand驅動必須要做到磨損平衡。
    所謂磨損平衡,就是儘可能均衡使用每一個塊,既不讓一個塊太大壓力,也不讓一個塊太過空閒。
3. 位翻轉(bit-flips)
    Nand的物理性質使其可能會在使用、儲存過程中出現位翻轉的現象。
    例如,原始資料為0xFFFC,在儲存過程中Flash的資料卻變成了0xFFFF。
    所以要不在nand內部,要不在nand控制器都會存在ecc校正模組,在位翻轉後校正。
    然而,ecc並不是萬能的,其校正能力有限,所以驅動必須在位翻轉數量進一步變多之前把資料搬移到其他塊。
    萌新可能會有疑問,ecc都已經校正了為什麼還要搬移?因為ecc校正的是從Flash中讀到記憶體中的資料,
    而不是Flash本身儲存的資料,換句話說,此時Flash中的資料依然是錯的,如果不搬移,隨著翻轉的位數量積累,
    ecc就校正不了了,此時就相當於永久丟失正確資料了。
4. 存在壞塊(Bad Block)
    製作工藝和Nand本身的物理性質,導致在出廠和正常使用過程中都會產生壞塊。
    所謂壞塊,就是說這個塊已經損壞,不能再用於儲存資料,因此Nand驅動需要能自動跳過壞塊。

關於SLC/MLC/TLC的比較,可參考這篇部落格

UBI Vs. UBIFS

如果說UBI在MTD之上,在FS之下的中間層,用於抽象MTD遮蔽nand差異,那麼ubifs就是正兒八經的檔案系統。
ubifs是基於UBI子系統的檔案系統,實現檔案系統該有的所有基本功能,例如檔案的實現,例如日誌的實現。

這裡需要特別注意的是,ubifs跟jffs/yaffs相比,並不包含nand特性的管理,而是交由ubi來實現。

UBI Vs. Block Layer

Block Layer是適用於常見塊裝置的通用塊層,其特有的概念有bio、request、電梯演算法等,其典型的裝置有磁碟、SSD、mmc等。
而ubi基於mtd,雖然能模擬塊裝置,從本質上來講其並不是塊裝置。跟蹤UBIFS的IO操作,發現其IO操作並不經過通用塊裝置層。

UBI Vs. FTL

FTL(Flash Translation Layer)是一個"黑盒子",其跟UBI非常像,都是對nand特性進行封裝。

按我的理解,UBI跟FTL的目標不同,導致其實現上會有差異。UBI遮蔽nand特性是為了對接UBIFS,而FTL則是為了對接Block Layer。例如MMC其實也是封裝起來的Nand,只不過在MMC內部實現了FTL,經過FTL的轉換就能以塊裝置層的方法直接操作Nand,就能在mmc上格式化常見的塊檔案系統,例如EXT、VFAT等。

UBI Volume Vs. UBI Device

在UBI中還有兩個概念,分別是UBI卷(UBI Volume)和UBI裝置(UBI Device)。這兩個概念,我們可以這麼理解:

UBI裝置 相當於 磁碟裝置(sda,mmcblk0)
UBI卷 相當於 磁碟上對應分割槽(sda1,mmcblk0p1)

換句話說,UBI裝置是在MTD裝置上建立出來的裝置,而UBI卷則是從UBI裝置上劃分出來的分割槽, 從裝置節點名(ubi0)和卷名(ubi0_3)可以看出端倪。

上面的描述是為了方便理解UBI卷和UBI裝置,實際上UBI卷和分割槽的概念之間還是有差別的。

LEB Vs. PEB

在UBI子系統中,還有LEB和PEB的概念:

LEB指Logical Erase Block,即邏輯擦除塊,簡稱邏輯塊,表示邏輯卷中的一個塊
PEB指Physical Erase Block,即物理擦除塊,簡稱物理塊,表示物理Nand中的一個塊

為什麼要劃分邏輯塊和物理塊?從PPT中我們可以發現,物理塊和邏輯塊存在動態對映關係,且由於UBI頭的存在,邏輯塊一般會比物理塊小2個頁。

UBI子系統扮演的角色及其作用

本節思維導圖總綱:
UBI子系統角色與作用章節大綱

UBI子系統就是ubifs與mtd之間的中間層,其向下連線MTD裝置,實現nand特性的管理邏輯,向上呈現無壞塊的卷。

所以UBI子系統的作用,主要包括兩點:

1. 遮蔽nand特性(壞塊管理、磨損平衡、位翻轉)
2. UBI卷的實現

UBI卷的邏輯擦除塊(LEB)與物理擦除塊(PEB)之間是動態對映的,詳細可以看PPT

UBI相關的工具

本節思維導圖總綱:
UBI工具章節大綱

ubi的工具整合在包mtd-utils中,分別有以下工具及其作用

工具 作用
ubinfo 提供ubi裝置和卷的資訊
ubiattach 連結MTD裝置到UBI並且建立相應的UBI裝置
ubidetach ubiattach相反的操作,將MTD裝置從UBI裝置上去連結
ubimkvol 從UBI裝置上建立UBI卷
ubirmvol 從UBI裝置上刪除UBI卷
ubiblock 管理UBI捲上的block
ubiupdatevol 更新卷,例如OTA直接更新某個分割槽映象
ubicrc32 使用與ubi相同的基數計算檔案的crc32
ubinize 製作UBI映象
ubiformat 格式化空的Flash裝置,擦除Flash,儲存擦除計數,寫入UBI映象到Flash
mtdinfo 報告從系統中找到的UBI裝置的資訊

UBI頭部

本節思維導圖總綱:
UBI頭部章節大綱

UBI子系統需要往每個物理塊的開頭寫入兩個關鍵資料,這兩個關鍵資料就叫做UBI的頭部。

這兩個資料分別是 此物理塊擦除次數頭此物理塊的邏輯卷標記頭,也分別稱為 EC頭(Erase Count)VID頭(Volume IDentifier)

不管是EC頭還是VID頭,都是64Bytes,分別記錄與Nand塊的第一個頁和第二個頁。

以Q&A的形式介紹UBI頭:

Q:為什麼要這兩個頭?  
A:前文有說道,nand每個block有擦除壽命限制,因此需要記錄擦除次數,以實現磨損平衡,因此需要EC頭。此外,為了實現卷,必須記錄卷的邏輯塊與物理塊之間的對映關係,因此需要VID頭。

Q:為什麼不合併成1個頭?
A:兩者寫入的時機不一致,導致兩個頭必須分開寫入。EC頭在每次擦除後,必須馬上寫入以避免丟失,而VID頭只有在對映卷後才會寫入。

Q:不管是EC頭還是VID頭都是64B,為什麼要用2個Page?  
A:使用2個Page是對Nand來說的。前文有說過,Nor的讀寫最小單元是Byte,而Nand的讀寫最小單元是Page,因此對Nor可以只使用64Bytes,對Nand則必須使用2個Page,就是說,即使只有64Bytes有效資料,也需要用無效資料填充滿1個Page一次性寫入。

Q:在記錄擦除次數時掉電等,導致丟失實際擦除次數怎麼辦?  
A:取所有物理塊的擦除次數的平均數

關於UBI頭部的詳細介紹,可參考連結

UBI卷表(UBI Volume Table)

本節思維導圖總綱:
UBI卷表章節大綱

UBI子系統有個對使用者隱藏的特殊卷,叫層卷(layout volume),用來記錄卷表。我們可以把卷表等價於分割槽表,記錄各個卷的資訊。卷表大小為2個邏輯擦除塊,每個邏輯擦除塊記錄一份卷表,換句話說,UBI子系統為了保證卷表的可靠性,用2個邏輯記錄2分卷標資訊。

由於層卷的大小是固定的(2個邏輯塊),導致能儲存的卷資訊受限,所以最大支援的卷數量是隨著邏輯塊的大小改變而改變的,但最多不超過128個。

卷表中每個卷都儲存了什麼資訊?

struct ubi_vtbl_record {                                                         
        __be32  reserved_pebs; //物理塊數量
        __be32  alignment; //卷對齊
        __be32  data_pad;                                                        
        __u8    vol_type; //靜態卷or動態卷標識
        __u8    upd_marker; //更新標識
        __be16  name_len; //卷名長度
        __u8    name[UBI_VOL_NAME_MAX+1]; //卷名
        __u8    flags; //常用語自動重分配大小標記
        __u8    padding[23]; //保留區域
        __be32  crc; //卷資訊的CRC32校驗值
} __packed;

由這個結構體我們可以發現,卷資訊是被CRC32保護著的。比較有意思的有兩個成員:vol_type 和 flags

動態卷 & 靜態卷

vol_type成員標記了卷的型別,在建立卷時指定,可選動態卷和靜態卷。那麼什麼是動態卷?什麼又是靜態卷?

動態卷和靜態卷是兩種卷的型別,靜態卷標記此卷只讀,於是UBI子系統使用CRC32來校驗保護整個卷的資料,動態卷是可讀寫的卷,資料的完整性由檔案系統來保證。

關於靜態卷和動態卷的介紹,可參考連結

更新標識

flags成員常用於標識是否自動重分配大小。怎麼樣自動充分配大小呢?在首次執行時自動resize卷,讓卷大小覆蓋所有未使用的邏輯塊。

例如Flash大小是128M,在燒錄的映象中分配的所有卷加起來只用了100M,如果有卷被表示為autoresize,那麼在首次執行時,那個卷會自動擴大,把剩餘的28M囊括在內。

這個功能挺實用的,例如某個方案規劃中,除去rootfs、核心等必要空間外,把剩餘所有空間儘可能分配給使用者資料分割槽。
在開發過程中加了個應用,導致rootfs卷需要更大的空間,進而需要壓縮user_data卷的空間。
如果user_data空間是autoresize的,那麼user_data卷的空間就會自動壓縮。

再例如舊方案用的是128M的nand,後面升級為256M,即使使用相同的韌體,也不用擔心多出來的128M浪費掉了,
因為user_data卷自動擴大囊括多出來的128M。

需要注意的是,只允許1個卷設定autoresize標誌

關於更新標識更多的介紹,參考連結

壞塊標記

本節思維導圖總綱:
壞塊標記章節大綱

我們知道Nand的物理性質,導致在使用久之後會產生壞塊,那麼UBI是如何判斷好塊是否變成了壞塊的呢?

有兩個場景可能會標識壞塊,分別是寫失敗和擦除失敗。擦除失敗且返回是EIO,則直接標記壞塊。比較有意思的是寫失敗的判斷邏輯。

UBI子系統有後臺程式對疑似的壞塊進行"嚴刑拷打"(torturing),有5個步驟:

1. 擦除嫌疑壞塊
2. 讀取擦除後的值,判斷是否都是0xFF(擦除後理應全為0xFF)
3. 寫入特定資料
4. 讀取並校驗寫入的資料
5. 以不同的資料模式重複步驟1-4

如果"嚴刑拷打"出問題,則標記壞塊,詳細的實現邏輯可參考函式torture_peb()

原文可參考連結

UBI管理開銷

本節思維導圖總綱:
UBI管理開銷章節大綱

什麼是管理開銷呢?為了管理Nand的空間,實現磨損平衡、壞塊管理等等功能,必須佔用一部分空間來儲存關鍵資料,就好像檔案系統的後設資料。管理佔用的空間是不會呈現給使用者空間使用的,這空間即為管理的開銷。

對Nand來說,UBI管理開銷主要包含5個部分:

1. 層卷(卷表) : 佔用兩個物理塊
2. 磨損平衡:佔用一個物理塊
3. 邏輯塊修改原子操作:佔用一個物理塊
4. 壞塊管理:預設每1024個塊則預留20個塊(核心引數可配:CONFIG_MTD_UBI_BEB_LIMIT)
5. UBI頭:(物理塊總數*2)個頁

壞塊管理預留的塊數量,也可以理解為最大能容納多少個壞塊;再考慮壞塊的存在,管理開銷計算公式為:

UBI管理總開銷 = 特性開銷 + UBI頭開銷

其中:
壞塊預留 = MAX(壞塊數量,壞塊管理預留數量)
特性開銷 = (壞塊預留 + 1個磨損平衡開銷 + 1個原子操作開銷 + 2個層捲開銷) * 物理塊大小
UBI頭開銷 = 2 * 頁大小 * (含壞塊的總塊數 - 壞塊預留 - 1個磨損平衡開銷 + 1個原子操作開銷 + 2個層捲開銷)

也就是說:
UBI管理總開銷 = (壞塊預留 + 4) * 物理塊大小 + 2 * 頁大小 * (含壞塊的總塊數 - 壞塊預留 - 4)

以128M的江波龍的FS35ND01G-S1F1 SPI Nand為例,其規格為:

總大小:128M(1Gbit)
頁大小:2K bytes
塊大小:128K
塊數量:1024

假設是完全無壞塊的片子,其管理開銷為:

UBI管理開銷 = (20 + 4) * 128K + 2 * 2K * (1024 - 20 - 4) = 7072K ≈ 7M

詳細參考原文連結

相關文章