為什么固態硬盤需要掉電保護?
作為企業級標準NVMe塊設備接口的固態硬盤固件,需要保證在存儲系統遇到異常掉電的情況下,或對盤進行熱插拔操作的情況下,保證盤上數據的可用性,正確性和一致性。
如果IDC機房忽然斷電,或者運維人員誤操作下了正在存儲數據的固態硬盤,發生數據丟失,可能會影響上層業務的運行,甚至導致客戶數據的丟失。后果可能會非常嚴重。
通常消費級的固態硬盤不需要添加掉電保護的功能,因為在消費級的PC上,通常不會存有特別關鍵的數據,異常掉電帶來的數據丟失后果通常不大,只要固件功能能夠保證盤重新上電保證可用性即可。所以在成本的考量下,沒有必要在筆記本電腦的硬盤上增加掉電保護功能。
固態硬盤掉電保護的特性?
可用性:
在存儲系統異常斷電之后,重新給服務器上電,固態硬盤在回復主機端CSTS.RDY信息后,所有功能可用。
正確性:
保存在盤上的數據,保證其數據不會因為異常掉電而被改變,成為錯誤數據。
一致性:
對于已經回復host Completion Queue Entry(CQE)的寫命令數據,固件應保證其信息的正確性。對于已發送未回復CQE的數據,盤可以是老數據,新數據,或者新老混合數據的狀態,其混合粒度與NVMe設計有關,在其粒度內(例如保證16KB粒度的數據一致性),數據可以全部都是新數據或者全部都是老數據,不可以出現其他數據。
掉電保護要保護什么數據?
首先,所有的存儲系統都有兩種基本的功能,即寫入時找到空余的位置和讀取時找到正確的對應數據位置。
那么為了維護這樣的功能,存儲系統中都至少有兩種數據即真正有效的 用戶數據(user data) 和管理用戶數據具體位置和狀態的 元數據(metadata) ,舉個例子,在單機文件系統ext中,文件的內容就是用戶數據,而super block和inode數據則為元數據。
掉電保護的目的是保證存儲系統的整個狀態在完成上電操作后,主機端看到的數據結構和數據內容在邏輯上完全和掉電前一致,即保證元數據和用戶數據都被固化在非易失存儲器上(NAND flash)。那在固態硬盤中,有哪些用戶數據和元數據需要保存?
1.用戶數據
NVMe協議中,對用戶數據的操作有很多種類,例如直接對數據操作的寫(write)命令,對用戶數據映射關系進行修改的Dataset Management(TRIM)命令和對整盤進行操作的Format及Sanitize命令等,這些操作的結果都是對主機端看到的邏輯數據塊內的內容進行修改,雖然其過程可能并非對NAND上的數據進行改動。
2.元數據
SSD固件中最重要的元數據為Logic address to physical address mapping table,邏輯地址到物理地址的映射表,我們常常稱之為L2P表,根據設計的不同主要有三種映射方式:塊映射(block mapping),頁映射(page mapping)和混合映射(hybird mapping)。
塊映射是以block為單位進行映射,在邏輯空間到物理空間的映射過程中,最小映射粒度是塊,從而極大的減少了映射表的大小,可以將其存放在SRAM中,假設當前盤的物理空間大小是5TB,其物理塊的大小是366 page * 2 plane * 3 (TLC)* 16KB = 34.3MB,如果以5TB/34.3MB可以獲得152854個映射關系,如果每個映射entry占用8B空間,則其映射表有1194KB,大約1.2MB的空間。
如果我們將其換成3840GB的標準塊設備映射表,每個映射entry依然以8B/4KB計算,其映射表約為7680MB。
當前,為了滿足企業級應用和云廠商對于IO latency的需求,企業級SSD通常選用頁映射。
通過計算我們可以得到,大容量SSD page mapping SSD對于DRAM的需求非常巨大,同時在異常掉電過程中,如何儲存這個巨大的元數據表,保證其上電后數據的一致性,
頁映射
塊映射
如何保護掉電時的數據?
保護掉電時的數據首先要有能量來源,作為電子設備,除了電源供能外,電池和電容都可以作為額外的供能來源。電池的問題是成本高,而電容的問題是電壓下降快,支撐時間短。當然為了可能在盤的生命周期內只會發生幾次的的異常掉電情況就在每塊盤上都安裝一塊電池,對于數據中心是非常不劃算的買賣,同時也非常的不環保,那么我就就要設計一下固態硬盤的固件,使其在異常掉電事件發生時,只需要毫秒級的時間,就可以完成用戶數據和元數據的整體固化,為下次上電過程提供充足的信息恢復數據。
如上圖所示,在一個SSD的PCB上,主要有如上圖所示的6種主要硬件,分別是與主機端連接的接口,現在主流的是PCIe接口,SSD控制器,PMIC電源控制芯片,DRAM,大量的NAND芯片以及電容。
當有主機的正常供電時,電源從PCIe接口供電,從PMIC將5V電壓轉化為Controller、DRAM和NAND使用的1.1V,2.5V和3.3V電壓,通過不同的通路送出,同時通過分壓電路將電容的電壓提升到一個相對較高的數值(因為電容存儲的電量和電容的電壓值平方成正比),將電量保存在電容中。
根據上面的公式,我們可以假設電容是1800uF,當充滿電時電容的電壓是36V,那么可以計算出其保存的電量大約是,1.166J。如果當前的最低可用電壓是5V,即電容電壓低于5V時SSD上有些器件已經開始不能工作了,則最低可用的電容能量是0.0225J,基本可以忽略不記。
假定SSD的標準功率是25W,最大瞬時功率是30W,那么1.166J的能量可以讓SSD在電容的供能情況下運行多久?
0.04664s,也就是46ms,如果是以30W來算也能撐38ms。
PMIC的選型?
通過上面的介紹,我們可以知道,為了實現異常斷電的保護,我們需要兩個硬件的器件,分別是PMIC和電容。PMIC的主要功能有:
電壓轉換
將供電的12V電壓轉換為SSD PCB板上器件使用的對應電壓。
電容充電
通過分壓電阻確定電容的電壓,并對電容進行充電。
異常掉電檢測
通過對接口電壓輸入的檢測,當接口的電壓下降到12V的90%以下時,產生中斷信號給到SSD controller,并開始使用電容供電
輸出電壓&電流實時監測
對于輸出的電路,進行電壓過流欠流,電流過流欠流的保護。
其他高級功能
例如現在許多PMIC芯片可以提供模擬主機端掉電的功能,通過內部切斷供電切換到電容供電,并在一定時間后檢測電容電壓的下降情況,從而確定電容的健康程度。
電容如何選型?
市面上的SSD主要有兩種主流的電容,一種是體積較大鋁電解電容,另一種是相對較為小但是相同容量占PCB面積較大的鉭電容:
鋁電容的好處是成本低,耐壓好,鉭電容的優點是容量密度大,穩定性好,壽命長,但是耐壓差,成本高。在現有的SSD使用壽命內(普遍在3-5年),鋁電容雖然容量較差,但是通過提高電容的電壓,可以很好的提高電容存儲電量從而彌補電容密度低的缺點,并且最最重要的是,鋁電容相對于鉭電容便宜太多,在大規模量產的SSD產品中,能夠節約非常大的成本。如果有機會拆一拆企業級的SSD,你會發現隨著時間的推移,越來越多的廠商選擇使用鋁電容解決熱插拔問題。
電容的容量如何確定?
在選定了電容類型之后,選擇電容的容量是下一步非常重要的工作,也是和SSD固件設計最密切相關的一步。我們上面提到的FTL表,在頁映射的模式下非常大,如果是我們現在主流使用的4TB盤,其映射表大小通常在3.84GB左右,按照我們前面的計算,盤需要在幾十毫秒的時間內,將3.84GB的數據全部寫入盤上,這是不現實的。
我們可以對SSD的后端(從主控到NAND的帶寬)進行計算,通常一個控制器有8-16個連接NAND芯片的channel,每個Channel上可連接16個NAND芯片來算,后端總共可以使能16*16=256個NAND Die同時做program操作,如果TLC的標準Program時間是2.5ms,一次寫入的數據量是2plane * 3 (TLC) *16KB = 96KB數據,則每秒鐘可以寫入9.375GB的數據(理論極限),這個數據量是在后端沒有任何損耗的情況下,并且按照平均program時間來算的,如果我們按照NAND的使用后期,program時間增長的情況來算,其帶寬最差可以減少到1/3的理論帶寬,在這種情況下,寫入3.84GB的數據也需要400ms以上的電容支撐時間,而我們計算出來的電容則只能支持40ms,這還是我們用了1800uF的大電容的情況下提供的時間。
如果保存整個FTL表的策略不可能,有沒有其他方法?
我們先來看一看FTL表上電恢復策略的歷史演進:
- 掃描全盤恢復
- Checkpoint的引入
- snapshot的引入
- P2L的引入
- Snapshot+Journal
- 單獨的Metadata stream
掃描全盤
這是一種最簡單樸素的上電恢復策略,其過程是在寫入數據的時候,對每個寫入單位,例如頁,加上一個表示寫入順序的sequence number或者叫version,這個數字用來表示當前頁的寫入順序。這種方法有一個前提,是用來排序的頁都在一個寫入流中,所以當我們有多個寫入的流,例如冷熱數據分離,GC和User data分離的情況,需要先區分數據的流,再區分頁的順序。
還有一種策略是sequence number策略的簡化版,叫block sequence number,這種策略借助了物理塊內寫入順序必然是連續的規則,只對block進行排序,block內的順序自然按照物理上的順序進行排序,但是這種情況很難應對多流同時寫入的順序排序問題。
sequence number一般被存放在物理頁的額外空間內,這個空間內和上電重建相關的主要信息就是sequence number和當前物理頁對應的邏輯塊的信息(LBA),通過這兩個信息,掃描全盤數據即可重新恢復L2P表的大部分信息。
按照上圖所示,如果下面的方框是NAND上真實的寫入順序,即先橫排從左向右寫滿再換行,那么其中有顏色的部分為最新的映射位置,因為按照寫入的順序,后面寫入的LBA是最新的,我們就可以恢復出來L2P表的映射關系。
掃描全盤的策略引入了一個問題,即TRIM命令的恢復問題。
這個問題的解決方法可以是保存一份Valid Page Bitmap,并在掉電的過程中將整體表保存下來,在SSD容量還比較小的時代,保存整個表還算是比較容易的事情,但是到了大容量時代,即使是VPB table,整個表的大小在4TB的情況下會是128MB。
除了TRIM信息沒有辦法恢復的問題,掃描全盤的策略還有另外一個問題,上電恢復時間太長,整個上電恢復除了讀取全盤的AUX區域信息外,還需要非常長時間的排序和DRAM訪問,通常這個時間都在分鐘級以上,在現有的云使用場景中,應用希望盡可能快的拉起服務,SSD和系統的啟動時間都在向著微秒級發展,分鐘級別的上電時間顯然是不夠用的。
Checkpoint
Checkpoint是存儲系統和文件系統里常用的概念,即將關鍵數據進行備份,用作系統的回滾。在SSD中,checkpoint作為一份完整的L2P表加VPB表,其信息通常周期性的被存放在固定的SSD固件內部block上,不與用戶數據混雜,同時一般最少保存2份最近的備份,以免一份數據損壞,不能恢復。
Checkpoint可以解決大部分的盤上信息的掃描,但是固件不可能每次寫入或者trim或者format時都做一次checkpoint,這樣寫放大就太大了,那如果在Checkpoint沒有cover的位置發生了掉電怎么辦?
假設我們的用戶數據按照順序寫到了如圖的位置,其中CP1的位置系統在其他地方做了checkpoint,后面更新的LBA1和LBA2的紅色數據沒有被包含在CP1中,那么通常的做法是,在做過最新的CP生效后,寫入的新數據的AUX中包含指向最近一份的CP的指針。
上電順序:
- 通過Sequence number掃描block,獲取最新的正在寫入的block信息
- 通過二分法查找到當前寫入的位置,通過最后一個頁的信息找到CP指針
- 從NAND中Load CP指針指向的位置,將整份CheckPoint加載到內存
- 通過CP對應的sequence number找到對應的user data寫入的sequence number
- 從該位置起啟動“全盤掃描”算法,將紅色部分全部更新到內存中的L2P表中
- 將Valid Page Bitmap生效到L2P表上
因為有了checkpoint,上電需要掃描的信息大大的減少,但TRIM對應的Valid Page Bitmap依然需要在掉電的過程中保存到NAND上。
Snapshot
所有掉電算法其實都能完成上電L2P表的重建,區別在于其速度和效率,同時魯棒性更好。這里的Snapshot方法其實原理上和checkpoint相同,但是將其拆分成多個部分分別固化在NAND上。
Snapshot方法首先將整個L2P表均勻的劃分成多個部分,例如圖上的L2P表只有8個LBA,被兩兩分成了4個部分,即CP1,CP2,CP3,和CP4。如果firmware計算下來,每一行user data寫入,觸發一次CP的寫入是最合算的,那么除了format全盤時寫入的一份空CP外,后面的每一個部分都由user data的寫入數量觸發。
上面的例子中,第一行user data寫完時,觸發了metadata流中的第二個CP1部分的寫入,寫入的是LBA1和LBA2的部分,寫入之后又到了第二行,則寫入了LBA3和LBA4的部分。
大家可以試想一下,這里寫入的LBA1和LBA2是指向哪個物理位置的?其實是指向第一對LBA1和LBA2的位置,即整個NAND分布的第一和第二個page,第二行的LBA1和LBA2并沒有被包含進來,所以在這種算法之下,每個LBA的部分要分別計算開始“全盤掃描”算法的位置,CP1的起始位置是第二行的開始,CP2的起始位置是第三行的開始,而CP3和CP4的起始位置是所有數據。
雖然在當前的例子中,看起來掃描全盤的數據變多了,但實際上在盤整體運行至穩態后,其掃描數據并不會比checkpoint方法多很多。其好處是每次CP運行時不會有太多數據需要寫,并且總體上可以很精準的控制user data和metadata的比例,從而更好的控制寫放大和latency。
上電流程:
- 通過block信息掃描到metadata block
- 找到最后一份完整的CP(當前例子是CP2 CP1 CP4 CP3)
- 通過每一份CP的sequence number找到需要“全盤掃描”的起始位置
- 開始全盤掃描更新L2P表
- 更新Valid Page Bitmap表到L2P表
這里同樣面臨的問題是TRIM的Valid Page Bitmap的問題,其數據依然需要在掉電過程中保存。總體上來說,從checkpoint到snapshot的優化,引入了固件邏輯復雜度,但是分片的方法可以避免很多在checkpoint過程中掉電的問題,以及避免了觸發Checkpoint的邏輯點的選擇,固定了user data和metadata的比例,更好的控制寫放大和時延。
P2L的引入
P2L是一種對L2P映射表的反向映射信息,其作用相當于在AUX中保存的LBA的信息的匯總。
前面提到“掃描全盤”算法中需要對每個頁都發起讀,這個過程是一個非常耗時的過程,而P2L可以將一整個block上的LBA信息進行集中,從而加速掃描全盤的過程。該信息作用更大的地方在于GC,在GC選中源Block時,其上數據的有效性也可以通過掃描其AUX并對比L2P表進行確認,但GC本身在穩態時成為最大數據量來源,此時能加速GC則對整個盤的性能提升非常有幫助。為了提升GC的效率,通常固件中都會引入P2L信息(物理位置到邏輯塊的反向映射信息)和Valid Bit Table信息(快速選源)。
回到我們的上電重建:
如果我們規定固件中每4行的用戶數據需要一個P2L的page,這個page中保存了上面4行中每個page對應的LBA的信息,這個信息被保存在這個block的最后一個一個page上,則我們可以得到的P2Lpage中的信息就像上圖中的矩陣一樣,P2L自己的LBA信息可以規定為一個特定的無效值,如0xFFFFFFFE等。
這樣當上電過程中,需要對該區域進行掃描全盤時,不需要再去讀取每一行的用戶數據,只需要將P2Lpage的信息讀取即可。如果該page發生讀錯誤,可以通過掃描全盤算法進行backup。
引入了P2L之后,不但能加速上電時間,還能起到元數據保護的作用,另外主要還是GC的速率得到非常大的提升。
Journal的引入
journal是什么?實際上Journal是用一個簡短的元數據記錄來保存L2P變化的信息,舉個例子,如果固件做了一筆寫,則L2P中的一個信息做了修改,則Journal中會記錄一次物理頁信息的替換,LBA3421中如果原來指向PPA 12,則新寫入的LBA3421會產生一個journal,類型是寫信息,記錄了LBA,新寫入的PPA信息(LBA和PPA pair)。這里如果是TRIM,則會記錄LBA的信息。
Journal會保存在DRAM或者SRAM中,當湊足一定的數量后(通常是16KB這樣的NAND可寫入單位),固化到metadata流中。
假設一個Journal page中剛好包含8個寫page的信息,則可以看到寫到4行用戶數據的時候,每個CP part前都有一個Journal page,如果Journal page出現在所有有效的CP part之前,則其已經被包含在后面的CP part中,可以不需要做update的操作。
Journal同時可以包括trim和write兩種操作的記錄,所以在這種設計下,Valid Page Bitmap是不一定需要的,當然這種Bitmap依然可以起到加速trim的作用,但是在邏輯正確性的層面,其并非是必須的。
其優點是顯而易見的,可以完全脫離User data來記錄metadata,但是也有缺點,即引入了Journal所占用的空間,同時不能完全代替P2L的作用,這里P2L的策略和Journal的策略各有優劣,可以根據設計SSD的目標用戶來做權衡,如果想提供更快的上電時間,Journal是更優的策略,如果想節省一定的OP(over provision),則P2L已經可以滿足需求。同時,Journal策略可能更匹配多流的設計其流間的上電順序更容易確定。
單獨的Metadata流
我們前面提到的Checkpoint,snapshot,journal信息都是獨立于user data的metadata,他們可以被單獨的放在一個流上(獨立的NAND block上),也可以和用戶數據混在一起。多種策略其實在之前的產品中都有實踐,并且都能交付。但是在云場景對QoS要求更加嚴格的今天,多流已經成為一個標配,而元數據不影響用戶數據的時延是一個基本需求,所以從協議到固件設計都提出了多流的概念。單獨的Metadata流可以引入更加復雜的GC機制,除了為用戶數據提供足夠的空間外,元數據也要保證數據失效后被擦除的速度。
如我們前面看到的snapshot+journal的方案,其數據就是被獨立放置在額外的NAND物理空間上。
根據以上提到的多種L2P元數據固化策略,掉電時需要保存的數據量也會有所不同。
首先是 write buffer中的數據 :
由于用戶對IO時延越來越苛刻的要求,現在多數的廠商將write buffer直接做在Controller的SRAM中,當數據從PCIe來的時候,直接fetch到SRAM的好處是速度非常快,在4KB的QS1寫測試中,Intel的TLC產品可以達到5-7us的寫時延。當固件在SRAM中收集到足夠的program數據時,開始直接向NAND傳輸數據。而在此之前,當數據完全寫入SRAM的時候,SSD固件就會向CQE中寫入信息,表示write命令完成,雖然此時數據還在SRAM中。
從圖上可以看出,當到達第二步的時候,host已經認為數據寫完成,如果此時發生掉電或者熱插拔現象,SRAM中的數據需要通過電容電量固化到NAND中。這個數據量有多少,各家各不相同,但是基本的計算思路應該是相同的,即需要足夠的數據量保證帶寬。
為什么寫入buffer的大小和帶寬有關?因為第一,controller和NAND的關系是1對多的關系,即分發的關系,每一個NAND Die是否在空閑狀態,是否出錯,是否正在read或者write是需要管理的,而在數據發送出去之前,是需要hold在buffer中的。第二,NAND的program是有一定出錯的概率的,如果發生的program錯誤,有兩種常見的處理方式:
- program過程中不釋放buffer,等到program返回正確狀態才釋放
- program開始即釋放buffer,如果program失敗按照read失敗處理
通常第一種方式更安全,所以企業級SSD一般會選擇第一種錯誤處理方式,而這種方式帶來的問題是需要hold更長時間的write buffer,需要更大的SRAM即更多的成本。在成本和性能之間,設計者需要做一個平衡。
所有在SRAM中的數據幾乎都要在掉電時保存到NAND上,假設我們有1MB的數據需要保存,就要將數據的信息計算到電容的電量中。
GC寫buffer的處理
通常GC的流程是,從GC源block中讀取一段有效數據,寫入到新的GC流block中,這個過程中有一些數據會緩存在SRAM或者DRAM的buffer中。這段數據是否需要在掉電過程中固化?答案是不需要,因為數據的來源來自于源block,這里需要注意的是,上電過程中的L2P表的重建過程會需要特別注意,不要將映射指向GC的目的block,這里有很多的細節需要設計,在不同的L2P表GC update策略中會有不同的做法。不過可以確認的是,在掉電過程中不需要對GC buffer做額外的處理即可。
元數據的處理:
我們前面提到了多種L2P表的固化方法,他們是為正常掉電設計的,異常掉電過程中,一般來說如果是消費級產品,不加電容的話,不論使用全盤掃描還是checkpoint,都可以達到讓SSD重新用起來的目標。只是write buffer中的數據不能保證恢復。
在企業級SSD中,雖然保底使用全盤掃描總能恢復當前L2P表,但是其速度會非常慢,所以通常我們把最后緩存在Metadata buffer中的數據,例如未寫入NAND的Journal,dirty Valid Page Bitmap等信息同時寫入NAND,其數據量通常不超過10MB。
我們假設我們設計的企業級產品有30MB的用戶數據buffer,10MB的元數據buffer需要寫入,那么需要多大的電容呢?
我們首先需要計算后端帶寬:
假設我們的controller有16個channel,每個channel上都掛有16個NAND Die,則共有256個獨立的物理die,也就是一個super page有256個獨立的physical page。die是NAND可以處理讀寫擦指令的最小邏輯單位,其內部是不能并行執行其他指令的(可以通過multiplane write等技術并行執行相同命令,但不可同時執行不同命令,因為其邏輯電路是per die設計的),所以后端帶寬如果全都執行program命令的話,在TLC的2plane NAND上,可以并行執行256*96KB的program,即24MB。當然這只是一個理論值,在實際運行過程中,還有其他各種損耗,Controller傳輸的階梯效應,整體SSD功耗限制等,最終能有效利用其中的80%就算是很好的帶寬利用率了。
假設總數據量是30MB+10MB=40MB,后端全die一次的program量是24MB,加上NAND上on going的program,總共需要所有Die在電容支持下program 3次完整的Tprog。
這里如果我們按照TLC NAND spec給出的最差情況,一個program完成的Max time是7ms,那么總共算下來需要3*7=21ms的總時間。前面我們已經計算過大概30w的SSD在1800uF的電容下可以運行多久,相信通過這個時間的比較大家也知道,只要電容運行時間超過program需要的時間,在理論上這個方案就是可行的。
但實際的過程中,我們還需要考慮很多其他的問題,例如如果NAND在做erase全盤的操作,這時掉電需要對NAND發送erase suspend命令并等到命令返回成功,如果盤上寫入數據出錯,是否需要重寫等問題。我們這里僅做掉電處理概念性的介紹,不做詳細的討論,如果您有興趣,可以聯系我私下討論這些掉電時的異常處理。
通常的處理是我們按照計算出來的需要時間的2-3倍來設計電容,這樣即使發生了異常,大概率還是能在電容涵蓋的時間內完成保護。
掉電的固件設計
完成了硬件的設計和參數的確定后,固件的設計其實主要還是配合硬件完成工作。首先是中斷設計。
掉電中斷設計
異常掉電時,PMIC會首先產生一個中斷信號脈沖傳遞給controller,當前絕大部分的controller使用了ARM核,使用的中斷可以選擇的是IRQ類型和FIQ類型。通常而言,掉電事件的優先級在整個系統中是最高的,但是由于SSD固件的特殊性,我所經歷過的一些項目都沒有選擇可打斷中斷的FIQ,而選擇了讓其他中斷運行完成后再進入的IRQ。這里的前提假設有兩個。第一,在設計其他中斷處理函數時,是否設計為可打斷的函數,通常我們認為中斷處理函數內容非常簡單,時間非常短,沒有同步行為,則會設計時不會考慮被打斷,資源也沒有鎖等處理機制。第二,在時間上,異常掉電時間是可以容忍微秒級別的延遲的,但是由于其對各種硬件資源的獨占性需求,希望在掉電過程中其他資源使用者能及時釋放資源。
掉電中斷通常設計為兩部分:
- 通知硬件中斷正在進行的PCIe傳輸,關閉其他中斷源
- 設置全局信號通知所有核上運行固件,發生掉電中斷
掉電固件設計
SSD上的資源釋放是掉電處理中的關鍵,SSD上的主要資源是Controller的CPU資源,DRAM資源(L2P表的訪問修改權限等),SRAM資源,NAND控制權和其他一些輔助器件的資源。
通常SSD的固件任務,除了IO之外都會設計為可被調度的task狀態機,例如GC任務就會被設計為多個階段,從觸發,到選源,到讀取,到寫入,到L2P更新,都會有自己獨立的狀態,同時獨立的狀態中會將數據拆分,例如數據可能會以256KB為單位進行操作等。這樣設計的好處是每個子任務都可以獨立完成,在任務的中間可以對全局的掉電狀態進行檢查,并且任務的間隙中對硬件和固件資源沒有占用,相當于每個子任務狀態機的一個狀態結束,都會釋放資源。這種設計方案的好處是,對于IO時延和異常掉電都有益處,但是設計復雜度非常高,并且要求設計者在設計任何其他task時,都要時刻考慮狀態機的設計,考慮掉電時的資源占用問題,給設計者增加了復雜度。
與之相應的,設計掉電流程時,設計者會盡可能的少占用runtime時的任務所使用的資源,例如掉電過程可以使用一個單獨reserved super block作為寫入的目的block,可以使用一段自定義的L2P表專門映射30MB的用戶數據等。
掉電過程中如果有足夠的電容,則可以將30MB+10MB的數據都寫入TLC block中,但是當數據量大,或者NAND寫入過慢時,又不能增加電容容量,可以將TLC(或QLC)NAND當做SLC來使用,尤其相比于QLC,SLC的后端帶寬可以顯著增加,從而使SSD在不增加硬件成本的情況下依然保證掉電數據不丟失。
如果使用TLC作為掉電保護的目標block:
- 收到全局的掉電處理狀態
- 完成當前核上的runtime task分片
- 切入掉電處理task
- 發送erase suspend指令處理正在erase的die
- 等待所有TLC上的program完成,將所有read指令進行abort
- 將所有buffer中的數據(30MB)寫入掉電處理block,補齊到RAID
- 等待寫狀態返回
- 將Dirty Journal及Dirty Valid Page Bitmap(10MB)寫入 目標block
- 等待寫狀態返回
- 等待電容釋放完電量
如果使用SLC作為掉電保護
- 收到全局的掉電處理狀態
- 完成當前核上的runtime task分片
- 切入掉電處理task
- 發送erase suspend和write、read abort指令(需NAND支持)
- 等待abort結束
- 將write buffer中數據寫入SLC block
- 等待寫狀態返回
- 將Dirty Journal及Dirty Valid Page Bitmap(10MB)寫入SLCblock
- 等待寫狀態返回
- 記錄SLCblock的位置到root信息中
可以看出,用戶數據要先于元數據信息寫入,這個也非常好理解,元數據必須包含新寫入的映射關系,否則上電恢復的過程中將丟掉這些信息。
上電恢復過程
上電的恢復過程與掉電的過程其實正好相反:
- 掃描所有的block以確定其所在的流和順序
- 從掉電保存信息的block上恢復write buffer中的信息和metadata信息
- 從metadata流中恢復L2P表
- 將write buffer中的信息重新寫入正常流
- 確認電容充電完成且電容狀態健康
- 回復host CSTS.RDY標志
上電的過程中需要注意,可以將上電整個過程分成三個部分,第一部分是Load信息的過程,第二部分是將掉電信息中的user data和metadata重新寫入正常路徑,第三部分是給host Ready flag,從而讓host可以開始發送新的IO,而盤也可以正常處理下一次異常掉電。
這里需要注意的是一種叫back to back的掉電,即在上電的第一部分和第二部分中再次發生異常掉電的處理。這種處理要求整個上電的過程是歸一化的,也就是說當再次發生掉電時,下次上電不需要占用額外的資源,可以統一處理。
掉電過程中的NAND錯誤
NAND是一種不穩定的介質,所以有可能在掉電保存信息的過程中,發生了NAND介質錯誤,或者在掉電后過了很長一段時間才上電,在此期間發生了介質錯誤,那么是否可以像正常的program或者read error一樣處理呢?
掉電處理的設計有一個通用性的原則就是,能在上電過程中處理的事情,就不要占用電容的電量在掉電過程處理,如果有額外的電量,可以處理。
如果在掉電過程中發現了program狀態出錯,這時的信息還在我們的buffer里,有兩種可供選擇的方法,第一是重新再次program,第二是等上電時像read error一樣用RAID來糾錯。選擇哪種方法要根據NAND出錯的概率來計算,如果program fail和UECC發生在同一個RAID set上的概率低于產品設計時的UECC允許概率,則可以將其當做read error來統一處理。
異常掉電的Debug
異常掉電的debug可以單獨的拿出來寫一篇文章,因為異常掉電的處理面臨非常多種的復雜情況,可以說盤上的固件在做任何Task時都有可能觸發異常掉電,所以要寫UT來測試異常掉電可能會非常復雜。而任何同步操作導致的資源釋放變慢,掉電處理task接管SSD時間晚等問題,都有可能造成SSD在電容電量之內沒能處理完所有數據,導致上電過程失敗。
首先,掉電過程在結束時需要一個明顯的標識來確定掉電task完整做完。
其次,掉電過程需要記錄足夠的log信息來供工程師debug使用。
最后,通過控制PMIC來進行假掉電事件的模擬可以抓到很多問題。
異常掉電處理的發展和展望
異常掉電是SSD固件中一個比較難設計好的feature,并且通過復雜的設計保證的很多功能在現實的應用場景中也很難遇到,一旦出現了掉電丟數據的情況,通常損失非常慘重,輕則丟失一些重要客戶,重則造成工程甚至人身安全的損失。隨著單盤的容量越來越大,L2P表的體積也越來越大,其上電時間因而也線性的增加了。
1.通過改變L2P表的結構來優化
L2P表是一個線性映射表,如果我們將其與文件或者對象相結合,是否可以通過樹或者其他形式進行直接對映射?
2.通過改變L2P表固化的介質
相變存儲器的引入和多層的L2P表merge設計可能會是一個固化L2P表的新思路,這里以Optane為代表的非易失存儲器可以用來存儲L2P表,這樣SSD中就徹底的解決了L2P表固化問題。
-
NAND
+關注
關注
16文章
1677瀏覽量
136022 -
熱插拔
+關注
關注
2文章
217瀏覽量
37223 -
SSD
+關注
關注
20文章
2851瀏覽量
117234 -
固態硬盤
+關注
關注
12文章
1452瀏覽量
57269 -
掉電保護
+關注
關注
2文章
25瀏覽量
15811
發布評論請先 登錄
相關推薦
評論