怎么优化nvme固态硬盘安装win7

NVMe SSD是个啥?5张图保你弄明白SSD那些事_amd吧_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:312,745贴子:
NVMe SSD是个啥?5张图保你弄明白SSD那些事收藏
转cfan 还有5款PCI-E/M.2 SSD横评:代表目前SSD最快速度?
2个帖子合在一起(转PConline)
我们都知道SSD拥有比HDD更快的读写速度,但SATA总线标准却拖累了SSD性能的发挥。好消息是,如今越来越多的笔记本都配备了支持PCI-E总线标准的M.2插槽,这就让更高速的NVMe SSD有了用武之地。看到这里可能很多童鞋已经泛起了迷糊:啥叫PCI-E 3.0×4(标准名称为PCIExpress Gen 3×4)?NVMe又是什么?M.2接口不是SATA总线吗?所以接下来,我们就通过5张图片,全面了解一下和M.2相关的这些参数吧。
SATA SSD发展遇困境:SATA SSD遇到了发展瓶颈  如果在几年前的最强消费级磁盘性能评测之中,那么肯定有SATA SSD的身影。可惜,时过境迁,SATA的潜力已经被开发殆尽,已经成为了众所周知的瓶颈。  这是因为,SATA接口利用的是主板芯片组的DMI总线,目前最新的SATA III的带宽为6Gbps,换算理论最高速度为768MB/s,但由于物理与电气等种种原因,实际传输速度最高不过600MB/s。这速度对于机械硬盘、SATA II固态硬盘的胃口已经绰绰有余,但是当芯片的速度追过了接口的速度,那就注定被历史遗弃了。
NVMe规范、PCI-E通道促使SSD性能爆发NVMe规范将会代表未来的SSD  既然SATA接口的先天不足导致了如此的瓶颈,破而后立,就需要一个更高更快更强的标准取代SATA。目前PCI-E通道下的NVMe规范将会是接下来存储产品的发展趋势。  比起AHCI,NVMe可以大幅度降低控制器和软件接口部分的延迟;NVMe的队列数量也从AHCI的1,提高了64000,大幅提高了SSD的IOPS(每秒读写次数)性能;同时加入了自动功耗状态切换和动态能耗管理功能。  其中大家关注的性能方面,NVMe规范采用了四通道PCI-E,理论接口速度高达32Gbps!相比SATA III的6Gbps高了五倍多。瓶颈,还给了NAND、主控与缓存部分。
PCI-E/M.2 SSD来袭凶猛:PCI-E SSD成为了高速SSD的象征  NVMe规范,在PCI-E/M.2 SSD之中得到了实践。M.2接口是Intel主推的一种替代mSATA的新接口规范。相比于SATA接口,M.2接口在传输带宽、容量、轻薄特性等方面拥有更多的优势。  早在Intel 9系主板上就配有M.2接口,到现在100系列主板,几乎成为了标配。M.2接口也细分为两种:Socket2和Socket3。前者走SATA通道兼容PCIe ×2,最大理论读写速度分别达到700MB/s、500MB/s;而后者专为高性能存储设计,支持PCIe ×4,理论接口速度高达32Gb/s,超五倍于SATA接口。  至于PCI-E SSD,部分是M.2 SSD转接到带M.2接口的PCI-E转接卡中,部分是原生支持NVMe接口标准的PCI-E 3.0 x4通道接口。本质上,PCI-E/M.2 SSD的属于NVMe类型(SATA的为AHCI)。  未来,是属于NVMe的SSD。那么,未来到底有多期待?今天,我们就从NVMe规范的5款400-512GB参评PCI-E/M.2 SSD的横评找到答案!
5款400-512GB参评PCI-E/M.2 SSD:
5款横评产品主要核心参数及价格:横评的5款SSD作为目前高端NVMe规范的产品,容量达到了400-512GB。主控也各不相同,这也让我们有机会看到不同品牌主控的实力。其中四款为PCI-E SSD,价格达到了2K以上,相信这类顶级的SSD会满足我们的观看欲。至于剩下的一款为M.2接口,也需要1K6元的价格。不菲的价格带来怎样的跑分分数的视觉震撼?马上关注下面的测试成绩!
性能大比拼:谁是速度王者?CrystalDiskMark软件性能测试5款SSD测试成绩对比  测试小结:通过CDM测试可看出,本次参与横评的5款PCI-E/M.2 400-512GB SSD,在持续读取速度都破2300MB/s,其中影驰 铠甲战将 512GB达到了2798MB/s,十分两眼。在持续写入速度上,饥饿鲨RD 400 512GB的表现最好,达到了1638MB/s。每秒超过2GB的读取速度以及超过1GB的写入速度,让普通不过600MB/s的SATA III SSD相形见绌。5款SSD测试成绩对比  测试小结:5款SSD在CMD的4K随机读写性能测试中,读取性能差距并不是特别明显,不过写入速度的差别就比较大了,其中Intel 750 PCI-E的表现最好,达到了326MB/s,是SATA III SSD的三倍多。
AS SSD Benchmark软件测试5款SSD测试成绩对比5款SSD测试成绩对比  测试小结:通过AS SSD Benchmark测试,我们看到Intel 750 PCI-E可谓一枝独秀。其中4K读取速度达到了让人瞠目结舌的1417MB/s,886MB/s的写入速度,甚至比一些其他竞争产品的读取速度还要高,相当彪悍。其他四款SSD的表现不分上下,自成一档。  IOPS (Input/Output Operations Per Second),即每秒进行读写(I/O)操作的次数,IOPS值的高低直接影响SSD实际性能表现。从上述测试数据看出,Intel 750 PCI-E依然无人能敌,362767的读取IOPS相信在短时间内无人能破。
Anvil’s Storage Utilities 软件测试5款SSD测试成绩对比  测试小结:在Anvil’s Storage Utilities测试,5款SSD的读取速度都去到10000分的水平,比起SATA III SSD快了一倍。不过有意思的是,Intel 750 PCI-E的跑分并非最高,反而是饥饿鲨的SSD。
PCMark 7性能测试5款SSD测试成绩对比  测试小结:PCmark 7测试能够模拟日常软件的使用,能够充分考验SSD的实际性能,通常我们将5400分作为衡量旗舰SSD的标准。这5款SSD产品在PCmark 7测试中轻易突破这条线,同时最高的饥饿鲨达到了6038分,勇夺第一。不过,这里的PCI-E/M.2 SSD的分数并没有和SATA III的SSD拉开太远,毕竟是用分数来评判性能,权重方面可能未照顾到现在的新版SSD。PCMark 8性能测试:5款SSD测试成绩对比  测试小结:PCMark 8测试,分数十分平均,估计这款测试软件设置了上限值,并非线性增加。所以,大家参考下就好。
PConline评测室总结:如何选?PConline评测室总结:性能分析:  是不是看上述密密麻麻的测试成绩有些眼花缭乱呢?为了更加直观的了解本次参测的5款产品性能差距,我们以Intel 750 PCI-E 400GB为标准,将前面的所有成绩作对比,取其平均值得到下图:  Intel 750 PCI-E 400GB的表现最好,在直接性能表现的测试之中,Intel 750 PCI-E 400GB超高的传输速度以及IOPS让人刷新三观,冠绝全场。毫无疑问,目前综合性能最强的SSD属于Intel 750 PCI-E 400GB。至于第二名为饥饿鲨RD400 512GB,与其他SSD的性能不相伯仲。总体来看,目前顶尖的PCI-E/M.2 SSD综合性能为最强SATA III SSD性能的两倍。
价格上:  目前顶尖的PCI-E/M.2 SSD的价格都十分“任性”,最高达到了2749元。不过性能最好的Intel 750 PCI-E 400GB却只有2000元出头,估计是容量的原因吧。最便宜的为影驰的铠甲战将 512GB,并且性能位于中游,性价比不错。售后服务方面:  这几款产品提供了三到五年的质保。其中影驰提供了“3年包换”服务、索泰SONIX PCI-E 480G同样提供三年包换服务,而Intel 750 PCI-E 400GB、饥饿鲨RD400 512GB、浦科特 M8Pe 512GB均提供了五年保固服务,各有千秋。选购建议:  追求极速的用户NVMe的“亲父亲”Intel 750 PCI-E 400GB自然是你性能狂飙的不二之选,价格也比较给力,不过只有400GB容量。如果要追求性价比,那么影驰 铠甲战将 512GB是一个不错的选择。不过呢,参与测试的SSD都是桀傲不驯的野马,如果真的有选择困难症,不如选择自己的信仰吧!
附录:参评SSD介绍及平台概况参评SSD概况介绍:编辑点评:Intel 首款消费级NVMe传输规范的PCI-E 3.0 SSD--750系列,比AHCI更进的NVMe规范,1.25GB变态缓存空间,怪兽级性能表现,堪称当前最强性能的消费级SSD,可以说没有之一。
顶一下,很好的帖子
编辑点评:浦科特 M8Pe 512G 的Marvel 88SS1093 Eldora主控带来了崭新的表现,实测性能达到了顶尖的水平,并将SATA SSD远远抛诸脑后。LDPC纠错技术,更先进的纠错技术,带来了新亮点,与数据交换更加频繁的M.2 SSD无缝结合。此外,带散热外壳的设计,避免了NVMe规格的主控发热现象,加上不俗的颜值,值得提倡。
编辑点评:TOSHIBA-饥饿鲨RD400 SSD以自家主控及MLC NAND V-Flash作为卖点,还特别编写了自家NVMe驱动程序,对NVMe 协议的SSD进行更全面的优化,性能表现可用“惊艳”两字来形容。  另外,较之普通采用PHISON PS5007-E7 NVMe SSD,由于在驱动、主控算法等方面有所优化,饥饿鲨RD400 SSD不仅性能上更突出,而且稳定性,可靠性更出色,并提供长达5年的保固服务,再加上OCZ在国内不断优化的售后服务体系,购买饥饿鲨RD400系列,绝对是信心的保证。
编辑点评:索泰SONIX PCI-E SSD采用比较成熟的群联PCI-E 主控,搭配东芝15nm MLC闪存颗粒,全新的PCI-E 3.0x4接口及支持NVMe 1.2规范,让它的性能达到SATA SSD难以企及的高度,虽然与Intel 怪兽级SSD相比还有一定差距,但可以看到通过对比测试来看,有个别项目甚至超过Intel 750 PCI-E SSD,这种差距在不断缩小。
编辑点评:影驰铠甲战将M.2搭载搭载Marvell88SS9183-BNP2主控,双核主控芯片,性能卓越而稳定,大幅降低SSD功耗,是综合性能最佳的主控之一,配置原厂极速闪存,造就低功耗,高性能,稳定。“3年包换”服务,当用户的固态硬盘出现故障时即可“零等待”更换SSD,减少用户返修过程的长时间等待,这是影驰对用户的重视也是对产品的自信。
评测平台介绍与说明:本次横评选用在100系列主板中Z170搭载i7-6770K、以
2TB SSD作为主盘的平台上测试。测试部分包括理论性能与实际性能两部分,理论性能测试软件包括CrystalDiskMark、TxBENCH、PCMark 7、AS SSD Benchmark等。原文地址:
cfan的链接百度不让放 只能放IT之家的链接了(内容都是一样的):
接下来就是5个高速SSD评测了PCI-E/M.2 SSD为什么能火?  【PConline 横评】犹记得当年Windows 7 体验指数中,那深入民心的5.9分磁盘分数,在其余四项的7.9分面前,似乎在告诉我们机械硬盘注定被时代淘汰。势如破竹的固态硬盘,打破了温彻斯特结构的机械硬盘多年来在电脑硬件领域的统治。数倍于机械硬盘的固态硬盘传输性能,让我们重新学习了什么叫木桶效应。经历多年来的发展,硬盘存储市场,固态硬盘已成主导。  性能与容量齐飞,固态硬盘以让人瞠目结舌的速度发展着,无疑是电脑硬件的一个增长极。速度与激情,我们渴望性能的最大化,即使我们无法在身边的硬件之中得到顶尖的性能体验,但我们也能从各大尖端产品的评测之中得到满足。今天,我们就带来了最强PCI-E/M.2 固态硬盘性能的横向测评,以飨网友。
虽然这些产品的价格现在还难以接受 不过以后或许就能用上了 不过这也是因为这里评测的产品都是旗舰级的产品(不过还是消费级) 或许现在SATAIII接口的SSD还是比较适合平民 有高一点的要求的化 可以找一下比较便宜的NVMe SSD
加钱英特尔750 1.2t
登录百度帐号你的位置:
联系我们得到最新产品资讯及报价
转载:NVMe SSD如何用之应用端缓存加速
时间: && 来源:本站 && 点击:248次 &&
[摘要]本文将介绍iCAS软件加速方案,以及企事录实验室利用iCAS在加速数据库方面的测试分享。
iCAS,即Intel Cache Acceleration Software(英特尔缓存加速软件),是Intel公司推出的一款轻量级缓存加速软件,其安装在应用服务器之上,利用应用服务器上的SSD,对本地存储、外置SAN存储或者直连JBOD等进行加速。可运行于Windows和Linux两大类别的操作系统平台,在缓存加速方面支持包括Write-Through和Write-Back在内共4种模式。在对这4种模式进行简单介绍之前, 先给出名词解释:Cache 设备——在这里指相对性能更高、容量更小、价格更高的设备。&核心设备(core device)——在这里指相对性能更低、容量更大、价格更低的设备——用于数据的持久化存储。Cache设备和核心设备是相对的概念。譬如,以SATA SSD为cache设备, 则机械硬盘可以是核心设备;如果NVMe SSD为cache设备, 则SATA SSD或者机械硬盘都可以是核心设备;如果以Optane SSD为cache 设备, 则NAND的NVMe SSD、SATA SSD、机械硬盘都可以是核心设备。CAS软件可以工作在如下模式:Write-through模式。在该模式中,iCAS在往cache设备写数据的同时也向core设备同步写。该模式保障cache里的数据和core里的数据100%同步的,对读密集型操作更有效。Core里的数据对共用的其他服务器都有效。Write-back模式。iCAS先往cache里写数据。一旦写入cache 成功, 便对应用返回确认写成功。这个确认发生在数据被写入core设备之前。 然后,周期性地,cache里的数据会在既定的机会里写入core设备。该模式对读密集型操作和写密集型操作都同时提升。Write-around模式。只有当cache里已经有了数据块(block),iCAS才会将数据同时写入cache和core设备。和write-through类似,cache里的数据100%同步于core设备。然而, write-around进一步优化了cache, 避免“cache污染”。比如,应用要写入的数据在后续确定不会被经常重读,数据便不用写入cache而是仅仅直接写入core设备。这个模式对读密集型操作更有效。Pass-through模式。这个模式下,iCAS略过cache设备。当用户在真正启用缓存设备前,可以利用这个模式把所有准备被缓存的core device关联起来。一旦这些core设备被关联好以后, 用户可以动态地切换到他们所要的cache模式。Intel缓存加速软件提供的两种加速模式:Write-through和Write-back,均可对本地存储、SAN存储和直连存储进行缓存加速从上图可以看出,iCAS对数据读写操作都有加速作用:读缓存(Read Cache)和写缓冲(Write Buffer)。顾名思义,Write-through和Write-back两种模式的区别体现在写入操作上,读缓存方面工作原理没有区别:在接收到应用的读请求后,先在SSD缓存中查找,如果缓存命中,即读取iCAS中的缓存数据;如果缓存未命中,则从后端读取数据返回给应用,并将数据缓存到SSD中。在写缓冲中区别就体现出来了:如果使用Write-through模式,iCAS会将应用数据同时写入到SSD缓存和后端数据存储之后,再返回写操作成功。这种模式实际并不能加速写操作,因为其写延迟取决于最慢的返回操作(即后端存储);Write-back模式则能够加速写操作,如同前者一样,数据会同时写入到SSD缓存和HDD磁盘存储中,但SSD缓存写完即返回操作成功,磁盘存储将在后台继续写入,直到完毕。iCAS直接安装在应用服务器上,针对存储卷(Volume)进行加速,所以可以在多种应用场景下使用,比如以数据库为代表的块存储场景,文件存储场景,以及虚拟化环境。iCAS在虚拟化场景下的使用方式跟物理机上的使用方式没有不同,其并非安装在Hypervisor层,而是应用虚拟机之上,所以能够针对应用数据进行加速。目前iCAS支持Windows平台和Linux平台,包括主流使用的Red Hat Enterprise Linux(RHEL)、CentOS、SUSE Linux Enterprise Server(SLES)以及Ubuntu Server等等,可以应用在绝大多数的企业环境之中。同时其安装也很简单,稍有Linux基础的用户通过一两个命令行即可安装使用。企事录实验室验证测试在iCAS缓存加速软件的验证测试中,企事录实验室使用与之前评估Intel DC PSSD的同台服务器(型号为Intel R2208WFTZSX),配备双路Xeon Gold 6146处理器和256GB内存。将原来的DC P替换为希捷(Seagate)公司的Exos 7E8系列大容量硬盘驱动器(5400 RPM,4TB容量),并加装SAS/SATA RAID卡将4片4TB磁盘组建RAID 5;分别保留一块DC P4600和DC S4500作为iCAS缓存:企事录实验室用于评估iCAS缓存加速性能的测试方案,分别使用Intel DC P4600和Intel DC S4500作为缓存,希捷磁盘作为数据实际存储。在Oracle数据库环境下,评估其在不同用户数并发下的性能表现在Oracle数据库服务器上安装Intel iCAS软件,将DC P4600和DC S4500分别划出300GB大小分区,用作缓存,启用Write-Back模式,以加速写性能。构建测试数据库后写入120GB数据,分别测试其在4、8、16、64等不同并发数下,Oracle数据库的性能表现。在开启iCAS缓存加速测试功能之前,企事录实验室原本希望基于HDD存储进行测试,将测试结果作为参照组,以与开启iCAS缓存加速功能后的性能结果进行对比。但由于所采用的大容量磁盘存储在随机读写方面性能较弱,即使是4块HDD(RAID 5)并发的情况下,仍不能为Orace数据库提供满足其所需的性能,导致测试无法正常进行。虽然经过多次调试,仍无法获得结果,企事录实验室遂跳过这一阶段。将DC P4600和DC S4500作为缓存,考量其在Oracle数据库下的应用加速表现:在DC P4600作为缓存的情况下,Oracle数据库在64用户数并发下获得3.5万TPS性能;而使用DC S4500作为缓存则获得了2.8万TPS性能。在4/8/16/64逐步增加用户数的情况下,Oracle数据库性能随之增长,提升幅度可达20%需要注意的是,这一测试方案经过优化,iCAS在Write-back下对所有的读写数据都有完全的加速作用,并且测试方案的构建基于中小型应用在数据量不大的情况下,热点数据全部缓存SSD中,所以取得了最理想的加速效果。同时,Oracle数据库作为了一个完整应用,考量的是整个硬件平台/组件的综合性能,不能也无法排除内存对于Oracle数据库的加速影响。为了尽可能发挥Intel新一代硬件平台中六通道内存的性能,测试用数据库SGA人为设置为64GB大小,其对数据库性能有着不小的性能增益。并且,结合企事录实验室以往的测试结果发现,高主频的Xeon Gold 6146处理器(3.2GHz,可睿频到4.2GHz)在高性能的NVMe SSD环境下,其对数据库等强计算性能应用利好。企事录实验室建议在实验室条件下验证了Intel公司的iCAS缓存加速软件确实能够大幅提升应用服务器的性能,能够接近或者达到全闪存的性能,但这都是严格控制实验条件的情况下获得的。如果要将iCAS缓存加速软件用于实际应用环境,并获得较好的加速效果,那么用户需要注意:首先,用于iCAS的SSD应该是耐写型的,即写入性能较高、写入寿命较长。因为作为读缓存和写缓冲的设备,不仅要承受所有的写入数据量,缓存的热点数据(加速读取)也要根据时间的推移更新——这也意味着数据的写入。如果写入性能较差,则写缓冲对写入操作的加速不明显;如果写入寿命不够,则可能会提前耗尽(损坏)。好在,写入性能好的SSD,写入寿命通常也会比较长。在我们测试所用的SSD中,P4600是所在系列中相对耐写型的(与P4500相比)——论写入还比不了天赋异禀的P4800X,但胜在容量大、价格有优势。S4500不以写入见长,但限于条件我们手里没有写入特性更好的S4600。如果用户需要iCAS搭配SATA SSD使用,建议选择DC S4600。其次,计算不能成为瓶颈。较高性能的CPU,并配备适量的内存容量。企事录实验室认为,较高的计算性能是iCAS缓存加速软件发挥作用的前提条件,较多的内核(例如12核及以上)或更高主频的CPU都对数据库性能利好,相较而言,更高主频的CPU在数据库性能提升方面更为直接简单。同时,更大内存容量能够给数据库提供更多高速缓存,能够大幅提升性能。再者,应当注意SSD(缓存)和HDD(数据实际存储)的容量比例,更大容量的SSD缓存能够明显提升缓存命中率,尽可能减少对磁盘的读写操作,或者尽可能让磁盘处于顺序读写状态,都对性能利好。一般而言,SSD缓存与HDD实际存储的容量比例保持在1:10是一个较好的状态,如果SSD缓存的比例更高,则显著提升iCAS的性能表现。最后,SSD缓存的容量配比要结合应用实际,即分析应用产生的数据增长情况,与热点数据的生命周期。应用数据的增长情况将会直接影响写加速(Write Buffer)的效率,Write Buffer能够完全容纳增长的数据容量,将具有最佳的加速效果。同时,还要与热点数据的生命周期相结合,及频繁访问数据的时间范围,比如应用数据在一个月内有较高的访问频率,超过这个时间范围,其访问频率迅速下降,甚至不再访问,那么读缓存的容量至少要能完全容量一个月以上的数据存储空间需求。如果热点数据的生命周期为一个月的话,那么SSD缓存的容量应该为一个月的热点数据容量(Read Cache),还要给每天应用所产生的新数据留出足够的写入空间(Write Buffer)。考虑到预估与实际情况会存在一定的偏差,SSD缓存在满足上述条件的同时,再留有一定的剩余容量,在实际应用中将更有成效。&
深圳兄弟海洋信息技术有限公司
初创于2011年,
是一家专业从事服务器配件领域的现货商。
深圳市福田区
天安数码城创新科技广场二期西座803B
CPU SSDMEMORY源代码及NVMe协议版本
SPDK : spdk-17.07.1
DPDK :&dpdk-17.08
NVMe Spec: 1.2.1
基本分析方法
01 - 到官网http://www.spdk.io/下载
02 - 到官网http://www.dpdk.org/下载
03 - 创建目录nvme/src, 将spdk-17.07.1.tar.gz和dpdk-17.08.tar.xz解压缩到nvme/src中,然后用OpenGrok创建网页版的源代码树
04 - 阅读SPDK/NVMe驱动源代码, 同时参考NVMeDirect和Linux内核NVMe驱动
1. 识别NVMe固态硬盘的方法
NVMe SSD是一个PCIe设备, 那么怎么识别这种类型的设备? 有两种方法。
方法1: 通过Device ID + Vendor ID
方法2: 通过Class Code
在Linux内核NVMe驱动中,使用的是第一种方法。而在SPDK中,使用的是第二种方法。 上代码:
src/spdk-17.07.1/include/spdk/pci_ids.h
<span style="color: # /**
* PCI class code for NVMe devices.
* Base class code 01h: mass storage
* Subclass code 08h: non-volatile memory
* Programming interface 02h: NVM Express
<span style="color: # #define SPDK_PCI_CLASS_NVME
而Class Code (0x010802) 在NVMe Specification中的定义如下:
2. Hello World
开始学习一门新的语言或者开发套件的时候,总是离不开"Hello World"。 SPDK也不例外, 让我们从hello_world.c开始, 看一下main()是如何使用SPDK/NVMe驱动的API的,从而帮助我们发现使用NVMe SSDs的主逻辑,
src/spdk-17.07.1/examples/nvme/hello_world/hello_world.c
<span style="color: #6 int main(int argc, char **argv)
<span style="color: #7 {
<span style="color: #8
<span style="color: #9
struct spdk_env_
<span style="color: #0
<span style="color: #1
* SPDK relies on an abstraction around the local environment
* named env that handles memory allocation and PCI device operations.
* This library must be initialized first.
<span style="color: #7
spdk_env_opts_init(&opts);
<span style="color: #8
opts.name = "hello_world";
<span style="color: #9
opts.shm_id = <span style="color: #;
<span style="color: #0
spdk_env_init(&opts);
<span style="color: #1
<span style="color: #2
printf("Initializing NVMe Controllers\n");
<span style="color: #3
<span style="color: #4
* Start the SPDK NVMe enumeration process.
probe_cb will be called
for each NVMe controller found, giving our application a choice on
whether to attach to each controller.
attach_cb will then be
called for each controller after the SPDK NVMe driver has completed
initializing the controller we chose to attach.
<span style="color: #1
rc = spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, NULL);
<span style="color: #2
if (rc != <span style="color: #) {
<span style="color: #3
fprintf(stderr, "spdk_nvme_probe() failed\n");
<span style="color: #4
cleanup();
<span style="color: #5
return <span style="color: #;
<span style="color: #6
<span style="color: #7
<span style="color: #8
if (g_controllers == NULL) {
<span style="color: #9
fprintf(stderr, "no NVMe controllers found\n");
<span style="color: #0
cleanup();
<span style="color: #1
return <span style="color: #;
<span style="color: #2
<span style="color: #3
<span style="color: #4
printf("Initialization complete.\n");
<span style="color: #5
hello_world();
<span style="color: #6
cleanup();
<span style="color: #7
return <span style="color: #;
<span style="color: #8 }
main()的处理流程为:
spdk_env_opts_init(&opts);
spdk_env_init(&opts);
rc = spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, NULL);
hello_world();
cleanup();
001-002,spdk运行环境初始化
<span style="color: #ff,调用函数spdk_nvme_probe()主动发现NVMe SSDs设备。 显然, 接下来我们要分析的关键函数就是spdk_nvme_probe()。
004,调用函数hello_world()做简单的读写操作
005,调用函数cleanup()以释放内存资源,detach NVMe SSD设备等。
&在分析关键函数spdk_nvme_probe()之前,让我们先搞清楚两个问题:
问题1: 每一块NVMe固态硬盘里都一个控制器(Controller), 那么发现的所有NVMe固态硬盘(也就是NVMe Controllers)以什么方式组织在一起?
问题2: 每一块NVMe固态硬盘都可以划分为多个NameSpace (类似逻辑分区的概念), 那么这些NameSpace以什么方式组织在一起?
对有经验的C程序员来说,回答这两个问题很easy,那就是链表。我们的hello_world.c也是这么干的。看代码:
<span style="color: # struct ctrlr_entry {
<span style="color: #
struct spdk_nvme_ctrlr
<span style="color: #
struct ctrlr_entry
<span style="color: #
name[<span style="color: #24];
<span style="color: # };
<span style="color: #
<span style="color: # struct ns_entry {
<span style="color: #
struct spdk_nvme_ctrlr
<span style="color: #
struct spdk_nvme_ns
<span style="color: #
struct ns_entry
<span style="color: #
struct spdk_nvme_qpair
<span style="color: # };
<span style="color: #
<span style="color: # static struct ctrlr_entry *g_controllers = NULL;
<span style="color: # static struct ns_entry *g_namespaces = NULL;
g_controllers是管理所有NVMe固态硬盘(i.e. NVMe Controllers)的全局链表头。
g_namespaces是管理所有的namespaces的全局链表头。
那么,回到main()的L338-342, 就很好理解了。 因为g_controllers指针为NULL, 所以没有找到NVMe SSD盘啊,于是cleanup后退出。
<span style="color: #8
if (g_controllers == NULL) {
<span style="color: #9
fprintf(stderr, "no NVMe controllers found\n");
<span style="color: #0
cleanup();
<span style="color: #1
return <span style="color: #;
<span style="color: #2
现在看看hello_world.c是如何使用spdk_nvme_probe()的,
<span style="color: #1
rc = spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, NULL);
显然,probe_cb和attach_cb是两个callback函数, (其实还有remove_cb, L331未使用)
probe_cb: 当枚举到一个NVMe设备的时候被调用
attach_cb: 当一个NVMe设备已经被attach(挂接?)到一个用户态的NVMe 驱动的时候被调用
probe_cb, attach_cb以及remove_cb的相关定义如下:
src/spdk-17.07.1/include/spdk/nvme.h
<span style="color: #8 /**
* Callback for spdk_nvme_probe() enumeration.
* \param opts NVMe controller initialization options.
This structure will be populated with the
* default values on entry, and the user callback may update any options to request a different
The controller may not support all requested parameters, so the final values will be
* provided during the attach callback.
* \return true to attach to this device.
<span style="color: #7 typedef bool (*spdk_nvme_probe_cb)(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
<span style="color: #8
struct spdk_nvme_ctrlr_opts *opts);
<span style="color: #9
<span style="color: #0 /**
* Callback for spdk_nvme_probe() to report a device that has been attached to the userspace NVMe driver.
* \param opts NVMe controller initialization options that were actually used.
Options may differ
* from the requested options from the probe call depending on what the controller supports.
<span style="color: #6 typedef void (*spdk_nvme_attach_cb)(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
<span style="color: #7
struct spdk_nvme_ctrlr *ctrlr,
<span style="color: #8
const struct spdk_nvme_ctrlr_opts *opts);
<span style="color: #9
<span style="color: #0 /**
* Callback for spdk_nvme_probe() to report that a device attached to the userspace NVMe driver
* has been removed from the system.
* The controller will remain in a failed state (any new I/O submitted will fail).
* The controller must be detached from the userspace driver by calling spdk_nvme_detach()
* once the controller is no longer in use.
It is up to the library user to ensure that
* no other threads are using the controller before calling spdk_nvme_detach().
* \param ctrlr NVMe controller instance that was removed.
<span style="color: #2 typedef void (*spdk_nvme_remove_cb)(void *cb_ctx, struct spdk_nvme_ctrlr *ctrlr);
<span style="color: #3
<span style="color: #4 /**
* \brief Enumerate the bus indicated by the transport ID and attach the userspace NVMe driver
* to each device found if desired.
* \param trid The transport ID indicating which bus to enumerate. If the trtype is PCIe or trid is NULL,
* this will scan the local PCIe bus. If the trtype is RDMA, the traddr and trsvcid must point at the
* location of an NVMe-oF discovery service.
* \param cb_ctx Opaque value which will be passed back in cb_ctx parameter of the callbacks.
* \param probe_cb will be called once per NVMe device found in the system.
* \param attach_cb will be called for devices for which probe_cb returned true once that NVMe
* controller has been attached to the userspace driver.
* \param remove_cb will be called for devices that were attached in a previous spdk_nvme_probe()
* call but are no longer attached to the system. O specify NULL if removal notices are not
* desired.
* This function is not thread safe and should only be called from one thread at a time while no
* other threads are actively using any NVMe devices.
* If called from a secondary process, only devices that have been attached to the userspace driver
* in the primary process will be probed.
* If called more than once, only devices that are not already attached to the SPDK NVMe driver
* will be reported.
* To stop using the the controller and release its associated resources,
* call \ref spdk_nvme_detach with the spdk_nvme_ctrlr instance returned by this function.
<span style="color: #1 int spdk_nvme_probe(const struct spdk_nvme_transport_id *trid,
<span style="color: #2
void *cb_ctx,
<span style="color: #3
spdk_nvme_probe_cb probe_cb,
<span style="color: #4
spdk_nvme_attach_cb attach_cb,
<span style="color: #5
spdk_nvme_remove_cb remove_cb);
为了不被proce_cb, attach_cb, remove_cb带跑偏了,我们接下来看看结构体struct spdk_nvme_transport_id和spdk_nvme_probe()函数的主逻辑。
src/spdk-17.07.1/include/spdk/nvme.h
<span style="color: #2 /**
* NVMe transport identifier.
* This identifies a unique endpoint on an NVMe fabric.
* A string representation of a transport ID may be converted to this type using
* spdk_nvme_transport_id_parse().
<span style="color: #0 struct spdk_nvme_transport_id {
<span style="color: #1
* NVMe transport type.
<span style="color: #4
enum spdk_nvme_transport_
<span style="color: #5
<span style="color: #6
* Address family of the transport address.
* For PCIe, this value is ignored.
<span style="color: #1
enum spdk_nvmf_
<span style="color: #2
<span style="color: #3
* Transport address of the NVMe-oF endpoint. For transports which use IP
* addressing (e.g. RDMA), this should be an IP address. For PCIe, this
* can either be a zero length string (the whole bus) or a PCI address
* in the format DDDD:BB:DD.FF or DDDD.BB.DD.FF
<span style="color: #9
char traddr[SPDK_NVMF_TRADDR_MAX_LEN + <span style="color: #];
<span style="color: #0
<span style="color: #1
* Transport service id of the NVMe-oF endpoint.
For transports which use
* IP addressing (e.g. RDMA), this field shoud be the port number. For PCIe,
* this is always a zero length string.
<span style="color: #6
char trsvcid[SPDK_NVMF_TRSVCID_MAX_LEN + <span style="color: #];
<span style="color: #7
<span style="color: #8
* Subsystem NQN of the NVMe over Fabrics endpoint. May be a zero length string.
<span style="color: #1
char subnqn[SPDK_NVMF_NQN_MAX_LEN + <span style="color: #];
<span style="color: #2 };
对于NVMe over PCIe, 我们只需要关注"NVMe transport type"这一项:
<span style="color: #4
enum spdk_nvme_transport_
&而目前,支持两种传输类型, PCIe和RDMA。
<span style="color: #0 enum spdk_nvme_transport_type {
<span style="color: #1
* PCIe Transport (locally attached devices)
<span style="color: #4
SPDK_NVME_TRANSPORT_PCIE = <span style="color: #6,
<span style="color: #5
<span style="color: #6
* RDMA Transport (RoCE, iWARP, etc.)
<span style="color: #9
SPDK_NVME_TRANSPORT_RDMA = SPDK_NVMF_TRTYPE_RDMA,
<span style="color: #0 };
有关RDMA的问题,我们后面暂时不做讨论,因为我们目前主要关心NVMe over PCIe。
接下来看函数spdk_nvme_probe()的代码,
src/spdk-17.07.1/lib/nvme/nvme.c
<span style="color: #6 int
<span style="color: #7 spdk_nvme_probe(const struct spdk_nvme_transport_id *trid, void *cb_ctx,
<span style="color: #8
spdk_nvme_probe_cb probe_cb, spdk_nvme_attach_cb attach_cb,
<span style="color: #9
spdk_nvme_remove_cb remove_cb)
<span style="color: #0 {
<span style="color: #1
<span style="color: #2
struct spdk_nvme_ctrlr *
<span style="color: #3
struct spdk_nvme_transport_id trid_
<span style="color: #4
<span style="color: #5
rc = nvme_driver_init();
<span style="color: #6
if (rc != <span style="color: #) {
<span style="color: #7
<span style="color: #8
<span style="color: #9
<span style="color: #0
if (trid == NULL) {
<span style="color: #1
memset(&trid_pcie, <span style="color: #, sizeof(trid_pcie));
<span style="color: #2
trid_pcie.trtype = SPDK_NVME_TRANSPORT_PCIE;
<span style="color: #3
trid = &trid_
<span style="color: #4
<span style="color: #5
<span style="color: #6
if (!spdk_nvme_transport_available(trid-&trtype)) {
<span style="color: #7
SPDK_ERRLOG("NVMe trtype %u not available\n", trid-&trtype);
<span style="color: #8
return -<span style="color: #;
<span style="color: #9
<span style="color: #0
<span style="color: #1
nvme_robust_mutex_lock(&g_spdk_nvme_driver-&lock);
<span style="color: #2
<span style="color: #3
nvme_transport_ctrlr_scan(trid, cb_ctx, probe_cb, remove_cb);
<span style="color: #4
<span style="color: #5
if (!spdk_process_is_primary()) {
<span style="color: #6
TAILQ_FOREACH(ctrlr, &g_spdk_nvme_driver-&attached_ctrlrs, tailq) {
<span style="color: #7
nvme_ctrlr_proc_get_ref(ctrlr);
<span style="color: #8
<span style="color: #9
* Unlock while calling attach_cb() so the user can call other functions
that may take the driver lock, like nvme_detach().
<span style="color: #3
nvme_robust_mutex_unlock(&g_spdk_nvme_driver-&lock);
<span style="color: #4
attach_cb(cb_ctx, &ctrlr-&trid, ctrlr, &ctrlr-&opts);
<span style="color: #5
nvme_robust_mutex_lock(&g_spdk_nvme_driver-&lock);
<span style="color: #6
<span style="color: #7
<span style="color: #8
nvme_robust_mutex_unlock(&g_spdk_nvme_driver-&lock);
<span style="color: #9
return <span style="color: #;
<span style="color: #0
<span style="color: #1
<span style="color: #2
nvme_robust_mutex_unlock(&g_spdk_nvme_driver-&lock);
<span style="color: #3
* Keep going even if one or more nvme_attach() calls failed,
but maintain the value of rc to signal errors when we return.
<span style="color: #7
<span style="color: #8
rc = nvme_init_controllers(cb_ctx, attach_cb);
<span style="color: #9
<span style="color: #0
<span style="color: #1 }
spdk_nvme_probe()的处理流程为:
rc = nvme_driver_init();
002 410-414: set trid if it is NULL
check NVMe trtype via spdk_nvme_transport_available(trid-&trtype)
nvme_transport_ctrlr_scan(trid, cb_ctx, probe_cb, remove_cb);
check spdk process is primary, if not, do something at L426-440
rc = nvme_init_controllers(cb_ctx, attach_cb);
接下来,让我们看看函数nvme_transport_ctrlr_scan(),
<span style="color: #3
nvme_transport_ctrlr_scan(trid, cb_ctx, probe_cb, remove_cb);
/* src/spdk-17.07.1/lib/nvme/nvme_transport.c#92 */
<span style="color: # int
<span style="color: # nvme_transport_ctrlr_scan(const struct spdk_nvme_transport_id *trid,
<span style="color: #
void *cb_ctx,
<span style="color: #
spdk_nvme_probe_cb probe_cb,
<span style="color: #
spdk_nvme_remove_cb remove_cb)
<span style="color: # {
<span style="color: #
NVME_TRANSPORT_CALL(trid-&trtype, ctrlr_scan, (trid, cb_ctx, probe_cb, remove_cb));
<span style="color: # }
而宏NVME_TRANSPORT_CALL的定义是:
/* src/spdk-17.07.1/lib/nvme/nvme_transport.c#60 */
<span style="color: # #define TRANSPORT_PCIE(func_name, args)
case SPDK_NVME_TRANSPORT_PCIE: return nvme_pcie_ ## func_
<span style="color: # #define NVME_TRANSPORT_CALL(trtype, func_name, args)
<span style="color: #
<span style="color: #
switch (trtype) {
<span style="color: #
TRANSPORT_PCIE(func_name, args)
<span style="color: #
TRANSPORT_FABRICS_RDMA(func_name, args)
<span style="color: #
TRANSPORT_DEFAULT(trtype)
<span style="color: #
<span style="color: #
SPDK_UNREACHABLE();
<span style="color: #
} while (<span style="color: #)
于是, nvme_transport_ctrlr_scan()被转化为nvme_pcie_ctrlr_scan()调用(对NVMe over PCIe)来说,
/* src/spdk-17.07.1/lib/nvme/nvme_pcie.c#620 */
<span style="color: #9 int
<span style="color: #0 nvme_pcie_ctrlr_scan(const struct spdk_nvme_transport_id *trid,
<span style="color: #1
void *cb_ctx,
<span style="color: #2
spdk_nvme_probe_cb probe_cb,
<span style="color: #3
spdk_nvme_remove_cb remove_cb)
<span style="color: #4 {
<span style="color: #5
struct nvme_pcie_enum_ctx enum_ctx = {};
<span style="color: #6
<span style="color: #7
enum_ctx.probe_cb = probe_
<span style="color: #8
enum_ctx.cb_ctx = cb_
<span style="color: #9
<span style="color: #0
if (strlen(trid-&traddr) != <span style="color: #) {
<span style="color: #1
if (spdk_pci_addr_parse(&enum_ctx.pci_addr, trid-&traddr)) {
<span style="color: #2
return -<span style="color: #;
<span style="color: #3
<span style="color: #4
enum_ctx.has_pci_addr = true;
<span style="color: #5
<span style="color: #6
<span style="color: #7
if (hotplug_fd & <span style="color: #) {
<span style="color: #8
hotplug_fd = spdk_uevent_connect();
<span style="color: #9
if (hotplug_fd & <span style="color: #) {
<span style="color: #0
SPDK_TRACELOG(SPDK_TRACE_NVME, "Failed to open uevent netlink socket\n");
<span style="color: #1
<span style="color: #2
<span style="color: #3
_nvme_pcie_hotplug_monitor(cb_ctx, probe_cb, remove_cb);
<span style="color: #4
<span style="color: #5
<span style="color: #6
if (enum_ctx.has_pci_addr == false) {
<span style="color: #7
return spdk_pci_nvme_enumerate(pcie_nvme_enum_cb, &enum_ctx);
<span style="color: #8
<span style="color: #9
return spdk_pci_nvme_device_attach(pcie_nvme_enum_cb, &enum_ctx, &enum_ctx.pci_addr);
<span style="color: #0
<span style="color: #1 }
接下来重点看看L647对应的函数spck_pci_nvme_enumerate()就好,因为我们的目标是看明白是如何利用Class Code发现SSD设备的。
<span style="color: #7
return spdk_pci_nvme_enumerate(pcie_nvme_enum_cb, &enum_ctx);
/* src/spdk-17.07.1/lib/env_dpdk/pci_nvme.c */
<span style="color: # int
<span style="color: # spdk_pci_nvme_enumerate(spdk_pci_enum_cb enum_cb, void *enum_ctx)
<span style="color: # {
<span style="color: #
return spdk_pci_enumerate(&g_nvme_pci_drv, enum_cb, enum_ctx);
<span style="color: # }
注意: L84第一个参数为一个全局变量g_nvme_pci_drv的地址, ( 看到一个全局结构体变量总是令人兴奋的:-) )
/* src/spdk-17.07.1/lib/env_dpdk/pci_nvme.c */
<span style="color: # static struct rte_pci_id nvme_pci_driver_id[] = {
<span style="color: # #if RTE_VERSION &= RTE_VERSION_NUM(16, 7, 0, 1)
<span style="color: #
<span style="color: #
.class_id = SPDK_PCI_CLASS_NVME,
<span style="color: #
.vendor_id = PCI_ANY_ID,
<span style="color: #
.device_id = PCI_ANY_ID,
<span style="color: #
.subsystem_vendor_id = PCI_ANY_ID,
<span style="color: #
.subsystem_device_id = PCI_ANY_ID,
<span style="color: #
<span style="color: # #else
<span style="color: #
{RTE_PCI_DEVICE(<span style="color: #x8086, <span style="color: #x0953)},
<span style="color: # #endif
<span style="color: #
{ .vendor_id = <span style="color: #, /* sentinel */ },
<span style="color: # };
<span style="color: # static struct spdk_pci_enum_ctx g_nvme_pci_drv = {
<span style="color: #
.driver = {
<span style="color: #
.drv_flags
= RTE_PCI_DRV_NEED_MAPPING,
<span style="color: #
= nvme_pci_driver_id,
<span style="color: #
<span style="color: #
<span style="color: #
.cb_fn = NULL,
<span style="color: #
.cb_arg = NULL,
<span style="color: #
.mtx = PTHREAD_MUTEX_INITIALIZER,
<span style="color: #
.is_registered = false,
<span style="color: # };
啊哈! 终于跟Class Code (SPDK_PCI_CLASS_NVME=<span style="color: #x010802)扯上了关系。 全局变量g_nvme_pci_drv就是在L53行定义的,而g_nvme_pci_drv.driver.id_table则是在L38行定义的。
<span style="color: # static struct rte_pci_id nvme_pci_driver_id[] = {
<span style="color: #
.class_id = SPDK_PCI_CLASS_NVME,
<span style="color: # static struct spdk_pci_enum_ctx g_nvme_pci_drv = {
<span style="color: #
.driver = {
<span style="color: #
= nvme_pci_driver_id,
那么,我们只需要进一步深挖spdk_pci_enumerate()就可以找到SSD设备是如何被发现的了...
/* src/spdk-17.07.1/lib/env_dpdk/pci.c#150 */
<span style="color: #9 int
<span style="color: #0 spdk_pci_enumerate(struct spdk_pci_enum_ctx *ctx,
<span style="color: #1
spdk_pci_enum_cb enum_cb,
<span style="color: #2
void *enum_ctx)
<span style="color: #3 {
<span style="color: #8
<span style="color: #9 #if RTE_VERSION &= RTE_VERSION_NUM(17, 05, 0, 4)
<span style="color: #0
if (rte_pci_probe() != <span style="color: #) {
<span style="color: #1 #else
<span style="color: #2
if (rte_eal_pci_probe() != <span style="color: #) {
<span style="color: #3 #endif
<span style="color: #4
return <span style="color: #;
<span style="color: #5 }
省略了一些代码,我们接下来重点关注L170,
<span style="color: #0
if (rte_pci_probe() != <span style="color: #) {
从rte_pci_probe()函数的实现开始,我们就深入到DPDK的内部了,代码如下,
/* src/dpdk-17.08/lib/librte_eal/common/eal_common_pci.c#413 */
<span style="color: #7 /*
* Scan the content of the PCI bus, and call the probe() function for
* all registered drivers that have a matching entry in its id_table
* for discovered devices.
<span style="color: #2 int
<span style="color: #3 rte_pci_probe(void)
<span style="color: #4 {
<span style="color: #5
struct rte_pci_device *dev = NULL;
<span style="color: #6
size_t probed = <span style="color: #, failed = <span style="color: #;
<span style="color: #7
struct rte_devargs *
<span style="color: #8
int probe_all = <span style="color: #;
<span style="color: #9
int ret = <span style="color: #;
<span style="color: #0
<span style="color: #1
if (rte_pci_bus.bus.conf.scan_mode != RTE_BUS_SCAN_WHITELIST)
<span style="color: #2
probe_all = <span style="color: #;
<span style="color: #3
<span style="color: #4
FOREACH_DEVICE_ON_PCIBUS(dev) {
<span style="color: #5
<span style="color: #6
<span style="color: #7
devargs = dev-&device.
<span style="color: #8
/* probe all or only whitelisted devices */
<span style="color: #9
if (probe_all)
<span style="color: #0
ret = pci_probe_all_drivers(dev);
<span style="color: #1
else if (devargs != NULL &&
<span style="color: #2
devargs-&policy == RTE_DEV_WHITELISTED)
<span style="color: #3
ret = pci_probe_all_drivers(dev);
<span style="color: #4
if (ret & <span style="color: #) {
<span style="color: #5
RTE_LOG(ERR, EAL, "Requested device " PCI_PRI_FMT
<span style="color: #6
" cannot be used\n", dev-&addr.domain, dev-&addr.bus,
<span style="color: #7
dev-&addr.devid, dev-&addr.function);
<span style="color: #8
rte_errno =
<span style="color: #9
<span style="color: #0
ret = <span style="color: #;
<span style="color: #1
<span style="color: #2
<span style="color: #3
<span style="color: #4
return (probed && probed == failed) ? -<span style="color: # : <span style="color: #;
<span style="color: #5 }
L430是我们关注的重点,
<span style="color: #0
ret = pci_probe_all_drivers(dev);
函数pci_probe_all_drivers()的实现如下:
/* src/dpdk-17.08/lib/librte_eal/common/eal_common_pci.c#307 */
<span style="color: #1 /*
* If vendor/device ID match, call the probe() function of all
* registered driver for the given device. Return -1 if initialization
* failed, return 1 if no driver is found for this device.
<span style="color: #6 static int
<span style="color: #7 pci_probe_all_drivers(struct rte_pci_device *dev)
<span style="color: #8 {
<span style="color: #9
struct rte_pci_driver *dr = NULL;
<span style="color: #0
int rc = <span style="color: #;
<span style="color: #1
<span style="color: #2
if (dev == NULL)
<span style="color: #3
return -<span style="color: #;
<span style="color: #4
<span style="color: #5
/* Check if a driver is already loaded */
<span style="color: #6
if (dev-&driver != NULL)
<span style="color: #7
return <span style="color: #;
<span style="color: #8
<span style="color: #9
FOREACH_DRIVER_ON_PCIBUS(dr) {
<span style="color: #0
rc = rte_pci_probe_one_driver(dr, dev);
<span style="color: #1
if (rc & <span style="color: #)
<span style="color: #2
/* negative value is an error */
<span style="color: #3
return -<span style="color: #;
<span style="color: #4
if (rc & <span style="color: #)
<span style="color: #5
/* positive value means driver doesn't support it */
<span style="color: #6
<span style="color: #7
return <span style="color: #;
<span style="color: #8
<span style="color: #9
return <span style="color: #;
<span style="color: #0 }
L320是我们关注的重点,
<span style="color: #0
rc = rte_pci_probe_one_driver(dr, dev);
/* src/dpdk-17.08/lib/librte_eal/common/eal_common_pci.c#200 */
<span style="color: #5 /*
* If vendor/device ID match, call the probe() function of the
<span style="color: #9 static int
<span style="color: #0 rte_pci_probe_one_driver(struct rte_pci_driver *dr,
<span style="color: #1
struct rte_pci_device *dev)
<span style="color: #2 {
<span style="color: #3
<span style="color: #4
struct rte_pci_addr *
<span style="color: #5
<span style="color: #6
if ((dr == NULL) || (dev == NULL))
<span style="color: #7
return -EINVAL;
<span style="color: #8
<span style="color: #9
loc = &dev-&
<span style="color: #0
<span style="color: #1
/* The devic Check if driver supports it */
<span style="color: #2
if (!rte_pci_match(dr, dev))
<span style="color: #3
/* Match of device and driver failed */
<span style="color: #4
return <span style="color: #;
<span style="color: #5
<span style="color: #6
RTE_LOG(INFO, EAL, "PCI device "PCI_PRI_FMT" on NUMA socket %i\n",
<span style="color: #7
loc-&domain, loc-&bus, loc-&devid, loc-&function,
<span style="color: #8
dev-&device.numa_node);
<span style="color: #9
<span style="color: #0
/* no initialization when blacklisted, return without error */
<span style="color: #1
if (dev-&device.devargs != NULL &&
<span style="color: #2
dev-&device.devargs-&policy ==
<span style="color: #3
RTE_DEV_BLACKLISTED) {
<span style="color: #4
RTE_LOG(INFO, EAL, "
Device is blacklisted, not"
<span style="color: #5
" initializing\n");
<span style="color: #6
return <span style="color: #;
<span style="color: #7
<span style="color: #8
<span style="color: #9
if (dev-&device.numa_node & <span style="color: #) {
<span style="color: #0
RTE_LOG(WARNING, EAL, "
Invalid NUMA socket, default to 0\n");
<span style="color: #1
dev-&device.numa_node = <span style="color: #;
<span style="color: #2
<span style="color: #3
<span style="color: #4
RTE_LOG(INFO, EAL, "
probe driver: %x:%x %s\n", dev-&id.vendor_id,
<span style="color: #5
dev-&id.device_id, dr-&driver.name);
<span style="color: #6
<span style="color: #7
if (dr-&drv_flags & RTE_PCI_DRV_NEED_MAPPING) {
<span style="color: #8
/* map resources for devices that use igb_uio */
<span style="color: #9
ret = rte_pci_map_device(dev);
<span style="color: #0
if (ret != <span style="color: #)
<span style="color: #1
<span style="color: #2
<span style="color: #3
<span style="color: #4
/* reference driver structure */
<span style="color: #5
dev-&driver =
<span style="color: #6
dev-&device.driver = &dr-&
<span style="color: #7
<span style="color: #8
/* call the driver probe() function */
<span style="color: #9
ret = dr-&probe(dr, dev);
<span style="color: #0
if (ret) {
<span style="color: #1
dev-&driver = NULL;
<span style="color: #2
dev-&device.driver = NULL;
<span style="color: #3
if ((dr-&drv_flags & RTE_PCI_DRV_NEED_MAPPING) &&
<span style="color: #4
/* Don't unmap if device is unsupported and
* driver needs mapped resources.
<span style="color: #7
!(ret & <span style="color: # &&
<span style="color: #8
(dr-&drv_flags & RTE_PCI_DRV_KEEP_MAPPED_RES)))
<span style="color: #9
rte_pci_unmap_device(dev);
<span style="color: #0
<span style="color: #1
<span style="color: #2
<span style="color: #3 }
L212是我们关注的重点,
<span style="color: #2
if (!rte_pci_match(dr, dev))
而rte_pci_match()的实现如下,
/* src/dpdk-17.08/lib/librte_eal/common/eal_common_pci.c#163 */
<span style="color: #1 /*
* Match the PCI Driver and Device using the ID Table
* @param pci_drv
PCI driver from which ID table would be extracted
* @param pci_dev
PCI device to match against the driver
1 for successful match
0 for unsuccessful match
<span style="color: #2 static int
<span style="color: #3 rte_pci_match(const struct rte_pci_driver *pci_drv,
<span style="color: #4
const struct rte_pci_device *pci_dev)
<span style="color: #5 {
<span style="color: #6
const struct rte_pci_id *id_
<span style="color: #7
<span style="color: #8
for (id_table = pci_drv-&id_ id_table-&vendor_id != <span style="color: #;
<span style="color: #9
id_table++) {
<span style="color: #0
/* check if device's identifiers match the driver's ones */
<span style="color: #1
if (id_table-&vendor_id != pci_dev-&id.vendor_id &&
<span style="color: #2
id_table-&vendor_id != PCI_ANY_ID)
<span style="color: #3
<span style="color: #4
if (id_table-&device_id != pci_dev-&id.device_id &&
<span style="color: #5
id_table-&device_id != PCI_ANY_ID)
<span style="color: #6
<span style="color: #7
if (id_table-&subsystem_vendor_id !=
<span style="color: #8
pci_dev-&id.subsystem_vendor_id &&
<span style="color: #9
id_table-&subsystem_vendor_id != PCI_ANY_ID)
<span style="color: #0
<span style="color: #1
if (id_table-&subsystem_device_id !=
<span style="color: #2
pci_dev-&id.subsystem_device_id &&
<span style="color: #3
id_table-&subsystem_device_id != PCI_ANY_ID)
<span style="color: #4
<span style="color: #5
if (id_table-&class_id != pci_dev-&id.class_id &&
<span style="color: #6
id_table-&class_id != RTE_CLASS_ANY_ID)
<span style="color: #7
<span style="color: #8
<span style="color: #9
return <span style="color: #;
<span style="color: #0
<span style="color: #1
<span style="color: #2
return <span style="color: #;
<span style="color: #3 }
看到这里,我们终于找到了SSD设备是如何被发现的, L185-187是我们最希望看到的三行代码:
<span style="color: #5
if (id_table-&class_id != pci_dev-&id.class_id &&
<span style="color: #6
id_table-&class_id != RTE_CLASS_ANY_ID)
<span style="color: #7
而结构体struct rte_pci_driver和struct rte_pci_device的定义为:
/* src/dpdk-17.08/lib/librte_eal/common/include/rte_pci.h#100 */
<span style="color: #
* A structure describing an ID for a PCI driver. Each driver provides a
* table of these IDs for each device that it supports.
<span style="color: #0 struct rte_pci_id {
<span style="color: #1
uint32_t class_
/**& Class ID (class, subclass, pi) or RTE_CLASS_ANY_ID. */
<span style="color: #2
uint16_t vendor_
/**& Vendor ID or PCI_ANY_ID. */
<span style="color: #3
uint16_t device_
/**& Device ID or PCI_ANY_ID. */
<span style="color: #4
uint16_t subsystem_vendor_ /**& Subsystem vendor ID or PCI_ANY_ID. */
<span style="color: #5
uint16_t subsystem_device_ /**& Subsystem device ID or PCI_ANY_ID. */
<span style="color: #6 };
/* src/dpdk-17.08/lib/librte_eal/common/include/rte_pci.h#120 */
<span style="color: #0 /**
* A structure describing a PCI device.
<span style="color: #3 struct rte_pci_device {
<span style="color: #4
TAILQ_ENTRY(rte_pci_device)
/**& Next probed PCI device. */
<span style="color: #5
struct rte_
/**& Inherit core device */
<span style="color: #6
struct rte_pci_
/**& PCI location. */
<span style="color: #7
struct rte_pci_
/**& PCI ID. */
<span style="color: #8
struct rte_mem_resource mem_resource[PCI_MAX_RESOURCE];
<span style="color: #9
/**& PCI Memory Resource */
<span style="color: #0
struct rte_intr_handle intr_
/**& Interrupt handle */
<span style="color: #1
struct rte_pci_driver *
/**& Associated driver */
<span style="color: #2
uint16_t max_
/**& sriov enable if not zero */
<span style="color: #3
enum rte_kernel_
/**& Kernel driver passthrough */
<span style="color: #4
char name[PCI_PRI_STR_SIZE+<span style="color: #];
/**& PCI location (ASCII) */
<span style="color: #5 };
/* src/dpdk-17.08/lib/librte_eal/common/include/rte_pci.h#178 */
<span style="color: #5 /**
* A structure describing a PCI driver.
<span style="color: #8 struct rte_pci_driver {
<span style="color: #9
TAILQ_ENTRY(rte_pci_driver)
/**& Next in list. */
<span style="color: #0
struct rte_
/**& Inherit core driver. */
<span style="color: #1
struct rte_pci_bus *
/**& PCI bus reference. */
<span style="color: #2
pci_probe_t *
/**& Device Probe function. */
<span style="color: #3
pci_remove_t *
/**& Device Remove function. */
<span style="color: #4
const struct rte_pci_id *id_
/**& ID table, NULL terminated. */
<span style="color: #5
uint32_t drv_
/**& Flags contolling handling of device. */
<span style="color: #6 };
到此为止,我们可以对SSD设备发现做如下总结:
01 - 使用Class Code (0x010802)作为SSD设备发现的依据
02 - 发现SSD设备的时候,从SPDK进入到DPDK中,函数调用栈为:
<span style="color: # hello_word.c
<span style="color: # -& main()
<span style="color: # --& spdk_nvme_probe()
<span style="color: # ---& nvme_transport_ctrlr_scan()
<span style="color: # ----& nvme_pcie_ctrlr_scan()
<span style="color: # -----& spdk_pci_nvme_enumerate()
<span style="color: # ------& spdk_pci_enumerate(&g_nvme_pci_drv, ...)
=========================================================================
<span style="color: # -------& rte_pci_probe()
<span style="color: # --------& pci_probe_all_drivers()
<span style="color: # ---------& rte_pci_probe_one_driver()
<span style="color: # ----------& rte_pci_match()
03 - DPDK中环境抽象层(EAL: Environment Abstraction Layer)的函数rte_pci_match()是发现SSD设备的关键。
04 - DPDK的EAL在DPDK架构中所处的位置,如下图所示:
Your greatness is measured by your horizons. | 你的成就是由你的眼界来衡量的。
阅读(...) 评论()

我要回帖

更多关于 nvme固态硬盘分区 的文章

 

随机推荐