主要想找到从nvme设备到vfio驱动的代码路径 〔spdklibnvmenvmepcie。c〕 nvmepciectrlrscan(structspdknvmeprobectxprobectx,booldirectconnect)(nvmetcp,nvmerdma等都有自己的scan,probe等函数,统一被封装起来,如下)1intnvmetransportctrlrscan(structspdknvmeprobectxprobectx,2booldirectconnect)3{4NVMETRANSPORTCALL(probectxtrid。trtype,ctrlrscan,(probectx,directconnect));5} 1。nvmepciectrlscan:1int2nvmepciectrlrscan(structspdknvmeprobectxprobectx,3booldirectconnect)4{5。。。。。。。若probectx中有指定nvmepciid,haspciaddrtrue6if(enumctx。haspciaddrfalse){7returnspdkpcienumerate(spdkpcinvmegetdriver(),若没有指定8pcienvmeenumcb,enumctx);9}else{10returnspdkpcideviceattach(spdkpcinvmegetdriver(),指定了话直接attach11pcienvmeenumcb,enumctx,enumctx。pciaddr);12}13 spdkpcienumerate:1int2spdkpcienumerate(structspdkpcidriverdriver,3spdkpcienumcbenumcb,4voidenumctx)5{6789cleanuppcidevices();1011pthreadmutexlock(gpcimutex);12TAILQFOREACH(dev,gpcidevices,internal。tailq){遍历gpcidevices13if(devinternal。attached14devinternal。driver!driver15devinternal。pendingremoval){1617}1819rcenumcb(enumctx,dev);从gpcidevices找到我们要的driver,那么调用回调函数进行后续的ctrlconstruct工作20if(rc0){21devinternal。构建成功,attach设置为true22}elseif(rc0){23pthreadmutexunlock(gpcimutex);24return1;25}26}27pthreadmutexunlock(gpcimutex);2829if(!driverisregistered){若改driver还没注册,那么register到rtepcibus。driverlist3031rtepciregister(driverdriver);32}33上面不是找到了吗,下面这些是干啥?为啥要scan和probe??上面只是放到rtepcibus。driverlist了,后面还需做什么?34353637if(rtebusscan()!0rtebusprobe()!0){若busscan和probe都不成功38drivercbargNULL;39drivercbfnNULL;40return1;41}4243drivercbargNULL;44drivercbfnNULL;4546cleanuppcidevices();47return0;48} 回调函数pcienvmeenumcb: 主要是执行nvmectrlrprobe(trid,enumctxprobectx,pcidev)从而构建相应type的ctrl:nvmetransportctrlrconstruct(trid,opts,devhandle),这些不放在这里介绍,会在spdk的源码解读里面分析。 rtepciregister:1registeradriver2void3rtepciregister(structrtepcidriverdriver)4{5TAILQINSERTTAIL(rtepcibus。driverlist,driver,next);67}注意这个structrtepcibusrtepcibus{。bus{。scanrtepciscan,。probertepciprobe,。finddevicepcifinddevice,。plugpciplug,。unplugpciunplug,。parsepciparse,。dmamappcidmamap,。dmaunmappcidmaunmap,。getiommuclassrtepcigetiommuclass,。deviteratertepcideviterate,。hotunplughandlerpcihotunplughandler,。sigbushandlerpcisigbushandler,},。devicelistTAILQHEADINITIALIZER(rtepcibus。devicelist),。driverlistTAILQHEADINITIALIZER(rtepcibus。driverlist),}; rtebusscan()和rtebusprobe()对所有bus进行scan和probe,若是pcibus,执行的是上面注册的 。scanrtepciscan,。probertepciprobe, 回到1中的attach函数: spdkpcideviceattach:跟原来看的有些不一样,之前看的版本直接调用的是rtepciscan1int2spdkpcideviceattach(structspdkpcidriverdriver,3spdkpcienumcbenumcb,4voidenumctx,structspdkpciaddrpciaddress)5{678charbdf〔32〕;910spdkpciaddrfmt(bdf,sizeof(bdf),pciaddress);1112cleanuppcidevices();1314TAILQFOREACH(dev,gpcidevices,internal。tailq){15if(spdkpciaddrcompare(devaddr,pciaddress)0){1617}18}1920if(dev!NULLdevinternal。driverdriver){21pthreadmutexlock(gpcimutex);22if(devinternal。attacheddevinternal。pendingremoval){23pthreadmutexunlock(gpcimutex);24return1;25}2627rcenumcb(enumctx,dev);执行constructctrl等28if(rc0){29devinternal。30}31pthreadmutexunlock(gpcimutex);3233}3435if(!driverisregistered){3637rtepciregister(driverdriver);register到rtepcibus。driverlist38}3940414243ifRTEVERSIONRTEVERSIONNUM(18,11,0,0)44inti0;4546do{47rcrteealhotplugadd(pci,bdf,);48}while(rcENOMSGiDPDKHOTPLUGRETRYCOUNT);4950if(i1rcEEXIST){51Eventhoughthepreviousrequesttimedout,thedevice52wasattachedsuccessfully。5354rc0;55}56else57rcrteealdevattach(bdf,);这个函数在旧版本的dpdk中58endif5960drivercbargNULL;61drivercbfnNULL;6263cleanuppcidevices();64returnrc0?0:1;65} rteealhotplugadd: rtedevprobe(devargs) 1int2rtedevprobe(constchardevargs)3{45678memset(req,0,sizeof(req));9req。tEALDEVREQTYPEATTACH;10strlcpy(req。devargs,devargs,EALDEVMPDEVARGSMAXLEN);1112if(rteealprocesstype()!RTEPROCPRIMARY){不是primary进程1314Ifinsecondaryprocess,justsendIPCrequestto15primaryprocess。1617retealdevhotplugrequesttoprimary(req);那么给primary进程发attch的请求18if(ret!0){19RTELOG(ERR,EAL,20Failedtosendhotplugrequesttoprimary);21returnENOMSG;22}23if(req。result!0)24RTELOG(ERR,EAL,25Failedtohotplugadddevice);26returnreq。27}2829attachashareddevicefromprimarystartfromhere:31primaryattachthenewdeviceitself。32retlocaldevprobe(devargs,dev);其中会执行dabusscan()3334if(ret!0){35RTELOG(ERR,EAL,36Failedtoattachdeviceonprimaryprocess);373839itispossiblethatsecondaryprocessfailedtoattacheda40devicethatprimaryprocesshaveduringinitialization,41soforEEXISTcase,westillneedtosyncwithsecondary42process。4344if(ret!EEXIST)4546}4748primarysendattachsyncrequesttosecondary。49retealdevhotplugrequesttosecondary(req);给secondary进程发消息同步attach情况5051ifanycommunicationerror,weneedtorollback。52if(ret!0){53RTELOG(ERR,EAL,54Failedtosendhotplugaddrequesttosecondary);55retENOMSG;5657}5960ifanysecondaryfailedtoattach,weneedtoconsiderifrollback61isnecessary。6263if(req。result!0){64RTELOG(ERR,EAL,65Failedtoattachdeviceonsecondaryprocess);66retreq。6768forEEXIST,wedontneedtorollback。69if(retEEXIST)707172}7374return0;7576rollback:77req。tEALDEVREQTYPEATTACHROLLBACK;7879primarysendrollbackrequesttosecondary。80if(ealdevhotplugrequesttosecondary(req)!0)81RTELOG(WARNING,EAL,82Failedtorollbackdeviceattachonsecondary。83Devicesinsecondarymaynotsyncwithprimary);8485primaryrollbackitself。86if(localdevremove(dev)!0)87RTELOG(WARNING,EAL,88Failedtorollbackdeviceattachonprimary。89Devicesinsecondarymaynotsyncwithprimary);909192} localdevprobe: 最终也是调用rtepciscan和pciprobealldrivers 1probedeviceatlocalprocess。2int3localdevprobe(constchardevargs,structrtedevicenewdev)4{56789newdevNULL;10dacalloc(1,sizeof(da));11if(daNULL)12returnENOMEM;1314retrtedevargsparse(da,devargs);15if(ret)161718if(dabusplugNULL){19RTELOG(ERR,EAL,Functionplugnotsupportedbybus(s),20dabusname);21retENOTSUP;2223}2425retrtedevargsinsert(da);26if(ret)272829thertedevargswillbereferencedinthematchingrtedevice30retdabusscan();pci的话执行register函数中注册的rtepciscan31if(ret)323334devdabusfinddevice(NULL,cmpdevname,daname);35if(devNULL){36RTELOG(ERR,EAL,Cannotfinddevice(s),37daname);38retENODEV;3940}41Sincethereisamatchingdevice,itisnowitsresponsibility42tomanagethedevargswevejustinserted。Fromthispoint43thosedevargsshouldntberemovedmanuallyanymore。444546retdevbusplug(dev);rtepciplug中执行的是pciprobealldrivers(RTEDEVTOPCI(dev))47if(ret0)48retENOTSUP;4950if(ret!rtedevisprobed(dev)){ifhasnteversucceeded51RTELOG(ERR,EAL,Drivercannotattachthedevice(s),52devname);5354}5556575859errdevarg:60if(rtedevargsremove(da)!0){61free(daargs);62free(da);63}6465} 所以1中nvmepciectrlscan不管是spdkpcienumerate还是spdkpcideviceattach,核心的流程都是一样的: 执行回调函数 rtepciscan rtepciprobepciprobealldrivers(rtepciprobe也是调用pciprobealldrivers) 所以后面我们看rtepcibus。scan(rtepciscan)和rtepcibus。probe(rtepciprobe)的实现。 rtepciscan:12ScanthecontentofthePCIbus,andthedevicesinthedevices3list45int6rtepciscan(void)7{89DIR10chardirname〔PATHMAX〕;111213。。。。。。。。14diropendir(rtepcigetsysfspath());sysbuspcidevices1516。。。。。。。。17while((ereaddir(dir))!NULL){18if(edname〔0〕。)192021if(parsepciaddrformat(edname,sizeof(edname),addr)!0)222324snprintf(dirname,sizeof(dirname),ss,25rtepcigetsysfspath(),edname);2627if(pciscanone(dirname,addr)0)扫描sysbuspcidevices下面每个目录2829}30closedir(dir);31return0;32。。。。。。。。。。33} lssysbuspcidevices0000:00:00。00000:00:04。40000:00:1c。60000:03:00。00000:ff:0d。10000:ff:12。20000:ff:14。50000:ff:17。60000:00:01。00000:00:04。50000:00:1c。70000:05:00。00000:ff:0f。00000:ff:12。40000:ff:14。60000:ff:17。71Scanonepcisysfsentry,andfillthedeviceslistfromit。2staticint3pciscanone(constchardirname,conststructrtepciaddraddr)4{5charfilename〔PATHMAX〕;678chardriver〔PATHMAX〕;91011devmalloc(sizeof(dev));12if(devNULL)13return1;1415memset(dev,0,sizeof(dev));16devdevice。busrtepcibus。171819getvendorid20snprintf(filename,sizeof(filename),svendor,dirname);21if(ealparsesysfsvalue(filename,tmp)0){22free(dev);23return1;24}25devid。vendorid(uint16t)2627getdeviceid28snprintf(filename,sizeof(filename),sdevice,dirname);29if(ealparsesysfsvalue(filename,tmp)0){30free(dev);31return1;32}33devid。deviceid(uint16t)3435getsubsystemvendorid36snprintf(filename,sizeof(filename),ssubsystemvendor,37dirname);38if(ealparsesysfsvalue(filename,tmp)0){39free(dev);40return1;41}42devid。subsystemvendorid(uint16t)4344getsubsystemdeviceid45snprintf(filename,sizeof(filename),ssubsystemdevice,46dirname);47if(ealparsesysfsvalue(filename,tmp)0){48free(dev);49return1;50}51devid。subsystemdeviceid(uint16t)5253getclassid54snprintf(filename,sizeof(filename),sclass,55dirname);56if(ealparsesysfsvalue(filename,tmp)0){57free(dev);58return1;59}60theleast24bitsarevalid:class,subclass,programinterface61devid。classid(uint32t)tmpRTECLASSANYID;6263getmaxvfs64devmaxvfs0;65snprintf(filename,sizeof(filename),smaxvfs,dirname);66if(!access(filename,FOK)67ealparsesysfsvalue(filename,tmp)0)68devmaxvfs(uint16t)69else{70fornonigbuiodriver,needkernelversion3。871snprintf(filename,sizeof(filename),72ssriovnumvfs,dirname);73if(!access(filename,FOK)74ealparsesysfsvalue(filename,tmp)0)75devmaxvfs(uint16t)76}7778getnumanode,defaultto0ifnotpresent79snprintf(filename,sizeof(filename),snumanode,80dirname);8182if(access(filename,FOK)!1){83if(ealparsesysfsvalue(filename,tmp)0)84devdevice。85else86devdevice。numanode1;87}else{88devdevice。numanode0;89}9091pcinameset(dev);9293parseresources94snprintf(filename,sizeof(filename),sresource,dirname);95if(pciparsesysfsresource(filename,dev)0){96RTELOG(ERR,EAL,s():cannotparseresource,func);97free(dev);98return1;99}100101parsedriver102snprintf(filename,sizeof(filename),sdriver,dirname);sysbuspcidrivers103retpcigetkerneldriverbypath(filename,driver,sizeof(driver));104if(ret0){105RTELOG(ERR,EAL,Failtogetkerneldriver);106free(dev);107return1;108}109110if(!ret){111if(!strcmp(driver,vfiopci))112devkdrvRTEKDRVVFIO;113elseif(!strcmp(driver,igbuio))114devkdrvRTEKDRVIGBUIO;115elseif(!strcmp(driver,uiopcigeneric))116devkdrvRTEKDRVUIOGENERIC;117else118devkdrvRTEKDRVUNKNOWN;119}else120devkdrvRTEKDRVNONE;121122deviceisvalid,addinlist(sorted)123if(TAILQEMPTY(rtepcibus。devicelist)){124rtepciadddevice(dev);125}else{126structrtepcidevicedev2;127128129TAILQFOREACH(dev2,rtepcibus。devicelist,next){130retrtepciaddrcmp(devaddr,dev2addr);131if(ret0)132133134if(ret0){135rtepciinsertdevice(dev2,dev);136}else{alreadyregistered137if(!rtedevisprobed(dev2device)){138dev2139dev2140pcinameset(dev2);141memmove(dev2memresource,142devmemresource,143sizeof(devmemresource));144}else{145146Ifdeviceispluggedanddriveris147probedalready,(Thishappenswhen148wecallrtedevprobewhichwill149scanalldeviceonthebus)wedont150needtodoanythinghereunless。。。151152if(dev2kdrv!devkdrv153dev2maxvfs!devmaxvfs)154155Thisshouldnothappens。156Butitisstillpossibleif157weunbindadevicefrom158vfiooruiobeforehotplug159removeandrebinditwith160adifferentconfigure。161Sowejustprintoutthe162errorasanalarm。163164RTELOG(ERR,EAL,Unexpecteddevicescanats!,165filename);166}167free(dev);168}169return0;170}171172rtepciadddevice(dev);173}174175return0;176} rtepciprobe: FOREACHDEVICEONPCIBUS(dev)pciprobealldrivers(dev) FOREACHDRIVERONPCIBUS(dr)rtepciprobeonedriver(dr,dev)12IfvendordeviceIDmatch,calltheprobe()functionofthe3driver。45staticint6rtepciprobeonedriver(structrtepcidriverdr,7structrtepcidevicedev)8{910111213if((drNULL)(devNULL))14returnEINVAL;15161718TCheckifdriversupportsit19if(!rtepcimatch(dr,dev))20Matchofdeviceanddriverfailed21return1;2223RTELOG(INFO,EAL,PCIdevicePCIPRIFMTonNUMAsocketi,24locdomain,locbus,locdevid,locfunction,25devdevice。numanode);2627noinitializationwhenblacklisted,returnwithouterror28if(devdevice。devargs!NULL29devdevice。devargspolicy30RTEDEVBLACKLISTED){31RTELOG(INFO,EAL,Deviceisblacklisted,not32initializing);33return1;34}3536if(devdevice。numanode0){37RTELOG(WARNING,EAL,InvalidNUMAsocket,defaultto0);38devdevice。numanode0;39}4041alreadyprobedrtedevisprobed(devdevice);42if(alreadyprobed!(drdrvflagsRTEPCIDRVPROBEAGAIN)){43RTELOG(DEBUG,EAL,Devicesisalreadyprobed,44devdevice。name);45returnEEXIST;46}4748RTELOG(INFO,EAL,probedriver:x:xs,devid。vendorid,49devid。deviceid,drdriver。name);505152referencedriverstructure53Thisneedstobebeforertepcimapdevice(),asitenablestouse54driverflagsforadjustingconfiguration。5556if(!alreadyprobed){57585960deviovamodepcideviceiovamode(dr,dev);从设备绑定的驱动判断deviovamode,例如若是UIO驱动,则这里是PA61iovamoderteealiovamode();dpdk初始化过程判断的,首先会去判断设备的驱动,如果驱动还没注册,那么里面是根据机器上是否有iommu等判断。而spdk中nvme驱动是在dpdk初始化之后才注册进去的,所以即使是用uio驱动,dpdk给出的iovamode也是VA,所以这里有点小问题!62if(deviovamode!RTEIOVADC63deviovamode!iovamode){64RTELOG(ERR,EAL,ExpectingsIOVAmodebutcurrentmodeiss,notinitializing,65deviovamodeRTEIOVAPA?PA:VA,66iovamodeRTEIOVAPA?PA:VA);67returnEINVAL;68}697071}7273if(!alreadyprobed(drdrvflagsRTEPCIDRVNEEDMAPPING)){74mapresourcesfordevicesthatuseigbuio???注释有问题??75retrtepcimapdevice(dev);76if(ret!0){77devdriverNULL;7879}80}8182callthedriverprobe()function83retdrprobe(dr,dev);在哪里?????84if(alreadyprobed)85norollbackifalreadysucceededearlier86if(ret){87devdriverNULL;88if((drdrvflagsRTEPCIDRVNEEDMAPPING)89Dontunmapifdeviceisunsupportedand90driverneedsmappedresources。9192!(ret093(drdrvflagsRTEPCIDRVKEEPMAPPEDRES)))94rtepciunmapdevice(dev);95}else{96devdevice。97}9899100} rtepcimapdevice从这遍开始和vfio,uio等相关1Mappcidevice2int3rtepcimapdevice(structrtepcidevicedev)4{5intret1;67trymappingtheNICresourcesusingVFIOifitexists8switch(devkdrv){9caseRTEKDRVVFIO:10ifdefVFIOPRESENT11if(pcivfioisenabled())12retpcivfiomapresource(dev);13endif1415caseRTEKDRVIGBUIO:16caseRTEKDRVUIOGENERIC:17if(rteealusingphysaddrs()){18mapresourcesfordevicesthatuseuio19retpciuiomapresource(dev);20}2122default:23RTELOG(DEBUG,EAL,24Notmanagedbyasupportedkerneldriver,skipped);25ret1;2627}282930} pcivfiomapresource: 1pcivfiomapresourceprimary若设primary进程 pcivfiomapresourcesecondary 2rtevfiosetupdevice 3vfiomemeventcallback 这三个函数要重点看 原文链接:https:www。cnblogs。comyimuxip12441357。html原文作者:yimuxi LinuxCC服务器开发架构师面试题、学习资料、教学视频和学习路线图(资料包括CC,Linux,Nginx,ZeroMQ,MySQL,Redis、MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCPIP,协程,DPDK等),或点击这里加qun免费领取,关注我持续更新哦!!