Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
99 kB · 3152 lines
C
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153// SPDX-License-Identifier: GPL-2.0/* * KUnit tests * * Copyright (C) 2020, Intel Corporation * Author: Mika Westerberg <mika.westerberg@linux.intel.com> */
#include <kunit/test.h>#include <linux/idr.h>
#include "tb.h"#include "tunnel.h"
static int __ida_init(struct kunit_resource *res, void *context){ struct ida *ida = context;
ida_init(ida); res->data = ida; return 0;}
static void __ida_destroy(struct kunit_resource *res){ struct ida *ida = res->data;
ida_destroy(ida);}
static void kunit_ida_init(struct kunit *test, struct ida *ida){ kunit_alloc_resource(test, __ida_init, __ida_destroy, GFP_KERNEL, ida);}
static struct tb_switch *alloc_switch(struct kunit *test, u64 route, u8 upstream_port, u8 max_port_number){ struct tb_switch *sw; size_t size; int i;
sw = kunit_kzalloc(test, sizeof(*sw), GFP_KERNEL); if (!sw) return NULL;
sw->config.upstream_port_number = upstream_port; sw->config.depth = tb_route_length(route); sw->config.route_hi = upper_32_bits(route); sw->config.route_lo = lower_32_bits(route); sw->config.enabled = 0; sw->config.max_port_number = max_port_number;
size = (sw->config.max_port_number + 1) * sizeof(*sw->ports); sw->ports = kunit_kzalloc(test, size, GFP_KERNEL); if (!sw->ports) return NULL;
for (i = 0; i <= sw->config.max_port_number; i++) { sw->ports[i].sw = sw; sw->ports[i].port = i; sw->ports[i].config.port_number = i; if (i) { kunit_ida_init(test, &sw->ports[i].in_hopids); kunit_ida_init(test, &sw->ports[i].out_hopids); } }
return sw;}
static struct tb_switch *alloc_host(struct kunit *test){ struct tb_switch *sw;
sw = alloc_switch(test, 0, 7, 13); if (!sw) return NULL;
sw->config.vendor_id = 0x8086; sw->config.device_id = 0x9a1b;
sw->ports[0].config.type = TB_TYPE_PORT; sw->ports[0].config.max_in_hop_id = 7; sw->ports[0].config.max_out_hop_id = 7;
sw->ports[1].config.type = TB_TYPE_PORT; sw->ports[1].config.max_in_hop_id = 19; sw->ports[1].config.max_out_hop_id = 19; sw->ports[1].total_credits = 60; sw->ports[1].ctl_credits = 2; sw->ports[1].dual_link_port = &sw->ports[2];
sw->ports[2].config.type = TB_TYPE_PORT; sw->ports[2].config.max_in_hop_id = 19; sw->ports[2].config.max_out_hop_id = 19; sw->ports[2].total_credits = 60; sw->ports[2].ctl_credits = 2; sw->ports[2].dual_link_port = &sw->ports[1]; sw->ports[2].link_nr = 1;
sw->ports[3].config.type = TB_TYPE_PORT; sw->ports[3].config.max_in_hop_id = 19; sw->ports[3].config.max_out_hop_id = 19; sw->ports[3].total_credits = 60; sw->ports[3].ctl_credits = 2; sw->ports[3].dual_link_port = &sw->ports[4];
sw->ports[4].config.type = TB_TYPE_PORT; sw->ports[4].config.max_in_hop_id = 19; sw->ports[4].config.max_out_hop_id = 19; sw->ports[4].total_credits = 60; sw->ports[4].ctl_credits = 2; sw->ports[4].dual_link_port = &sw->ports[3]; sw->ports[4].link_nr = 1;
sw->ports[5].config.type = TB_TYPE_DP_HDMI_IN; sw->ports[5].config.max_in_hop_id = 9; sw->ports[5].config.max_out_hop_id = 9; sw->ports[5].cap_adap = -1;
sw->ports[6].config.type = TB_TYPE_DP_HDMI_IN; sw->ports[6].config.max_in_hop_id = 9; sw->ports[6].config.max_out_hop_id = 9; sw->ports[6].cap_adap = -1;
sw->ports[7].config.type = TB_TYPE_NHI; sw->ports[7].config.max_in_hop_id = 11; sw->ports[7].config.max_out_hop_id = 11; sw->ports[7].config.nfc_credits = 0x41800000;
sw->ports[8].config.type = TB_TYPE_PCIE_DOWN; sw->ports[8].config.max_in_hop_id = 8; sw->ports[8].config.max_out_hop_id = 8;
sw->ports[9].config.type = TB_TYPE_PCIE_DOWN; sw->ports[9].config.max_in_hop_id = 8; sw->ports[9].config.max_out_hop_id = 8;
sw->ports[10].disabled = true; sw->ports[11].disabled = true;
sw->ports[12].config.type = TB_TYPE_USB3_DOWN; sw->ports[12].config.max_in_hop_id = 8; sw->ports[12].config.max_out_hop_id = 8;
sw->ports[13].config.type = TB_TYPE_USB3_DOWN; sw->ports[13].config.max_in_hop_id = 8; sw->ports[13].config.max_out_hop_id = 8;
return sw;}
static struct tb_switch *alloc_host_usb4(struct kunit *test){ struct tb_switch *sw;
sw = alloc_host(test); if (!sw) return NULL;
sw->generation = 4; sw->credit_allocation = true; sw->max_usb3_credits = 32; sw->min_dp_aux_credits = 1; sw->min_dp_main_credits = 0; sw->max_pcie_credits = 64; sw->max_dma_credits = 14;
return sw;}
static struct tb_switch *alloc_host_br(struct kunit *test){ struct tb_switch *sw;
sw = alloc_host_usb4(test); if (!sw) return NULL;
sw->ports[10].config.type = TB_TYPE_DP_HDMI_IN; sw->ports[10].config.max_in_hop_id = 9; sw->ports[10].config.max_out_hop_id = 9; sw->ports[10].cap_adap = -1; sw->ports[10].disabled = false;
return sw;}
static struct tb_switch *alloc_dev_default(struct kunit *test, struct tb_switch *parent, u64 route, bool bonded){ struct tb_port *port, *upstream_port; struct tb_switch *sw;
sw = alloc_switch(test, route, 1, 19); if (!sw) return NULL;
sw->config.vendor_id = 0x8086; sw->config.device_id = 0x15ef;
sw->ports[0].config.type = TB_TYPE_PORT; sw->ports[0].config.max_in_hop_id = 8; sw->ports[0].config.max_out_hop_id = 8;
sw->ports[1].config.type = TB_TYPE_PORT; sw->ports[1].config.max_in_hop_id = 19; sw->ports[1].config.max_out_hop_id = 19; sw->ports[1].total_credits = 60; sw->ports[1].ctl_credits = 2; sw->ports[1].dual_link_port = &sw->ports[2];
sw->ports[2].config.type = TB_TYPE_PORT; sw->ports[2].config.max_in_hop_id = 19; sw->ports[2].config.max_out_hop_id = 19; sw->ports[2].total_credits = 60; sw->ports[2].ctl_credits = 2; sw->ports[2].dual_link_port = &sw->ports[1]; sw->ports[2].link_nr = 1;
sw->ports[3].config.type = TB_TYPE_PORT; sw->ports[3].config.max_in_hop_id = 19; sw->ports[3].config.max_out_hop_id = 19; sw->ports[3].total_credits = 60; sw->ports[3].ctl_credits = 2; sw->ports[3].dual_link_port = &sw->ports[4];
sw->ports[4].config.type = TB_TYPE_PORT; sw->ports[4].config.max_in_hop_id = 19; sw->ports[4].config.max_out_hop_id = 19; sw->ports[4].total_credits = 60; sw->ports[4].ctl_credits = 2; sw->ports[4].dual_link_port = &sw->ports[3]; sw->ports[4].link_nr = 1;
sw->ports[5].config.type = TB_TYPE_PORT; sw->ports[5].config.max_in_hop_id = 19; sw->ports[5].config.max_out_hop_id = 19; sw->ports[5].total_credits = 60; sw->ports[5].ctl_credits = 2; sw->ports[5].dual_link_port = &sw->ports[6];
sw->ports[6].config.type = TB_TYPE_PORT; sw->ports[6].config.max_in_hop_id = 19; sw->ports[6].config.max_out_hop_id = 19; sw->ports[6].total_credits = 60; sw->ports[6].ctl_credits = 2; sw->ports[6].dual_link_port = &sw->ports[5]; sw->ports[6].link_nr = 1;
sw->ports[7].config.type = TB_TYPE_PORT; sw->ports[7].config.max_in_hop_id = 19; sw->ports[7].config.max_out_hop_id = 19; sw->ports[7].total_credits = 60; sw->ports[7].ctl_credits = 2; sw->ports[7].dual_link_port = &sw->ports[8];
sw->ports[8].config.type = TB_TYPE_PORT; sw->ports[8].config.max_in_hop_id = 19; sw->ports[8].config.max_out_hop_id = 19; sw->ports[8].total_credits = 60; sw->ports[8].ctl_credits = 2; sw->ports[8].dual_link_port = &sw->ports[7]; sw->ports[8].link_nr = 1;
sw->ports[9].config.type = TB_TYPE_PCIE_UP; sw->ports[9].config.max_in_hop_id = 8; sw->ports[9].config.max_out_hop_id = 8;
sw->ports[10].config.type = TB_TYPE_PCIE_DOWN; sw->ports[10].config.max_in_hop_id = 8; sw->ports[10].config.max_out_hop_id = 8;
sw->ports[11].config.type = TB_TYPE_PCIE_DOWN; sw->ports[11].config.max_in_hop_id = 8; sw->ports[11].config.max_out_hop_id = 8;
sw->ports[12].config.type = TB_TYPE_PCIE_DOWN; sw->ports[12].config.max_in_hop_id = 8; sw->ports[12].config.max_out_hop_id = 8;
sw->ports[13].config.type = TB_TYPE_DP_HDMI_OUT; sw->ports[13].config.max_in_hop_id = 9; sw->ports[13].config.max_out_hop_id = 9; sw->ports[13].cap_adap = -1;
sw->ports[14].config.type = TB_TYPE_DP_HDMI_OUT; sw->ports[14].config.max_in_hop_id = 9; sw->ports[14].config.max_out_hop_id = 9; sw->ports[14].cap_adap = -1;
sw->ports[15].disabled = true;
sw->ports[16].config.type = TB_TYPE_USB3_UP; sw->ports[16].config.max_in_hop_id = 8; sw->ports[16].config.max_out_hop_id = 8;
sw->ports[17].config.type = TB_TYPE_USB3_DOWN; sw->ports[17].config.max_in_hop_id = 8; sw->ports[17].config.max_out_hop_id = 8;
sw->ports[18].config.type = TB_TYPE_USB3_DOWN; sw->ports[18].config.max_in_hop_id = 8; sw->ports[18].config.max_out_hop_id = 8;
sw->ports[19].config.type = TB_TYPE_USB3_DOWN; sw->ports[19].config.max_in_hop_id = 8; sw->ports[19].config.max_out_hop_id = 8;
if (!parent) return sw;
/* Link them */ upstream_port = tb_upstream_port(sw); port = tb_port_at(route, parent); port->remote = upstream_port; upstream_port->remote = port; if (port->dual_link_port && upstream_port->dual_link_port) { port->dual_link_port->remote = upstream_port->dual_link_port; upstream_port->dual_link_port->remote = port->dual_link_port;
if (bonded) { /* Bonding is used */ port->bonded = true; port->total_credits *= 2; port->dual_link_port->bonded = true; port->dual_link_port->total_credits = 0; upstream_port->bonded = true; upstream_port->total_credits *= 2; upstream_port->dual_link_port->bonded = true; upstream_port->dual_link_port->total_credits = 0; } }
return sw;}
static struct tb_switch *alloc_dev_with_dpin(struct kunit *test, struct tb_switch *parent, u64 route, bool bonded){ struct tb_switch *sw;
sw = alloc_dev_default(test, parent, route, bonded); if (!sw) return NULL;
sw->ports[13].config.type = TB_TYPE_DP_HDMI_IN; sw->ports[13].config.max_in_hop_id = 9; sw->ports[13].config.max_out_hop_id = 9;
sw->ports[14].config.type = TB_TYPE_DP_HDMI_IN; sw->ports[14].config.max_in_hop_id = 9; sw->ports[14].config.max_out_hop_id = 9;
return sw;}
static struct tb_switch *alloc_dev_without_dp(struct kunit *test, struct tb_switch *parent, u64 route, bool bonded){ struct tb_switch *sw; int i;
sw = alloc_dev_default(test, parent, route, bonded); if (!sw) return NULL; /* * Device with: * 2x USB4 Adapters (adapters 1,2 and 3,4), * 1x PCIe Upstream (adapter 9), * 1x PCIe Downstream (adapter 10), * 1x USB3 Upstream (adapter 16), * 1x USB3 Downstream (adapter 17) */ for (i = 5; i <= 8; i++) sw->ports[i].disabled = true;
for (i = 11; i <= 14; i++) sw->ports[i].disabled = true;
sw->ports[13].cap_adap = 0; sw->ports[14].cap_adap = 0;
for (i = 18; i <= 19; i++) sw->ports[i].disabled = true;
sw->generation = 4; sw->credit_allocation = true; sw->max_usb3_credits = 109; sw->min_dp_aux_credits = 0; sw->min_dp_main_credits = 0; sw->max_pcie_credits = 30; sw->max_dma_credits = 1;
return sw;}
static struct tb_switch *alloc_dev_usb4(struct kunit *test, struct tb_switch *parent, u64 route, bool bonded){ struct tb_switch *sw;
sw = alloc_dev_default(test, parent, route, bonded); if (!sw) return NULL;
sw->generation = 4; sw->credit_allocation = true; sw->max_usb3_credits = 14; sw->min_dp_aux_credits = 1; sw->min_dp_main_credits = 18; sw->max_pcie_credits = 32; sw->max_dma_credits = 14;
return sw;}
static void tb_test_path_basic(struct kunit *test){ struct tb_port *src_port, *dst_port, *p; struct tb_switch *host;
host = alloc_host(test);
src_port = &host->ports[5]; dst_port = src_port;
p = tb_next_port_on_path(src_port, dst_port, NULL); KUNIT_EXPECT_PTR_EQ(test, p, dst_port);
p = tb_next_port_on_path(src_port, dst_port, p); KUNIT_EXPECT_TRUE(test, !p);}
static void tb_test_path_not_connected_walk(struct kunit *test){ struct tb_port *src_port, *dst_port, *p; struct tb_switch *host, *dev;
host = alloc_host(test); /* No connection between host and dev */ dev = alloc_dev_default(test, NULL, 3, true);
src_port = &host->ports[12]; dst_port = &dev->ports[16];
p = tb_next_port_on_path(src_port, dst_port, NULL); KUNIT_EXPECT_PTR_EQ(test, p, src_port);
p = tb_next_port_on_path(src_port, dst_port, p); KUNIT_EXPECT_PTR_EQ(test, p, &host->ports[3]);
p = tb_next_port_on_path(src_port, dst_port, p); KUNIT_EXPECT_TRUE(test, !p);
/* Other direction */
p = tb_next_port_on_path(dst_port, src_port, NULL); KUNIT_EXPECT_PTR_EQ(test, p, dst_port);
p = tb_next_port_on_path(dst_port, src_port, p); KUNIT_EXPECT_PTR_EQ(test, p, &dev->ports[1]);
p = tb_next_port_on_path(dst_port, src_port, p); KUNIT_EXPECT_TRUE(test, !p);}
struct port_expectation { u64 route; u8 port; enum tb_port_type type;};
static void tb_test_path_single_hop_walk(struct kunit *test){ /* * Walks from Host PCIe downstream port to Device #1 PCIe * upstream port. * * [Host] * 1 | * 1 | * [Device] */ static const struct port_expectation test_data[] = { { .route = 0x0, .port = 8, .type = TB_TYPE_PCIE_DOWN }, { .route = 0x0, .port = 1, .type = TB_TYPE_PORT }, { .route = 0x1, .port = 1, .type = TB_TYPE_PORT }, { .route = 0x1, .port = 9, .type = TB_TYPE_PCIE_UP }, }; struct tb_port *src_port, *dst_port, *p; struct tb_switch *host, *dev; int i;
host = alloc_host(test); dev = alloc_dev_default(test, host, 1, true);
src_port = &host->ports[8]; dst_port = &dev->ports[9];
/* Walk both directions */
i = 0; tb_for_each_port_on_path(src_port, dst_port, p) { KUNIT_EXPECT_TRUE(test, i < ARRAY_SIZE(test_data)); KUNIT_EXPECT_EQ(test, tb_route(p->sw), test_data[i].route); KUNIT_EXPECT_EQ(test, p->port, test_data[i].port); KUNIT_EXPECT_EQ(test, (enum tb_port_type)p->config.type, test_data[i].type); i++; }
KUNIT_EXPECT_EQ(test, i, ARRAY_SIZE(test_data));
i = ARRAY_SIZE(test_data) - 1; tb_for_each_port_on_path(dst_port, src_port, p) { KUNIT_EXPECT_TRUE(test, i < ARRAY_SIZE(test_data)); KUNIT_EXPECT_EQ(test, tb_route(p->sw), test_data[i].route); KUNIT_EXPECT_EQ(test, p->port, test_data[i].port); KUNIT_EXPECT_EQ(test, (enum tb_port_type)p->config.type, test_data[i].type); i--; }
KUNIT_EXPECT_EQ(test, i, -1);}
static void tb_test_path_daisy_chain_walk(struct kunit *test){ /* * Walks from Host DP IN to Device #2 DP OUT. * * [Host] * 1 | * 1 | * [Device #1] * 3 / * 1 / * [Device #2] */ static const struct port_expectation test_data[] = { { .route = 0x0, .port = 5, .type = TB_TYPE_DP_HDMI_IN }, { .route = 0x0, .port = 1, .type = TB_TYPE_PORT }, { .route = 0x1, .port = 1, .type = TB_TYPE_PORT }, { .route = 0x1, .port = 3, .type = TB_TYPE_PORT }, { .route = 0x301, .port = 1, .type = TB_TYPE_PORT }, { .route = 0x301, .port = 13, .type = TB_TYPE_DP_HDMI_OUT }, }; struct tb_port *src_port, *dst_port, *p; struct tb_switch *host, *dev1, *dev2; int i;
host = alloc_host(test); dev1 = alloc_dev_default(test, host, 0x1, true); dev2 = alloc_dev_default(test, dev1, 0x301, true);
src_port = &host->ports[5]; dst_port = &dev2->ports[13];
/* Walk both directions */
i = 0; tb_for_each_port_on_path(src_port, dst_port, p) { KUNIT_EXPECT_TRUE(test, i < ARRAY_SIZE(test_data)); KUNIT_EXPECT_EQ(test, tb_route(p->sw), test_data[i].route); KUNIT_EXPECT_EQ(test, p->port, test_data[i].port); KUNIT_EXPECT_EQ(test, (enum tb_port_type)p->config.type, test_data[i].type); i++; }
KUNIT_EXPECT_EQ(test, i, ARRAY_SIZE(test_data));
i = ARRAY_SIZE(test_data) - 1; tb_for_each_port_on_path(dst_port, src_port, p) { KUNIT_EXPECT_TRUE(test, i < ARRAY_SIZE(test_data)); KUNIT_EXPECT_EQ(test, tb_route(p->sw), test_data[i].route); KUNIT_EXPECT_EQ(test, p->port, test_data[i].port); KUNIT_EXPECT_EQ(test, (enum tb_port_type)p->config.type, test_data[i].type); i--; }
KUNIT_EXPECT_EQ(test, i, -1);}
static void tb_test_path_simple_tree_walk(struct kunit *test){ /* * Walks from Host DP IN to Device #3 DP OUT. * * [Host] * 1 | * 1 | * [Device #1] * 3 / | 5 \ 7 * 1 / | \ 1 * [Device #2] | [Device #4] * | 1 * [Device #3] */ static const struct port_expectation test_data[] = { { .route = 0x0, .port = 5, .type = TB_TYPE_DP_HDMI_IN }, { .route = 0x0, .port = 1, .type = TB_TYPE_PORT }, { .route = 0x1, .port = 1, .type = TB_TYPE_PORT }, { .route = 0x1, .port = 5, .type = TB_TYPE_PORT }, { .route = 0x501, .port = 1, .type = TB_TYPE_PORT }, { .route = 0x501, .port = 13, .type = TB_TYPE_DP_HDMI_OUT }, }; struct tb_port *src_port, *dst_port, *p; struct tb_switch *host, *dev1, *dev3; int i;
host = alloc_host(test); dev1 = alloc_dev_default(test, host, 0x1, true); alloc_dev_default(test, dev1, 0x301, true); dev3 = alloc_dev_default(test, dev1, 0x501, true); alloc_dev_default(test, dev1, 0x701, true);
src_port = &host->ports[5]; dst_port = &dev3->ports[13];
/* Walk both directions */
i = 0; tb_for_each_port_on_path(src_port, dst_port, p) { KUNIT_EXPECT_TRUE(test, i < ARRAY_SIZE(test_data)); KUNIT_EXPECT_EQ(test, tb_route(p->sw), test_data[i].route); KUNIT_EXPECT_EQ(test, p->port, test_data[i].port); KUNIT_EXPECT_EQ(test, (enum tb_port_type)p->config.type, test_data[i].type); i++; }
KUNIT_EXPECT_EQ(test, i, ARRAY_SIZE(test_data));
i = ARRAY_SIZE(test_data) - 1; tb_for_each_port_on_path(dst_port, src_port, p) { KUNIT_EXPECT_TRUE(test, i < ARRAY_SIZE(test_data)); KUNIT_EXPECT_EQ(test, tb_route(p->sw), test_data[i].route); KUNIT_EXPECT_EQ(test, p->port, test_data[i].port); KUNIT_EXPECT_EQ(test, (enum tb_port_type)p->config.type, test_data[i].type); i--; }
KUNIT_EXPECT_EQ(test, i, -1);}
static void tb_test_path_complex_tree_walk(struct kunit *test){ /* * Walks from Device #3 DP IN to Device #9 DP OUT. * * [Host] * 1 | * 1 | * [Device #1] * 3 / | 5 \ 7 * 1 / | \ 1 * [Device #2] | [Device #5] * 5 | | 1 \ 7 * 1 | [Device #4] \ 1 * [Device #3] [Device #6] * 3 / * 1 / * [Device #7] * 3 / | 5 * 1 / | * [Device #8] | 1 * [Device #9] */ static const struct port_expectation test_data[] = { { .route = 0x50301, .port = 13, .type = TB_TYPE_DP_HDMI_IN }, { .route = 0x50301, .port = 1, .type = TB_TYPE_PORT }, { .route = 0x301, .port = 5, .type = TB_TYPE_PORT }, { .route = 0x301, .port = 1, .type = TB_TYPE_PORT }, { .route = 0x1, .port = 3, .type = TB_TYPE_PORT }, { .route = 0x1, .port = 7, .type = TB_TYPE_PORT }, { .route = 0x701, .port = 1, .type = TB_TYPE_PORT }, { .route = 0x701, .port = 7, .type = TB_TYPE_PORT }, { .route = 0x70701, .port = 1, .type = TB_TYPE_PORT }, { .route = 0x70701, .port = 3, .type = TB_TYPE_PORT }, { .route = 0x3070701, .port = 1, .type = TB_TYPE_PORT }, { .route = 0x3070701, .port = 5, .type = TB_TYPE_PORT }, { .route = 0x503070701, .port = 1, .type = TB_TYPE_PORT }, { .route = 0x503070701, .port = 14, .type = TB_TYPE_DP_HDMI_OUT }, }; struct tb_switch *host, *dev1, *dev2, *dev3, *dev5, *dev6, *dev7, *dev9; struct tb_port *src_port, *dst_port, *p; int i;
host = alloc_host(test); dev1 = alloc_dev_default(test, host, 0x1, true); dev2 = alloc_dev_default(test, dev1, 0x301, true); dev3 = alloc_dev_with_dpin(test, dev2, 0x50301, true); alloc_dev_default(test, dev1, 0x501, true); dev5 = alloc_dev_default(test, dev1, 0x701, true); dev6 = alloc_dev_default(test, dev5, 0x70701, true); dev7 = alloc_dev_default(test, dev6, 0x3070701, true); alloc_dev_default(test, dev7, 0x303070701, true); dev9 = alloc_dev_default(test, dev7, 0x503070701, true);
src_port = &dev3->ports[13]; dst_port = &dev9->ports[14];
/* Walk both directions */
i = 0; tb_for_each_port_on_path(src_port, dst_port, p) { KUNIT_EXPECT_TRUE(test, i < ARRAY_SIZE(test_data)); KUNIT_EXPECT_EQ(test, tb_route(p->sw), test_data[i].route); KUNIT_EXPECT_EQ(test, p->port, test_data[i].port); KUNIT_EXPECT_EQ(test, (enum tb_port_type)p->config.type, test_data[i].type); i++; }
KUNIT_EXPECT_EQ(test, i, ARRAY_SIZE(test_data));
i = ARRAY_SIZE(test_data) - 1; tb_for_each_port_on_path(dst_port, src_port, p) { KUNIT_EXPECT_TRUE(test, i < ARRAY_SIZE(test_data)); KUNIT_EXPECT_EQ(test, tb_route(p->sw), test_data[i].route); KUNIT_EXPECT_EQ(test, p->port, test_data[i].port); KUNIT_EXPECT_EQ(test, (enum tb_port_type)p->config.type, test_data[i].type); i--; }
KUNIT_EXPECT_EQ(test, i, -1);}
static void tb_test_path_max_length_walk(struct kunit *test){ struct tb_switch *host, *dev1, *dev2, *dev3, *dev4, *dev5, *dev6; struct tb_switch *dev7, *dev8, *dev9, *dev10, *dev11, *dev12; struct tb_port *src_port, *dst_port, *p; int i;
/* * Walks from Device #6 DP IN to Device #12 DP OUT. * * [Host] * 1 / \ 3 * 1 / \ 1 * [Device #1] [Device #7] * 3 | | 3 * 1 | | 1 * [Device #2] [Device #8] * 3 | | 3 * 1 | | 1 * [Device #3] [Device #9] * 3 | | 3 * 1 | | 1 * [Device #4] [Device #10] * 3 | | 3 * 1 | | 1 * [Device #5] [Device #11] * 3 | | 3 * 1 | | 1 * [Device #6] [Device #12] */ static const struct port_expectation test_data[] = { { .route = 0x30303030301, .port = 13, .type = TB_TYPE_DP_HDMI_IN }, { .route = 0x30303030301, .port = 1, .type = TB_TYPE_PORT }, { .route = 0x303030301, .port = 3, .type = TB_TYPE_PORT }, { .route = 0x303030301, .port = 1, .type = TB_TYPE_PORT }, { .route = 0x3030301, .port = 3, .type = TB_TYPE_PORT }, { .route = 0x3030301, .port = 1, .type = TB_TYPE_PORT }, { .route = 0x30301, .port = 3, .type = TB_TYPE_PORT }, { .route = 0x30301, .port = 1, .type = TB_TYPE_PORT }, { .route = 0x301, .port = 3, .type = TB_TYPE_PORT }, { .route = 0x301, .port = 1, .type = TB_TYPE_PORT }, { .route = 0x1, .port = 3, .type = TB_TYPE_PORT }, { .route = 0x1, .port = 1, .type = TB_TYPE_PORT }, { .route = 0x0, .port = 1, .type = TB_TYPE_PORT }, { .route = 0x0, .port = 3, .type = TB_TYPE_PORT }, { .route = 0x3, .port = 1, .type = TB_TYPE_PORT }, { .route = 0x3, .port = 3, .type = TB_TYPE_PORT }, { .route = 0x303, .port = 1, .type = TB_TYPE_PORT }, { .route = 0x303, .port = 3, .type = TB_TYPE_PORT }, { .route = 0x30303, .port = 1, .type = TB_TYPE_PORT }, { .route = 0x30303, .port = 3, .type = TB_TYPE_PORT }, { .route = 0x3030303, .port = 1, .type = TB_TYPE_PORT }, { .route = 0x3030303, .port = 3, .type = TB_TYPE_PORT }, { .route = 0x303030303, .port = 1, .type = TB_TYPE_PORT }, { .route = 0x303030303, .port = 3, .type = TB_TYPE_PORT }, { .route = 0x30303030303, .port = 1, .type = TB_TYPE_PORT }, { .route = 0x30303030303, .port = 13, .type = TB_TYPE_DP_HDMI_OUT }, };
host = alloc_host(test); dev1 = alloc_dev_default(test, host, 0x1, true); dev2 = alloc_dev_default(test, dev1, 0x301, true); dev3 = alloc_dev_default(test, dev2, 0x30301, true); dev4 = alloc_dev_default(test, dev3, 0x3030301, true); dev5 = alloc_dev_default(test, dev4, 0x303030301, true); dev6 = alloc_dev_with_dpin(test, dev5, 0x30303030301, true); dev7 = alloc_dev_default(test, host, 0x3, true); dev8 = alloc_dev_default(test, dev7, 0x303, true); dev9 = alloc_dev_default(test, dev8, 0x30303, true); dev10 = alloc_dev_default(test, dev9, 0x3030303, true); dev11 = alloc_dev_default(test, dev10, 0x303030303, true); dev12 = alloc_dev_default(test, dev11, 0x30303030303, true);
src_port = &dev6->ports[13]; dst_port = &dev12->ports[13];
/* Walk both directions */
i = 0; tb_for_each_port_on_path(src_port, dst_port, p) { KUNIT_EXPECT_TRUE(test, i < ARRAY_SIZE(test_data)); KUNIT_EXPECT_EQ(test, tb_route(p->sw), test_data[i].route); KUNIT_EXPECT_EQ(test, p->port, test_data[i].port); KUNIT_EXPECT_EQ(test, (enum tb_port_type)p->config.type, test_data[i].type); i++; }
KUNIT_EXPECT_EQ(test, i, ARRAY_SIZE(test_data));
i = ARRAY_SIZE(test_data) - 1; tb_for_each_port_on_path(dst_port, src_port, p) { KUNIT_EXPECT_TRUE(test, i < ARRAY_SIZE(test_data)); KUNIT_EXPECT_EQ(test, tb_route(p->sw), test_data[i].route); KUNIT_EXPECT_EQ(test, p->port, test_data[i].port); KUNIT_EXPECT_EQ(test, (enum tb_port_type)p->config.type, test_data[i].type); i--; }
KUNIT_EXPECT_EQ(test, i, -1);}
static void tb_test_path_not_connected(struct kunit *test){ struct tb_switch *host, *dev1, *dev2; struct tb_port *down, *up; struct tb_path *path;
host = alloc_host(test); dev1 = alloc_dev_default(test, host, 0x3, false); /* Not connected to anything */ dev2 = alloc_dev_default(test, NULL, 0x303, false);
down = &dev1->ports[10]; up = &dev2->ports[9];
path = tb_path_alloc(NULL, down, 8, up, 8, 0, "PCIe Down"); KUNIT_ASSERT_NULL(test, path); path = tb_path_alloc(NULL, down, 8, up, 8, 1, "PCIe Down"); KUNIT_ASSERT_NULL(test, path);}
struct hop_expectation { u64 route; u8 in_port; enum tb_port_type in_type; u8 out_port; enum tb_port_type out_type;};
static void tb_test_path_not_bonded_lane0(struct kunit *test){ /* * PCIe path from host to device using lane 0. * * [Host] * 3 |: 4 * 1 |: 2 * [Device] */ static const struct hop_expectation test_data[] = { { .route = 0x0, .in_port = 9, .in_type = TB_TYPE_PCIE_DOWN, .out_port = 3, .out_type = TB_TYPE_PORT, }, { .route = 0x3, .in_port = 1, .in_type = TB_TYPE_PORT, .out_port = 9, .out_type = TB_TYPE_PCIE_UP, }, }; struct tb_switch *host, *dev; struct tb_port *down, *up; struct tb_path *path; int i;
host = alloc_host(test); dev = alloc_dev_default(test, host, 0x3, false);
down = &host->ports[9]; up = &dev->ports[9];
path = tb_path_alloc(NULL, down, 8, up, 8, 0, "PCIe Down"); KUNIT_ASSERT_NOT_NULL(test, path); KUNIT_ASSERT_EQ(test, path->path_length, ARRAY_SIZE(test_data)); for (i = 0; i < ARRAY_SIZE(test_data); i++) { const struct tb_port *in_port, *out_port;
in_port = path->hops[i].in_port; out_port = path->hops[i].out_port;
KUNIT_EXPECT_EQ(test, tb_route(in_port->sw), test_data[i].route); KUNIT_EXPECT_EQ(test, in_port->port, test_data[i].in_port); KUNIT_EXPECT_EQ(test, (enum tb_port_type)in_port->config.type, test_data[i].in_type); KUNIT_EXPECT_EQ(test, tb_route(out_port->sw), test_data[i].route); KUNIT_EXPECT_EQ(test, out_port->port, test_data[i].out_port); KUNIT_EXPECT_EQ(test, (enum tb_port_type)out_port->config.type, test_data[i].out_type); } tb_path_free(path);}
static void tb_test_path_not_bonded_lane1(struct kunit *test){ /* * DP Video path from host to device using lane 1. Paths like * these are only used with Thunderbolt 1 devices where lane * bonding is not possible. USB4 specifically does not allow * paths like this (you either use lane 0 where lane 1 is * disabled or both lanes are bonded). * * [Host] * 1 :| 2 * 1 :| 2 * [Device] */ static const struct hop_expectation test_data[] = { { .route = 0x0, .in_port = 5, .in_type = TB_TYPE_DP_HDMI_IN, .out_port = 2, .out_type = TB_TYPE_PORT, }, { .route = 0x1, .in_port = 2, .in_type = TB_TYPE_PORT, .out_port = 13, .out_type = TB_TYPE_DP_HDMI_OUT, }, }; struct tb_switch *host, *dev; struct tb_port *in, *out; struct tb_path *path; int i;
host = alloc_host(test); dev = alloc_dev_default(test, host, 0x1, false);
in = &host->ports[5]; out = &dev->ports[13];
path = tb_path_alloc(NULL, in, 9, out, 9, 1, "Video"); KUNIT_ASSERT_NOT_NULL(test, path); KUNIT_ASSERT_EQ(test, path->path_length, ARRAY_SIZE(test_data)); for (i = 0; i < ARRAY_SIZE(test_data); i++) { const struct tb_port *in_port, *out_port;
in_port = path->hops[i].in_port; out_port = path->hops[i].out_port;
KUNIT_EXPECT_EQ(test, tb_route(in_port->sw), test_data[i].route); KUNIT_EXPECT_EQ(test, in_port->port, test_data[i].in_port); KUNIT_EXPECT_EQ(test, (enum tb_port_type)in_port->config.type, test_data[i].in_type); KUNIT_EXPECT_EQ(test, tb_route(out_port->sw), test_data[i].route); KUNIT_EXPECT_EQ(test, out_port->port, test_data[i].out_port); KUNIT_EXPECT_EQ(test, (enum tb_port_type)out_port->config.type, test_data[i].out_type); } tb_path_free(path);}
static void tb_test_path_not_bonded_lane1_chain(struct kunit *test){ /* * DP Video path from host to device 3 using lane 1. * * [Host] * 1 :| 2 * 1 :| 2 * [Device #1] * 7 :| 8 * 1 :| 2 * [Device #2] * 5 :| 6 * 1 :| 2 * [Device #3] */ static const struct hop_expectation test_data[] = { { .route = 0x0, .in_port = 5, .in_type = TB_TYPE_DP_HDMI_IN, .out_port = 2, .out_type = TB_TYPE_PORT, }, { .route = 0x1, .in_port = 2, .in_type = TB_TYPE_PORT, .out_port = 8, .out_type = TB_TYPE_PORT, }, { .route = 0x701, .in_port = 2, .in_type = TB_TYPE_PORT, .out_port = 6, .out_type = TB_TYPE_PORT, }, { .route = 0x50701, .in_port = 2, .in_type = TB_TYPE_PORT, .out_port = 13, .out_type = TB_TYPE_DP_HDMI_OUT, }, }; struct tb_switch *host, *dev1, *dev2, *dev3; struct tb_port *in, *out; struct tb_path *path; int i;
host = alloc_host(test); dev1 = alloc_dev_default(test, host, 0x1, false); dev2 = alloc_dev_default(test, dev1, 0x701, false); dev3 = alloc_dev_default(test, dev2, 0x50701, false);
in = &host->ports[5]; out = &dev3->ports[13];
path = tb_path_alloc(NULL, in, 9, out, 9, 1, "Video"); KUNIT_ASSERT_NOT_NULL(test, path); KUNIT_ASSERT_EQ(test, path->path_length, ARRAY_SIZE(test_data)); for (i = 0; i < ARRAY_SIZE(test_data); i++) { const struct tb_port *in_port, *out_port;
in_port = path->hops[i].in_port; out_port = path->hops[i].out_port;
KUNIT_EXPECT_EQ(test, tb_route(in_port->sw), test_data[i].route); KUNIT_EXPECT_EQ(test, in_port->port, test_data[i].in_port); KUNIT_EXPECT_EQ(test, (enum tb_port_type)in_port->config.type, test_data[i].in_type); KUNIT_EXPECT_EQ(test, tb_route(out_port->sw), test_data[i].route); KUNIT_EXPECT_EQ(test, out_port->port, test_data[i].out_port); KUNIT_EXPECT_EQ(test, (enum tb_port_type)out_port->config.type, test_data[i].out_type); } tb_path_free(path);}
static void tb_test_path_not_bonded_lane1_chain_reverse(struct kunit *test){ /* * DP Video path from device 3 to host using lane 1. * * [Host] * 1 :| 2 * 1 :| 2 * [Device #1] * 7 :| 8 * 1 :| 2 * [Device #2] * 5 :| 6 * 1 :| 2 * [Device #3] */ static const struct hop_expectation test_data[] = { { .route = 0x50701, .in_port = 13, .in_type = TB_TYPE_DP_HDMI_IN, .out_port = 2, .out_type = TB_TYPE_PORT, }, { .route = 0x701, .in_port = 6, .in_type = TB_TYPE_PORT, .out_port = 2, .out_type = TB_TYPE_PORT, }, { .route = 0x1, .in_port = 8, .in_type = TB_TYPE_PORT, .out_port = 2, .out_type = TB_TYPE_PORT, }, { .route = 0x0, .in_port = 2, .in_type = TB_TYPE_PORT, .out_port = 5, .out_type = TB_TYPE_DP_HDMI_IN, }, }; struct tb_switch *host, *dev1, *dev2, *dev3; struct tb_port *in, *out; struct tb_path *path; int i;
host = alloc_host(test); dev1 = alloc_dev_default(test, host, 0x1, false); dev2 = alloc_dev_default(test, dev1, 0x701, false); dev3 = alloc_dev_with_dpin(test, dev2, 0x50701, false);
in = &dev3->ports[13]; out = &host->ports[5];
path = tb_path_alloc(NULL, in, 9, out, 9, 1, "Video"); KUNIT_ASSERT_NOT_NULL(test, path); KUNIT_ASSERT_EQ(test, path->path_length, ARRAY_SIZE(test_data)); for (i = 0; i < ARRAY_SIZE(test_data); i++) { const struct tb_port *in_port, *out_port;
in_port = path->hops[i].in_port; out_port = path->hops[i].out_port;
KUNIT_EXPECT_EQ(test, tb_route(in_port->sw), test_data[i].route); KUNIT_EXPECT_EQ(test, in_port->port, test_data[i].in_port); KUNIT_EXPECT_EQ(test, (enum tb_port_type)in_port->config.type, test_data[i].in_type); KUNIT_EXPECT_EQ(test, tb_route(out_port->sw), test_data[i].route); KUNIT_EXPECT_EQ(test, out_port->port, test_data[i].out_port); KUNIT_EXPECT_EQ(test, (enum tb_port_type)out_port->config.type, test_data[i].out_type); } tb_path_free(path);}
static void tb_test_path_mixed_chain(struct kunit *test){ /* * DP Video path from host to device 4 where first and last link * is bonded. * * [Host] * 1 | * 1 | * [Device #1] * 7 :| 8 * 1 :| 2 * [Device #2] * 5 :| 6 * 1 :| 2 * [Device #3] * 3 | * 1 | * [Device #4] */ static const struct hop_expectation test_data[] = { { .route = 0x0, .in_port = 5, .in_type = TB_TYPE_DP_HDMI_IN, .out_port = 1, .out_type = TB_TYPE_PORT, }, { .route = 0x1, .in_port = 1, .in_type = TB_TYPE_PORT, .out_port = 8, .out_type = TB_TYPE_PORT, }, { .route = 0x701, .in_port = 2, .in_type = TB_TYPE_PORT, .out_port = 6, .out_type = TB_TYPE_PORT, }, { .route = 0x50701, .in_port = 2, .in_type = TB_TYPE_PORT, .out_port = 3, .out_type = TB_TYPE_PORT, }, { .route = 0x3050701, .in_port = 1, .in_type = TB_TYPE_PORT, .out_port = 13, .out_type = TB_TYPE_DP_HDMI_OUT, }, }; struct tb_switch *host, *dev1, *dev2, *dev3, *dev4; struct tb_port *in, *out; struct tb_path *path; int i;
host = alloc_host(test); dev1 = alloc_dev_default(test, host, 0x1, true); dev2 = alloc_dev_default(test, dev1, 0x701, false); dev3 = alloc_dev_default(test, dev2, 0x50701, false); dev4 = alloc_dev_default(test, dev3, 0x3050701, true);
in = &host->ports[5]; out = &dev4->ports[13];
path = tb_path_alloc(NULL, in, 9, out, 9, 1, "Video"); KUNIT_ASSERT_NOT_NULL(test, path); KUNIT_ASSERT_EQ(test, path->path_length, ARRAY_SIZE(test_data)); for (i = 0; i < ARRAY_SIZE(test_data); i++) { const struct tb_port *in_port, *out_port;
in_port = path->hops[i].in_port; out_port = path->hops[i].out_port;
KUNIT_EXPECT_EQ(test, tb_route(in_port->sw), test_data[i].route); KUNIT_EXPECT_EQ(test, in_port->port, test_data[i].in_port); KUNIT_EXPECT_EQ(test, (enum tb_port_type)in_port->config.type, test_data[i].in_type); KUNIT_EXPECT_EQ(test, tb_route(out_port->sw), test_data[i].route); KUNIT_EXPECT_EQ(test, out_port->port, test_data[i].out_port); KUNIT_EXPECT_EQ(test, (enum tb_port_type)out_port->config.type, test_data[i].out_type); } tb_path_free(path);}
static void tb_test_path_mixed_chain_reverse(struct kunit *test){ /* * DP Video path from device 4 to host where first and last link * is bonded. * * [Host] * 1 | * 1 | * [Device #1] * 7 :| 8 * 1 :| 2 * [Device #2] * 5 :| 6 * 1 :| 2 * [Device #3] * 3 | * 1 | * [Device #4] */ static const struct hop_expectation test_data[] = { { .route = 0x3050701, .in_port = 13, .in_type = TB_TYPE_DP_HDMI_OUT, .out_port = 1, .out_type = TB_TYPE_PORT, }, { .route = 0x50701, .in_port = 3, .in_type = TB_TYPE_PORT, .out_port = 2, .out_type = TB_TYPE_PORT, }, { .route = 0x701, .in_port = 6, .in_type = TB_TYPE_PORT, .out_port = 2, .out_type = TB_TYPE_PORT, }, { .route = 0x1, .in_port = 8, .in_type = TB_TYPE_PORT, .out_port = 1, .out_type = TB_TYPE_PORT, }, { .route = 0x0, .in_port = 1, .in_type = TB_TYPE_PORT, .out_port = 5, .out_type = TB_TYPE_DP_HDMI_IN, }, }; struct tb_switch *host, *dev1, *dev2, *dev3, *dev4; struct tb_port *in, *out; struct tb_path *path; int i;
host = alloc_host(test); dev1 = alloc_dev_default(test, host, 0x1, true); dev2 = alloc_dev_default(test, dev1, 0x701, false); dev3 = alloc_dev_default(test, dev2, 0x50701, false); dev4 = alloc_dev_default(test, dev3, 0x3050701, true);
in = &dev4->ports[13]; out = &host->ports[5];
path = tb_path_alloc(NULL, in, 9, out, 9, 1, "Video"); KUNIT_ASSERT_NOT_NULL(test, path); KUNIT_ASSERT_EQ(test, path->path_length, ARRAY_SIZE(test_data)); for (i = 0; i < ARRAY_SIZE(test_data); i++) { const struct tb_port *in_port, *out_port;
in_port = path->hops[i].in_port; out_port = path->hops[i].out_port;
KUNIT_EXPECT_EQ(test, tb_route(in_port->sw), test_data[i].route); KUNIT_EXPECT_EQ(test, in_port->port, test_data[i].in_port); KUNIT_EXPECT_EQ(test, (enum tb_port_type)in_port->config.type, test_data[i].in_type); KUNIT_EXPECT_EQ(test, tb_route(out_port->sw), test_data[i].route); KUNIT_EXPECT_EQ(test, out_port->port, test_data[i].out_port); KUNIT_EXPECT_EQ(test, (enum tb_port_type)out_port->config.type, test_data[i].out_type); } tb_path_free(path);}
static void tb_test_tunnel_pcie(struct kunit *test){ struct tb_switch *host, *dev1, *dev2; struct tb_tunnel *tunnel1, *tunnel2; struct tb_port *down, *up;
/* * Create PCIe tunnel between host and two devices. * * [Host] * 1 | * 1 | * [Device #1] * 5 | * 1 | * [Device #2] */ host = alloc_host(test); dev1 = alloc_dev_default(test, host, 0x1, true); dev2 = alloc_dev_default(test, dev1, 0x501, true);
down = &host->ports[8]; up = &dev1->ports[9]; tunnel1 = tb_tunnel_alloc_pci(NULL, up, down); KUNIT_ASSERT_NOT_NULL(test, tunnel1); KUNIT_EXPECT_EQ(test, tunnel1->type, TB_TUNNEL_PCI); KUNIT_EXPECT_PTR_EQ(test, tunnel1->src_port, down); KUNIT_EXPECT_PTR_EQ(test, tunnel1->dst_port, up); KUNIT_ASSERT_EQ(test, tunnel1->npaths, 2); KUNIT_ASSERT_EQ(test, tunnel1->paths[0]->path_length, 2); KUNIT_EXPECT_PTR_EQ(test, tunnel1->paths[0]->hops[0].in_port, down); KUNIT_EXPECT_PTR_EQ(test, tunnel1->paths[0]->hops[1].out_port, up); KUNIT_ASSERT_EQ(test, tunnel1->paths[1]->path_length, 2); KUNIT_EXPECT_PTR_EQ(test, tunnel1->paths[1]->hops[0].in_port, up); KUNIT_EXPECT_PTR_EQ(test, tunnel1->paths[1]->hops[1].out_port, down);
down = &dev1->ports[10]; up = &dev2->ports[9]; tunnel2 = tb_tunnel_alloc_pci(NULL, up, down); KUNIT_ASSERT_NOT_NULL(test, tunnel2); KUNIT_EXPECT_EQ(test, tunnel2->type, TB_TUNNEL_PCI); KUNIT_EXPECT_PTR_EQ(test, tunnel2->src_port, down); KUNIT_EXPECT_PTR_EQ(test, tunnel2->dst_port, up); KUNIT_ASSERT_EQ(test, tunnel2->npaths, 2); KUNIT_ASSERT_EQ(test, tunnel2->paths[0]->path_length, 2); KUNIT_EXPECT_PTR_EQ(test, tunnel2->paths[0]->hops[0].in_port, down); KUNIT_EXPECT_PTR_EQ(test, tunnel2->paths[0]->hops[1].out_port, up); KUNIT_ASSERT_EQ(test, tunnel2->paths[1]->path_length, 2); KUNIT_EXPECT_PTR_EQ(test, tunnel2->paths[1]->hops[0].in_port, up); KUNIT_EXPECT_PTR_EQ(test, tunnel2->paths[1]->hops[1].out_port, down);
tb_tunnel_put(tunnel2); tb_tunnel_put(tunnel1);}
static void tb_test_tunnel_dp(struct kunit *test){ struct tb_switch *host, *dev; struct tb_port *in, *out; struct tb_tunnel *tunnel;
/* * Create DP tunnel between Host and Device * * [Host] * 1 | * 1 | * [Device] */ host = alloc_host(test); dev = alloc_dev_default(test, host, 0x3, true);
in = &host->ports[5]; out = &dev->ports[13];
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL); KUNIT_ASSERT_NOT_NULL(test, tunnel); KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP); KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in); KUNIT_EXPECT_PTR_EQ(test, tunnel->dst_port, out); KUNIT_ASSERT_EQ(test, tunnel->npaths, 3); KUNIT_ASSERT_EQ(test, tunnel->paths[0]->path_length, 2); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].in_port, in); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[1].out_port, out); KUNIT_ASSERT_EQ(test, tunnel->paths[1]->path_length, 2); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[0].in_port, in); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[1].out_port, out); KUNIT_ASSERT_EQ(test, tunnel->paths[2]->path_length, 2); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[2]->hops[0].in_port, out); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[2]->hops[1].out_port, in); tb_tunnel_put(tunnel);}
static void tb_test_tunnel_dp_chain(struct kunit *test){ struct tb_switch *host, *dev1, *dev4; struct tb_port *in, *out; struct tb_tunnel *tunnel;
/* * Create DP tunnel from Host DP IN to Device #4 DP OUT. * * [Host] * 1 | * 1 | * [Device #1] * 3 / | 5 \ 7 * 1 / | \ 1 * [Device #2] | [Device #4] * | 1 * [Device #3] */ host = alloc_host(test); dev1 = alloc_dev_default(test, host, 0x1, true); alloc_dev_default(test, dev1, 0x301, true); alloc_dev_default(test, dev1, 0x501, true); dev4 = alloc_dev_default(test, dev1, 0x701, true);
in = &host->ports[5]; out = &dev4->ports[14];
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL); KUNIT_ASSERT_NOT_NULL(test, tunnel); KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP); KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in); KUNIT_EXPECT_PTR_EQ(test, tunnel->dst_port, out); KUNIT_ASSERT_EQ(test, tunnel->npaths, 3); KUNIT_ASSERT_EQ(test, tunnel->paths[0]->path_length, 3); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].in_port, in); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[2].out_port, out); KUNIT_ASSERT_EQ(test, tunnel->paths[1]->path_length, 3); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[0].in_port, in); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[2].out_port, out); KUNIT_ASSERT_EQ(test, tunnel->paths[2]->path_length, 3); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[2]->hops[0].in_port, out); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[2]->hops[2].out_port, in); tb_tunnel_put(tunnel);}
static void tb_test_tunnel_dp_tree(struct kunit *test){ struct tb_switch *host, *dev1, *dev2, *dev3, *dev5; struct tb_port *in, *out; struct tb_tunnel *tunnel;
/* * Create DP tunnel from Device #2 DP IN to Device #5 DP OUT. * * [Host] * 3 | * 1 | * [Device #1] * 3 / | 5 \ 7 * 1 / | \ 1 * [Device #2] | [Device #4] * | 1 * [Device #3] * | 5 * | 1 * [Device #5] */ host = alloc_host(test); dev1 = alloc_dev_default(test, host, 0x3, true); dev2 = alloc_dev_with_dpin(test, dev1, 0x303, true); dev3 = alloc_dev_default(test, dev1, 0x503, true); alloc_dev_default(test, dev1, 0x703, true); dev5 = alloc_dev_default(test, dev3, 0x50503, true);
in = &dev2->ports[13]; out = &dev5->ports[13];
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL); KUNIT_ASSERT_NOT_NULL(test, tunnel); KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP); KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in); KUNIT_EXPECT_PTR_EQ(test, tunnel->dst_port, out); KUNIT_ASSERT_EQ(test, tunnel->npaths, 3); KUNIT_ASSERT_EQ(test, tunnel->paths[0]->path_length, 4); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].in_port, in); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[3].out_port, out); KUNIT_ASSERT_EQ(test, tunnel->paths[1]->path_length, 4); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[0].in_port, in); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[3].out_port, out); KUNIT_ASSERT_EQ(test, tunnel->paths[2]->path_length, 4); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[2]->hops[0].in_port, out); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[2]->hops[3].out_port, in); tb_tunnel_put(tunnel);}
static void tb_test_tunnel_dp_max_length(struct kunit *test){ struct tb_switch *host, *dev1, *dev2, *dev3, *dev4, *dev5, *dev6; struct tb_switch *dev7, *dev8, *dev9, *dev10, *dev11, *dev12; struct tb_port *in, *out; struct tb_tunnel *tunnel;
/* * Creates DP tunnel from Device #6 to Device #12. * * [Host] * 1 / \ 3 * 1 / \ 1 * [Device #1] [Device #7] * 3 | | 3 * 1 | | 1 * [Device #2] [Device #8] * 3 | | 3 * 1 | | 1 * [Device #3] [Device #9] * 3 | | 3 * 1 | | 1 * [Device #4] [Device #10] * 3 | | 3 * 1 | | 1 * [Device #5] [Device #11] * 3 | | 3 * 1 | | 1 * [Device #6] [Device #12] */ host = alloc_host(test); dev1 = alloc_dev_default(test, host, 0x1, true); dev2 = alloc_dev_default(test, dev1, 0x301, true); dev3 = alloc_dev_default(test, dev2, 0x30301, true); dev4 = alloc_dev_default(test, dev3, 0x3030301, true); dev5 = alloc_dev_default(test, dev4, 0x303030301, true); dev6 = alloc_dev_with_dpin(test, dev5, 0x30303030301, true); dev7 = alloc_dev_default(test, host, 0x3, true); dev8 = alloc_dev_default(test, dev7, 0x303, true); dev9 = alloc_dev_default(test, dev8, 0x30303, true); dev10 = alloc_dev_default(test, dev9, 0x3030303, true); dev11 = alloc_dev_default(test, dev10, 0x303030303, true); dev12 = alloc_dev_default(test, dev11, 0x30303030303, true);
in = &dev6->ports[13]; out = &dev12->ports[13];
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL); KUNIT_ASSERT_NOT_NULL(test, tunnel); KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP); KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in); KUNIT_EXPECT_PTR_EQ(test, tunnel->dst_port, out); KUNIT_ASSERT_EQ(test, tunnel->npaths, 3); KUNIT_ASSERT_EQ(test, tunnel->paths[0]->path_length, 13); /* First hop */ KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].in_port, in); /* Middle */ KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[6].in_port, &host->ports[1]); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[6].out_port, &host->ports[3]); /* Last */ KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[12].out_port, out); KUNIT_ASSERT_EQ(test, tunnel->paths[1]->path_length, 13); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[0].in_port, in); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[6].in_port, &host->ports[1]); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[6].out_port, &host->ports[3]); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[12].out_port, out); KUNIT_ASSERT_EQ(test, tunnel->paths[2]->path_length, 13); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[2]->hops[0].in_port, out); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[2]->hops[6].in_port, &host->ports[3]); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[2]->hops[6].out_port, &host->ports[1]); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[2]->hops[12].out_port, in); tb_tunnel_put(tunnel);}
static void tb_test_tunnel_3dp(struct kunit *test){ struct tb_switch *host, *dev1, *dev2, *dev3, *dev4, *dev5; struct tb_port *in1, *in2, *in3, *out1, *out2, *out3; struct tb_tunnel *tunnel1, *tunnel2, *tunnel3;
/* * Create 3 DP tunnels from Host to Devices #2, #5 and #4. * * [Host] * 3 | * 1 | * [Device #1] * 3 / | 5 \ 7 * 1 / | \ 1 * [Device #2] | [Device #4] * | 1 * [Device #3] * | 5 * | 1 * [Device #5] */ host = alloc_host_br(test); dev1 = alloc_dev_default(test, host, 0x3, true); dev2 = alloc_dev_default(test, dev1, 0x303, true); dev3 = alloc_dev_default(test, dev1, 0x503, true); dev4 = alloc_dev_default(test, dev1, 0x703, true); dev5 = alloc_dev_default(test, dev3, 0x50503, true);
in1 = &host->ports[5]; in2 = &host->ports[6]; in3 = &host->ports[10];
out1 = &dev2->ports[13]; out2 = &dev5->ports[13]; out3 = &dev4->ports[14];
tunnel1 = tb_tunnel_alloc_dp(NULL, in1, out1, 1, 0, 0, NULL, NULL); KUNIT_ASSERT_TRUE(test, tunnel1 != NULL); KUNIT_EXPECT_EQ(test, tunnel1->type, TB_TUNNEL_DP); KUNIT_EXPECT_PTR_EQ(test, tunnel1->src_port, in1); KUNIT_EXPECT_PTR_EQ(test, tunnel1->dst_port, out1); KUNIT_ASSERT_EQ(test, tunnel1->npaths, 3); KUNIT_ASSERT_EQ(test, tunnel1->paths[0]->path_length, 3);
tunnel2 = tb_tunnel_alloc_dp(NULL, in2, out2, 1, 0, 0, NULL, NULL); KUNIT_ASSERT_TRUE(test, tunnel2 != NULL); KUNIT_EXPECT_EQ(test, tunnel2->type, TB_TUNNEL_DP); KUNIT_EXPECT_PTR_EQ(test, tunnel2->src_port, in2); KUNIT_EXPECT_PTR_EQ(test, tunnel2->dst_port, out2); KUNIT_ASSERT_EQ(test, tunnel2->npaths, 3); KUNIT_ASSERT_EQ(test, tunnel2->paths[0]->path_length, 4);
tunnel3 = tb_tunnel_alloc_dp(NULL, in3, out3, 1, 0, 0, NULL, NULL); KUNIT_ASSERT_TRUE(test, tunnel3 != NULL); KUNIT_EXPECT_EQ(test, tunnel3->type, TB_TUNNEL_DP); KUNIT_EXPECT_PTR_EQ(test, tunnel3->src_port, in3); KUNIT_EXPECT_PTR_EQ(test, tunnel3->dst_port, out3); KUNIT_ASSERT_EQ(test, tunnel3->npaths, 3); KUNIT_ASSERT_EQ(test, tunnel3->paths[0]->path_length, 3);
tb_tunnel_put(tunnel3); tb_tunnel_put(tunnel2); tb_tunnel_put(tunnel1);}
static void tb_test_tunnel_usb3(struct kunit *test){ struct tb_switch *host, *dev1, *dev2; struct tb_tunnel *tunnel1, *tunnel2; struct tb_port *down, *up;
/* * Create USB3 tunnel between host and two devices. * * [Host] * 1 | * 1 | * [Device #1] * \ 7 * \ 1 * [Device #2] */ host = alloc_host(test); dev1 = alloc_dev_default(test, host, 0x1, true); dev2 = alloc_dev_default(test, dev1, 0x701, true);
down = &host->ports[12]; up = &dev1->ports[16]; tunnel1 = tb_tunnel_alloc_usb3(NULL, up, down, 0, 0); KUNIT_ASSERT_NOT_NULL(test, tunnel1); KUNIT_EXPECT_EQ(test, tunnel1->type, TB_TUNNEL_USB3); KUNIT_EXPECT_PTR_EQ(test, tunnel1->src_port, down); KUNIT_EXPECT_PTR_EQ(test, tunnel1->dst_port, up); KUNIT_ASSERT_EQ(test, tunnel1->npaths, 2); KUNIT_ASSERT_EQ(test, tunnel1->paths[0]->path_length, 2); KUNIT_EXPECT_PTR_EQ(test, tunnel1->paths[0]->hops[0].in_port, down); KUNIT_EXPECT_PTR_EQ(test, tunnel1->paths[0]->hops[1].out_port, up); KUNIT_ASSERT_EQ(test, tunnel1->paths[1]->path_length, 2); KUNIT_EXPECT_PTR_EQ(test, tunnel1->paths[1]->hops[0].in_port, up); KUNIT_EXPECT_PTR_EQ(test, tunnel1->paths[1]->hops[1].out_port, down);
down = &dev1->ports[17]; up = &dev2->ports[16]; tunnel2 = tb_tunnel_alloc_usb3(NULL, up, down, 0, 0); KUNIT_ASSERT_NOT_NULL(test, tunnel2); KUNIT_EXPECT_EQ(test, tunnel2->type, TB_TUNNEL_USB3); KUNIT_EXPECT_PTR_EQ(test, tunnel2->src_port, down); KUNIT_EXPECT_PTR_EQ(test, tunnel2->dst_port, up); KUNIT_ASSERT_EQ(test, tunnel2->npaths, 2); KUNIT_ASSERT_EQ(test, tunnel2->paths[0]->path_length, 2); KUNIT_EXPECT_PTR_EQ(test, tunnel2->paths[0]->hops[0].in_port, down); KUNIT_EXPECT_PTR_EQ(test, tunnel2->paths[0]->hops[1].out_port, up); KUNIT_ASSERT_EQ(test, tunnel2->paths[1]->path_length, 2); KUNIT_EXPECT_PTR_EQ(test, tunnel2->paths[1]->hops[0].in_port, up); KUNIT_EXPECT_PTR_EQ(test, tunnel2->paths[1]->hops[1].out_port, down);
tb_tunnel_put(tunnel2); tb_tunnel_put(tunnel1);}
static void tb_test_tunnel_port_on_path(struct kunit *test){ struct tb_switch *host, *dev1, *dev2, *dev3, *dev4, *dev5; struct tb_port *in, *out, *port; struct tb_tunnel *dp_tunnel;
/* * [Host] * 3 | * 1 | * [Device #1] * 3 / | 5 \ 7 * 1 / | \ 1 * [Device #2] | [Device #4] * | 1 * [Device #3] * | 5 * | 1 * [Device #5] */ host = alloc_host(test); dev1 = alloc_dev_default(test, host, 0x3, true); dev2 = alloc_dev_with_dpin(test, dev1, 0x303, true); dev3 = alloc_dev_default(test, dev1, 0x503, true); dev4 = alloc_dev_default(test, dev1, 0x703, true); dev5 = alloc_dev_default(test, dev3, 0x50503, true);
in = &dev2->ports[13]; out = &dev5->ports[13];
dp_tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL); KUNIT_ASSERT_NOT_NULL(test, dp_tunnel);
KUNIT_EXPECT_TRUE(test, tb_tunnel_port_on_path(dp_tunnel, in)); KUNIT_EXPECT_TRUE(test, tb_tunnel_port_on_path(dp_tunnel, out));
port = &host->ports[8]; KUNIT_EXPECT_FALSE(test, tb_tunnel_port_on_path(dp_tunnel, port));
port = &host->ports[3]; KUNIT_EXPECT_FALSE(test, tb_tunnel_port_on_path(dp_tunnel, port));
port = &dev1->ports[1]; KUNIT_EXPECT_FALSE(test, tb_tunnel_port_on_path(dp_tunnel, port));
port = &dev1->ports[3]; KUNIT_EXPECT_TRUE(test, tb_tunnel_port_on_path(dp_tunnel, port));
port = &dev1->ports[5]; KUNIT_EXPECT_TRUE(test, tb_tunnel_port_on_path(dp_tunnel, port));
port = &dev1->ports[7]; KUNIT_EXPECT_FALSE(test, tb_tunnel_port_on_path(dp_tunnel, port));
port = &dev3->ports[1]; KUNIT_EXPECT_TRUE(test, tb_tunnel_port_on_path(dp_tunnel, port));
port = &dev5->ports[1]; KUNIT_EXPECT_TRUE(test, tb_tunnel_port_on_path(dp_tunnel, port));
port = &dev4->ports[1]; KUNIT_EXPECT_FALSE(test, tb_tunnel_port_on_path(dp_tunnel, port));
tb_tunnel_put(dp_tunnel);}
static void tb_test_tunnel_dma(struct kunit *test){ struct tb_port *nhi, *port; struct tb_tunnel *tunnel; struct tb_switch *host;
/* * Create DMA tunnel from NHI to port 1 and back. * * [Host 1] * 1 ^ In HopID 1 -> Out HopID 8 * | * v In HopID 8 -> Out HopID 1 * ............ Domain border * | * [Host 2] */ host = alloc_host(test); nhi = &host->ports[7]; port = &host->ports[1];
tunnel = tb_tunnel_alloc_dma(NULL, nhi, port, 8, 1, 8, 1); KUNIT_ASSERT_NOT_NULL(test, tunnel); KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DMA); KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, nhi); KUNIT_EXPECT_PTR_EQ(test, tunnel->dst_port, port); KUNIT_ASSERT_EQ(test, tunnel->npaths, 2); /* RX path */ KUNIT_ASSERT_EQ(test, tunnel->paths[0]->path_length, 1); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].in_port, port); KUNIT_EXPECT_EQ(test, tunnel->paths[0]->hops[0].in_hop_index, 8); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].out_port, nhi); KUNIT_EXPECT_EQ(test, tunnel->paths[0]->hops[0].next_hop_index, 1); /* TX path */ KUNIT_ASSERT_EQ(test, tunnel->paths[1]->path_length, 1); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[0].in_port, nhi); KUNIT_EXPECT_EQ(test, tunnel->paths[1]->hops[0].in_hop_index, 1); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[0].out_port, port); KUNIT_EXPECT_EQ(test, tunnel->paths[1]->hops[0].next_hop_index, 8);
tb_tunnel_put(tunnel);}
static void tb_test_tunnel_dma_rx(struct kunit *test){ struct tb_port *nhi, *port; struct tb_tunnel *tunnel; struct tb_switch *host;
/* * Create DMA RX tunnel from port 1 to NHI. * * [Host 1] * 1 ^ * | * | In HopID 15 -> Out HopID 2 * ............ Domain border * | * [Host 2] */ host = alloc_host(test); nhi = &host->ports[7]; port = &host->ports[1];
tunnel = tb_tunnel_alloc_dma(NULL, nhi, port, -1, -1, 15, 2); KUNIT_ASSERT_NOT_NULL(test, tunnel); KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DMA); KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, nhi); KUNIT_EXPECT_PTR_EQ(test, tunnel->dst_port, port); KUNIT_ASSERT_EQ(test, tunnel->npaths, 1); /* RX path */ KUNIT_ASSERT_EQ(test, tunnel->paths[0]->path_length, 1); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].in_port, port); KUNIT_EXPECT_EQ(test, tunnel->paths[0]->hops[0].in_hop_index, 15); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].out_port, nhi); KUNIT_EXPECT_EQ(test, tunnel->paths[0]->hops[0].next_hop_index, 2);
tb_tunnel_put(tunnel);}
static void tb_test_tunnel_dma_tx(struct kunit *test){ struct tb_port *nhi, *port; struct tb_tunnel *tunnel; struct tb_switch *host;
/* * Create DMA TX tunnel from NHI to port 1. * * [Host 1] * 1 | In HopID 2 -> Out HopID 15 * | * v * ............ Domain border * | * [Host 2] */ host = alloc_host(test); nhi = &host->ports[7]; port = &host->ports[1];
tunnel = tb_tunnel_alloc_dma(NULL, nhi, port, 15, 2, -1, -1); KUNIT_ASSERT_NOT_NULL(test, tunnel); KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DMA); KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, nhi); KUNIT_EXPECT_PTR_EQ(test, tunnel->dst_port, port); KUNIT_ASSERT_EQ(test, tunnel->npaths, 1); /* TX path */ KUNIT_ASSERT_EQ(test, tunnel->paths[0]->path_length, 1); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].in_port, nhi); KUNIT_EXPECT_EQ(test, tunnel->paths[0]->hops[0].in_hop_index, 2); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].out_port, port); KUNIT_EXPECT_EQ(test, tunnel->paths[0]->hops[0].next_hop_index, 15);
tb_tunnel_put(tunnel);}
static void tb_test_tunnel_dma_chain(struct kunit *test){ struct tb_switch *host, *dev1, *dev2; struct tb_port *nhi, *port; struct tb_tunnel *tunnel;
/* * Create DMA tunnel from NHI to Device #2 port 3 and back. * * [Host 1] * 1 ^ In HopID 1 -> Out HopID x * | * 1 | In HopID x -> Out HopID 1 * [Device #1] * 7 \ * 1 \ * [Device #2] * 3 | In HopID x -> Out HopID 8 * | * v In HopID 8 -> Out HopID x * ............ Domain border * | * [Host 2] */ host = alloc_host(test); dev1 = alloc_dev_default(test, host, 0x1, true); dev2 = alloc_dev_default(test, dev1, 0x701, true);
nhi = &host->ports[7]; port = &dev2->ports[3]; tunnel = tb_tunnel_alloc_dma(NULL, nhi, port, 8, 1, 8, 1); KUNIT_ASSERT_NOT_NULL(test, tunnel); KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DMA); KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, nhi); KUNIT_EXPECT_PTR_EQ(test, tunnel->dst_port, port); KUNIT_ASSERT_EQ(test, tunnel->npaths, 2); /* RX path */ KUNIT_ASSERT_EQ(test, tunnel->paths[0]->path_length, 3); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].in_port, port); KUNIT_EXPECT_EQ(test, tunnel->paths[0]->hops[0].in_hop_index, 8); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[0].out_port, &dev2->ports[1]); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[1].in_port, &dev1->ports[7]); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[1].out_port, &dev1->ports[1]); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[2].in_port, &host->ports[1]); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[0]->hops[2].out_port, nhi); KUNIT_EXPECT_EQ(test, tunnel->paths[0]->hops[2].next_hop_index, 1); /* TX path */ KUNIT_ASSERT_EQ(test, tunnel->paths[1]->path_length, 3); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[0].in_port, nhi); KUNIT_EXPECT_EQ(test, tunnel->paths[1]->hops[0].in_hop_index, 1); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[1].in_port, &dev1->ports[1]); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[1].out_port, &dev1->ports[7]); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[2].in_port, &dev2->ports[1]); KUNIT_EXPECT_PTR_EQ(test, tunnel->paths[1]->hops[2].out_port, port); KUNIT_EXPECT_EQ(test, tunnel->paths[1]->hops[2].next_hop_index, 8);
tb_tunnel_put(tunnel);}
static void tb_test_tunnel_dma_match(struct kunit *test){ struct tb_port *nhi, *port; struct tb_tunnel *tunnel; struct tb_switch *host;
host = alloc_host(test); nhi = &host->ports[7]; port = &host->ports[1];
tunnel = tb_tunnel_alloc_dma(NULL, nhi, port, 15, 1, 15, 1); KUNIT_ASSERT_NOT_NULL(test, tunnel);
KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, 15, 1, 15, 1)); KUNIT_ASSERT_FALSE(test, tb_tunnel_match_dma(tunnel, 8, 1, 15, 1)); KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, -1, 15, 1)); KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, 15, 1, -1, -1)); KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, 15, -1, -1, -1)); KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, 1, -1, -1)); KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, -1, 15, -1)); KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, -1, -1, 1)); KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, -1, -1, -1)); KUNIT_ASSERT_FALSE(test, tb_tunnel_match_dma(tunnel, 8, -1, 8, -1));
tb_tunnel_put(tunnel);
tunnel = tb_tunnel_alloc_dma(NULL, nhi, port, 15, 1, -1, -1); KUNIT_ASSERT_NOT_NULL(test, tunnel); KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, 15, 1, -1, -1)); KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, 15, -1, -1, -1)); KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, 1, -1, -1)); KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, -1, -1, -1)); KUNIT_ASSERT_FALSE(test, tb_tunnel_match_dma(tunnel, 15, 1, 15, 1)); KUNIT_ASSERT_FALSE(test, tb_tunnel_match_dma(tunnel, -1, -1, 15, 1)); KUNIT_ASSERT_FALSE(test, tb_tunnel_match_dma(tunnel, 15, 11, -1, -1));
tb_tunnel_put(tunnel);
tunnel = tb_tunnel_alloc_dma(NULL, nhi, port, -1, -1, 15, 11); KUNIT_ASSERT_NOT_NULL(test, tunnel); KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, -1, 15, 11)); KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, -1, 15, -1)); KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, -1, -1, 11)); KUNIT_ASSERT_TRUE(test, tb_tunnel_match_dma(tunnel, -1, -1, -1, -1)); KUNIT_ASSERT_FALSE(test, tb_tunnel_match_dma(tunnel, -1, -1, 15, 1)); KUNIT_ASSERT_FALSE(test, tb_tunnel_match_dma(tunnel, -1, -1, 10, 11)); KUNIT_ASSERT_FALSE(test, tb_tunnel_match_dma(tunnel, 15, 11, -1, -1));
tb_tunnel_put(tunnel);}
static void tb_test_credit_alloc_legacy_not_bonded(struct kunit *test){ struct tb_switch *host, *dev; struct tb_port *up, *down; struct tb_tunnel *tunnel; struct tb_path *path;
host = alloc_host(test); dev = alloc_dev_default(test, host, 0x1, false);
down = &host->ports[8]; up = &dev->ports[9]; tunnel = tb_tunnel_alloc_pci(NULL, up, down); KUNIT_ASSERT_NOT_NULL(test, tunnel); KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)2);
path = tunnel->paths[0]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 16U);
path = tunnel->paths[1]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 16U);
tb_tunnel_put(tunnel);}
static void tb_test_credit_alloc_legacy_bonded(struct kunit *test){ struct tb_switch *host, *dev; struct tb_port *up, *down; struct tb_tunnel *tunnel; struct tb_path *path;
host = alloc_host(test); dev = alloc_dev_default(test, host, 0x1, true);
down = &host->ports[8]; up = &dev->ports[9]; tunnel = tb_tunnel_alloc_pci(NULL, up, down); KUNIT_ASSERT_NOT_NULL(test, tunnel); KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)2);
path = tunnel->paths[0]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 32U);
path = tunnel->paths[1]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 32U);
tb_tunnel_put(tunnel);}
static void tb_test_credit_alloc_pcie(struct kunit *test){ struct tb_switch *host, *dev; struct tb_port *up, *down; struct tb_tunnel *tunnel; struct tb_path *path;
host = alloc_host_usb4(test); dev = alloc_dev_usb4(test, host, 0x1, true);
down = &host->ports[8]; up = &dev->ports[9]; tunnel = tb_tunnel_alloc_pci(NULL, up, down); KUNIT_ASSERT_NOT_NULL(test, tunnel); KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)2);
path = tunnel->paths[0]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 32U);
path = tunnel->paths[1]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 64U);
tb_tunnel_put(tunnel);}
static void tb_test_credit_alloc_without_dp(struct kunit *test){ struct tb_switch *host, *dev; struct tb_port *up, *down; struct tb_tunnel *tunnel; struct tb_path *path;
host = alloc_host_usb4(test); dev = alloc_dev_without_dp(test, host, 0x1, true);
/* * The device has no DP therefore baMinDPmain = baMinDPaux = 0 * * Create PCIe path with buffers less than baMaxPCIe. * * For a device with buffers configurations: * baMaxUSB3 = 109 * baMinDPaux = 0 * baMinDPmain = 0 * baMaxPCIe = 30 * baMaxHI = 1 * Remaining Buffers = Total - (CP + DP) = 120 - (2 + 0) = 118 * PCIe Credits = Max(6, Min(baMaxPCIe, Remaining Buffers - baMaxUSB3) * = Max(6, Min(30, 9) = 9 */ down = &host->ports[8]; up = &dev->ports[9]; tunnel = tb_tunnel_alloc_pci(NULL, up, down); KUNIT_ASSERT_TRUE(test, tunnel != NULL); KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)2);
/* PCIe downstream path */ path = tunnel->paths[0]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 9U);
/* PCIe upstream path */ path = tunnel->paths[1]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 64U);
tb_tunnel_put(tunnel);}
static void tb_test_credit_alloc_dp(struct kunit *test){ struct tb_switch *host, *dev; struct tb_port *in, *out; struct tb_tunnel *tunnel; struct tb_path *path;
host = alloc_host_usb4(test); dev = alloc_dev_usb4(test, host, 0x1, true);
in = &host->ports[5]; out = &dev->ports[14];
tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL); KUNIT_ASSERT_NOT_NULL(test, tunnel); KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)3);
/* Video (main) path */ path = tunnel->paths[0]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 12U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 18U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 0U);
/* AUX TX */ path = tunnel->paths[1]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 1U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
/* AUX RX */ path = tunnel->paths[2]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 1U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
tb_tunnel_put(tunnel);}
static void tb_test_credit_alloc_usb3(struct kunit *test){ struct tb_switch *host, *dev; struct tb_port *up, *down; struct tb_tunnel *tunnel; struct tb_path *path;
host = alloc_host_usb4(test); dev = alloc_dev_usb4(test, host, 0x1, true);
down = &host->ports[12]; up = &dev->ports[16]; tunnel = tb_tunnel_alloc_usb3(NULL, up, down, 0, 0); KUNIT_ASSERT_NOT_NULL(test, tunnel); KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)2);
path = tunnel->paths[0]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
path = tunnel->paths[1]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 32U);
tb_tunnel_put(tunnel);}
static void tb_test_credit_alloc_dma(struct kunit *test){ struct tb_switch *host, *dev; struct tb_port *nhi, *port; struct tb_tunnel *tunnel; struct tb_path *path;
host = alloc_host_usb4(test); dev = alloc_dev_usb4(test, host, 0x1, true);
nhi = &host->ports[7]; port = &dev->ports[3];
tunnel = tb_tunnel_alloc_dma(NULL, nhi, port, 8, 1, 8, 1); KUNIT_ASSERT_NOT_NULL(test, tunnel); KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)2);
/* DMA RX */ path = tunnel->paths[0]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 14U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
/* DMA TX */ path = tunnel->paths[1]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
tb_tunnel_put(tunnel);}
static void tb_test_credit_alloc_dma_multiple(struct kunit *test){ struct tb_tunnel *tunnel1, *tunnel2, *tunnel3; struct tb_switch *host, *dev; struct tb_port *nhi, *port; struct tb_path *path;
host = alloc_host_usb4(test); dev = alloc_dev_usb4(test, host, 0x1, true);
nhi = &host->ports[7]; port = &dev->ports[3];
/* * Create three DMA tunnels through the same ports. With the * default buffers we should be able to create two and the last * one fails. * * For default host we have following buffers for DMA: * * 120 - (2 + 2 * (1 + 0) + 32 + 64 + spare) = 20 * * For device we have following: * * 120 - (2 + 2 * (1 + 18) + 14 + 32 + spare) = 34 * * spare = 14 + 1 = 15 * * So on host the first tunnel gets 14 and the second gets the * remaining 1 and then we run out of buffers. */ tunnel1 = tb_tunnel_alloc_dma(NULL, nhi, port, 8, 1, 8, 1); KUNIT_ASSERT_NOT_NULL(test, tunnel1); KUNIT_ASSERT_EQ(test, tunnel1->npaths, (size_t)2);
path = tunnel1->paths[0]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 14U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
path = tunnel1->paths[1]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
tunnel2 = tb_tunnel_alloc_dma(NULL, nhi, port, 9, 2, 9, 2); KUNIT_ASSERT_NOT_NULL(test, tunnel2); KUNIT_ASSERT_EQ(test, tunnel2->npaths, (size_t)2);
path = tunnel2->paths[0]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 14U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
path = tunnel2->paths[1]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
tunnel3 = tb_tunnel_alloc_dma(NULL, nhi, port, 10, 3, 10, 3); KUNIT_ASSERT_NULL(test, tunnel3);
/* * Release the first DMA tunnel. That should make 14 buffers * available for the next tunnel. */ tb_tunnel_put(tunnel1);
tunnel3 = tb_tunnel_alloc_dma(NULL, nhi, port, 10, 3, 10, 3); KUNIT_ASSERT_NOT_NULL(test, tunnel3);
path = tunnel3->paths[0]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 14U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
path = tunnel3->paths[1]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
tb_tunnel_put(tunnel3); tb_tunnel_put(tunnel2);}
static struct tb_tunnel *TB_TEST_PCIE_TUNNEL(struct kunit *test, struct tb_switch *host, struct tb_switch *dev){ struct tb_port *up, *down; struct tb_tunnel *pcie_tunnel; struct tb_path *path;
down = &host->ports[8]; up = &dev->ports[9]; pcie_tunnel = tb_tunnel_alloc_pci(NULL, up, down); KUNIT_ASSERT_NOT_NULL(test, pcie_tunnel); KUNIT_ASSERT_EQ(test, pcie_tunnel->npaths, (size_t)2);
path = pcie_tunnel->paths[0]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 32U);
path = pcie_tunnel->paths[1]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 64U);
return pcie_tunnel;}
static struct tb_tunnel *TB_TEST_DP_TUNNEL1(struct kunit *test, struct tb_switch *host, struct tb_switch *dev){ struct tb_port *in, *out; struct tb_tunnel *dp_tunnel1; struct tb_path *path;
in = &host->ports[5]; out = &dev->ports[13]; dp_tunnel1 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL); KUNIT_ASSERT_NOT_NULL(test, dp_tunnel1); KUNIT_ASSERT_EQ(test, dp_tunnel1->npaths, (size_t)3);
path = dp_tunnel1->paths[0]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 12U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 18U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 0U);
path = dp_tunnel1->paths[1]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 1U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
path = dp_tunnel1->paths[2]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 1U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
return dp_tunnel1;}
static struct tb_tunnel *TB_TEST_DP_TUNNEL2(struct kunit *test, struct tb_switch *host, struct tb_switch *dev){ struct tb_port *in, *out; struct tb_tunnel *dp_tunnel2; struct tb_path *path;
in = &host->ports[6]; out = &dev->ports[14]; dp_tunnel2 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL); KUNIT_ASSERT_NOT_NULL(test, dp_tunnel2); KUNIT_ASSERT_EQ(test, dp_tunnel2->npaths, (size_t)3);
path = dp_tunnel2->paths[0]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 12U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 18U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 0U);
path = dp_tunnel2->paths[1]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 1U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
path = dp_tunnel2->paths[2]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 1U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
return dp_tunnel2;}
static struct tb_tunnel *TB_TEST_USB3_TUNNEL(struct kunit *test, struct tb_switch *host, struct tb_switch *dev){ struct tb_port *up, *down; struct tb_tunnel *usb3_tunnel; struct tb_path *path;
down = &host->ports[12]; up = &dev->ports[16]; usb3_tunnel = tb_tunnel_alloc_usb3(NULL, up, down, 0, 0); KUNIT_ASSERT_NOT_NULL(test, usb3_tunnel); KUNIT_ASSERT_EQ(test, usb3_tunnel->npaths, (size_t)2);
path = usb3_tunnel->paths[0]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
path = usb3_tunnel->paths[1]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 32U);
return usb3_tunnel;}
static struct tb_tunnel *TB_TEST_DMA_TUNNEL1(struct kunit *test, struct tb_switch *host, struct tb_switch *dev){ struct tb_port *nhi, *port; struct tb_tunnel *dma_tunnel1; struct tb_path *path;
nhi = &host->ports[7]; port = &dev->ports[3]; dma_tunnel1 = tb_tunnel_alloc_dma(NULL, nhi, port, 8, 1, 8, 1); KUNIT_ASSERT_NOT_NULL(test, dma_tunnel1); KUNIT_ASSERT_EQ(test, dma_tunnel1->npaths, (size_t)2);
path = dma_tunnel1->paths[0]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 14U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
path = dma_tunnel1->paths[1]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 14U);
return dma_tunnel1;}
static struct tb_tunnel *TB_TEST_DMA_TUNNEL2(struct kunit *test, struct tb_switch *host, struct tb_switch *dev){ struct tb_port *nhi, *port; struct tb_tunnel *dma_tunnel2; struct tb_path *path;
nhi = &host->ports[7]; port = &dev->ports[3]; dma_tunnel2 = tb_tunnel_alloc_dma(NULL, nhi, port, 9, 2, 9, 2); KUNIT_ASSERT_NOT_NULL(test, dma_tunnel2); KUNIT_ASSERT_EQ(test, dma_tunnel2->npaths, (size_t)2);
path = dma_tunnel2->paths[0]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 14U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
path = dma_tunnel2->paths[1]; KUNIT_ASSERT_EQ(test, path->path_length, 2); KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U); KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 1U);
return dma_tunnel2;}
static void tb_test_credit_alloc_all(struct kunit *test){ struct tb_tunnel *pcie_tunnel, *dp_tunnel1, *dp_tunnel2, *usb3_tunnel; struct tb_tunnel *dma_tunnel1, *dma_tunnel2; struct tb_switch *host, *dev;
/* * Create PCIe, 2 x DP, USB 3.x and two DMA tunnels from host to * device. Expectation is that all these can be established with * the default credit allocation found in Intel hardware. */
host = alloc_host_usb4(test); dev = alloc_dev_usb4(test, host, 0x1, true);
pcie_tunnel = TB_TEST_PCIE_TUNNEL(test, host, dev); dp_tunnel1 = TB_TEST_DP_TUNNEL1(test, host, dev); dp_tunnel2 = TB_TEST_DP_TUNNEL2(test, host, dev); usb3_tunnel = TB_TEST_USB3_TUNNEL(test, host, dev); dma_tunnel1 = TB_TEST_DMA_TUNNEL1(test, host, dev); dma_tunnel2 = TB_TEST_DMA_TUNNEL2(test, host, dev);
tb_tunnel_put(dma_tunnel2); tb_tunnel_put(dma_tunnel1); tb_tunnel_put(usb3_tunnel); tb_tunnel_put(dp_tunnel2); tb_tunnel_put(dp_tunnel1); tb_tunnel_put(pcie_tunnel);}
static const u32 root_directory[] = { 0x55584401, /* "UXD" v1 */ 0x00000018, /* Root directory length */ 0x76656e64, /* "vend" */ 0x6f726964, /* "orid" */ 0x76000001, /* "v" R 1 */ 0x00000a27, /* Immediate value, ! Vendor ID */ 0x76656e64, /* "vend" */ 0x6f726964, /* "orid" */ 0x74000003, /* "t" R 3 */ 0x0000001a, /* Text leaf offset, (“Apple Inc.”) */ 0x64657669, /* "devi" */ 0x63656964, /* "ceid" */ 0x76000001, /* "v" R 1 */ 0x0000000a, /* Immediate value, ! Device ID */ 0x64657669, /* "devi" */ 0x63656964, /* "ceid" */ 0x74000003, /* "t" R 3 */ 0x0000001d, /* Text leaf offset, (“Macintosh”) */ 0x64657669, /* "devi" */ 0x63657276, /* "cerv" */ 0x76000001, /* "v" R 1 */ 0x80000100, /* Immediate value, Device Revision */ 0x6e657477, /* "netw" */ 0x6f726b00, /* "ork" */ 0x44000014, /* "D" R 20 */ 0x00000021, /* Directory data offset, (Network Directory) */ 0x4170706c, /* "Appl" */ 0x6520496e, /* "e In" */ 0x632e0000, /* "c." ! */ 0x4d616369, /* "Maci" */ 0x6e746f73, /* "ntos" */ 0x68000000, /* "h" */ 0x00000000, /* padding */ 0xca8961c6, /* Directory UUID, Network Directory */ 0x9541ce1c, /* Directory UUID, Network Directory */ 0x5949b8bd, /* Directory UUID, Network Directory */ 0x4f5a5f2e, /* Directory UUID, Network Directory */ 0x70727463, /* "prtc" */ 0x69640000, /* "id" */ 0x76000001, /* "v" R 1 */ 0x00000001, /* Immediate value, Network Protocol ID */ 0x70727463, /* "prtc" */ 0x76657273, /* "vers" */ 0x76000001, /* "v" R 1 */ 0x00000001, /* Immediate value, Network Protocol Version */ 0x70727463, /* "prtc" */ 0x72657673, /* "revs" */ 0x76000001, /* "v" R 1 */ 0x00000001, /* Immediate value, Network Protocol Revision */ 0x70727463, /* "prtc" */ 0x73746e73, /* "stns" */ 0x76000001, /* "v" R 1 */ 0x00000000, /* Immediate value, Network Protocol Settings */};
static const uuid_t network_dir_uuid = UUID_INIT(0xc66189ca, 0x1cce, 0x4195, 0xbd, 0xb8, 0x49, 0x59, 0x2e, 0x5f, 0x5a, 0x4f);
static void tb_test_property_parse(struct kunit *test){ struct tb_property_dir *dir, *network_dir; struct tb_property *p;
dir = tb_property_parse_dir(root_directory, ARRAY_SIZE(root_directory)); KUNIT_ASSERT_NOT_NULL(test, dir);
p = tb_property_find(dir, "foo", TB_PROPERTY_TYPE_TEXT); KUNIT_ASSERT_NULL(test, p);
p = tb_property_find(dir, "vendorid", TB_PROPERTY_TYPE_TEXT); KUNIT_ASSERT_NOT_NULL(test, p); KUNIT_EXPECT_STREQ(test, p->value.text, "Apple Inc.");
p = tb_property_find(dir, "vendorid", TB_PROPERTY_TYPE_VALUE); KUNIT_ASSERT_NOT_NULL(test, p); KUNIT_EXPECT_EQ(test, p->value.immediate, 0xa27);
p = tb_property_find(dir, "deviceid", TB_PROPERTY_TYPE_TEXT); KUNIT_ASSERT_NOT_NULL(test, p); KUNIT_EXPECT_STREQ(test, p->value.text, "Macintosh");
p = tb_property_find(dir, "deviceid", TB_PROPERTY_TYPE_VALUE); KUNIT_ASSERT_NOT_NULL(test, p); KUNIT_EXPECT_EQ(test, p->value.immediate, 0xa);
p = tb_property_find(dir, "missing", TB_PROPERTY_TYPE_DIRECTORY); KUNIT_ASSERT_NULL(test, p);
p = tb_property_find(dir, "network", TB_PROPERTY_TYPE_DIRECTORY); KUNIT_ASSERT_NOT_NULL(test, p);
network_dir = p->value.dir; KUNIT_EXPECT_TRUE(test, uuid_equal(network_dir->uuid, &network_dir_uuid));
p = tb_property_find(network_dir, "prtcid", TB_PROPERTY_TYPE_VALUE); KUNIT_ASSERT_NOT_NULL(test, p); KUNIT_EXPECT_EQ(test, p->value.immediate, 0x1);
p = tb_property_find(network_dir, "prtcvers", TB_PROPERTY_TYPE_VALUE); KUNIT_ASSERT_NOT_NULL(test, p); KUNIT_EXPECT_EQ(test, p->value.immediate, 0x1);
p = tb_property_find(network_dir, "prtcrevs", TB_PROPERTY_TYPE_VALUE); KUNIT_ASSERT_NOT_NULL(test, p); KUNIT_EXPECT_EQ(test, p->value.immediate, 0x1);
p = tb_property_find(network_dir, "prtcstns", TB_PROPERTY_TYPE_VALUE); KUNIT_ASSERT_NOT_NULL(test, p); KUNIT_EXPECT_EQ(test, p->value.immediate, 0x0);
p = tb_property_find(network_dir, "deviceid", TB_PROPERTY_TYPE_VALUE); KUNIT_EXPECT_TRUE(test, !p); p = tb_property_find(network_dir, "deviceid", TB_PROPERTY_TYPE_TEXT); KUNIT_EXPECT_TRUE(test, !p);
tb_property_free_dir(dir);}
static void tb_test_property_format(struct kunit *test){ struct tb_property_dir *dir; ssize_t block_len; u32 *block; int ret, i;
dir = tb_property_parse_dir(root_directory, ARRAY_SIZE(root_directory)); KUNIT_ASSERT_NOT_NULL(test, dir);
ret = tb_property_format_dir(dir, NULL, 0); KUNIT_ASSERT_EQ(test, ret, ARRAY_SIZE(root_directory));
block_len = ret;
block = kunit_kzalloc(test, block_len * sizeof(u32), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, block);
ret = tb_property_format_dir(dir, block, block_len); KUNIT_EXPECT_EQ(test, ret, 0);
for (i = 0; i < ARRAY_SIZE(root_directory); i++) KUNIT_EXPECT_EQ(test, root_directory[i], block[i]);
tb_property_free_dir(dir);}
static void compare_dirs(struct kunit *test, struct tb_property_dir *d1, struct tb_property_dir *d2){ struct tb_property *p1, *p2, *tmp; int n1, n2, i;
if (d1->uuid) { KUNIT_ASSERT_NOT_NULL(test, d2->uuid); KUNIT_ASSERT_TRUE(test, uuid_equal(d1->uuid, d2->uuid)); } else { KUNIT_ASSERT_NULL(test, d2->uuid); }
n1 = 0; tb_property_for_each(d1, tmp) n1++; KUNIT_ASSERT_NE(test, n1, 0);
n2 = 0; tb_property_for_each(d2, tmp) n2++; KUNIT_ASSERT_NE(test, n2, 0);
KUNIT_ASSERT_EQ(test, n1, n2);
p1 = NULL; p2 = NULL; for (i = 0; i < n1; i++) { p1 = tb_property_get_next(d1, p1); KUNIT_ASSERT_NOT_NULL(test, p1); p2 = tb_property_get_next(d2, p2); KUNIT_ASSERT_NOT_NULL(test, p2);
KUNIT_ASSERT_STREQ(test, &p1->key[0], &p2->key[0]); KUNIT_ASSERT_EQ(test, p1->type, p2->type); KUNIT_ASSERT_EQ(test, p1->length, p2->length);
switch (p1->type) { case TB_PROPERTY_TYPE_DIRECTORY: KUNIT_ASSERT_NOT_NULL(test, p1->value.dir); KUNIT_ASSERT_NOT_NULL(test, p2->value.dir); compare_dirs(test, p1->value.dir, p2->value.dir); break;
case TB_PROPERTY_TYPE_DATA: KUNIT_ASSERT_NOT_NULL(test, p1->value.data); KUNIT_ASSERT_NOT_NULL(test, p2->value.data); KUNIT_ASSERT_TRUE(test, !memcmp(p1->value.data, p2->value.data, p1->length * 4) ); break;
case TB_PROPERTY_TYPE_TEXT: KUNIT_ASSERT_NOT_NULL(test, p1->value.text); KUNIT_ASSERT_NOT_NULL(test, p2->value.text); KUNIT_ASSERT_STREQ(test, p1->value.text, p2->value.text); break;
case TB_PROPERTY_TYPE_VALUE: KUNIT_ASSERT_EQ(test, p1->value.immediate, p2->value.immediate); break; default: KUNIT_FAIL(test, "unexpected property type"); break; } }}
static void tb_test_property_copy(struct kunit *test){ struct tb_property_dir *src, *dst; u32 *block; int ret, i;
src = tb_property_parse_dir(root_directory, ARRAY_SIZE(root_directory)); KUNIT_ASSERT_NOT_NULL(test, src);
dst = tb_property_copy_dir(src); KUNIT_ASSERT_NOT_NULL(test, dst);
/* Compare the structures */ compare_dirs(test, src, dst);
/* Compare the resulting property block */ ret = tb_property_format_dir(dst, NULL, 0); KUNIT_ASSERT_EQ(test, ret, ARRAY_SIZE(root_directory));
block = kunit_kzalloc(test, sizeof(root_directory), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, block);
ret = tb_property_format_dir(dst, block, ARRAY_SIZE(root_directory)); KUNIT_EXPECT_TRUE(test, !ret);
for (i = 0; i < ARRAY_SIZE(root_directory); i++) KUNIT_EXPECT_EQ(test, root_directory[i], block[i]);
tb_property_free_dir(dst); tb_property_free_dir(src);}
/* * Reproducers for three memory-safety defects in * drivers/thunderbolt/property.c reached from a crafted XDomain * PROPERTIES_RESPONSE payload. Without the fix these trip KASAN or * smash the kernel stack; with the fix each returns NULL cleanly. * * The on-wire entry layout matches struct tb_property_entry in * property.c (private to that translation unit): u32 key_hi, u32 * key_lo, then a packed u32 = (type << 24) | (reserved << 16) | * length, then u32 value. Each entry is 4 dwords. */
static void tb_test_property_parse_u32_wrap(struct kunit *test){ /* * 0x102 dwords: enough for the entry's length field (0x100) to * pass the "entry->length > block_len" gate so the wrap check * is actually exercised. parse_dwdata's downstream OOB read * lands ~16 GiB past the allocation regardless. */ u32 *block = kunit_kzalloc(test, 0x102 * sizeof(u32), GFP_KERNEL); struct tb_property_dir *dir;
KUNIT_ASSERT_NOT_NULL(test, block);
block[0] = 0x55584401; /* "UXD" v1 magic */ block[1] = 0x00000004; /* Root directory length: one entry */
/* * DATA entry whose value 0xffffff00 + length 0x100 wrap to 0 * in u32, passing the sum <= block_len guard even though the * real offset is far past the allocation. */ block[2] = 0x61616161; /* key_hi */ block[3] = 0x61616161; /* key_lo */ block[4] = 0x64000100; /* type=DATA, reserved=0, length=0x100 */ block[5] = 0xffffff00; /* value */
dir = tb_property_parse_dir(block, 0x102); KUNIT_EXPECT_NULL(test, dir); tb_property_free_dir(dir);}
static void tb_test_property_parse_recursion(struct kunit *test){ /* * 10 dwords: rootdir header (2) + parent DIRECTORY entry (4) + * the child entry that lives at dir_offset(2) + UUID(4) = 6, * occupying block[6..9]. Each recursive level re-reads the * same block[6..9] as its first child entry, which is itself * a DIRECTORY pointing at offset 2. */ u32 *block = kunit_kzalloc(test, 10 * sizeof(u32), GFP_KERNEL); struct tb_property_dir *dir;
KUNIT_ASSERT_NOT_NULL(test, block);
block[0] = 0x55584401; /* "UXD" v1 magic */ block[1] = 0x00000004; /* Root directory length: one entry */
/* * DIRECTORY entry pointing at dir_offset = 2 with length = 8. * Non-root parse derives content_offset = 6, content_len = 4, * nentries = 1. block[6..9] is read both as the parent's UUID * (kmemdup'd into dir->uuid) and as the single child entry -- * which is itself a DIRECTORY pointing at offset 2, so the * recursion never terminates and the kernel stack is exhausted. */ block[2] = 0x61616161; /* key_hi */ block[3] = 0x61616161; /* key_lo */ block[4] = 0x44000008; /* type=DIRECTORY, reserved=0, length=8 */ block[5] = 0x00000002; /* value = dir_offset */
block[6] = 0x62626262; /* doubles as UUID dword 0 / child key_hi */ block[7] = 0x62626262; /* doubles as UUID dword 1 / child key_lo */ block[8] = 0x44000008; /* type=DIRECTORY, reserved=0, length=8 */ block[9] = 0x00000002; /* value = dir_offset (back at parent) */
dir = tb_property_parse_dir(block, 10); KUNIT_EXPECT_NULL(test, dir); tb_property_free_dir(dir);}
static void tb_test_property_parse_dir_len_underflow(struct kunit *test){ /* * Allocate exactly 7 dwords (28 bytes) so the kmalloc-32 chunk * leaves a 4-byte slab redzone tail that KASAN-Generic can flag. * With block_len = 7, dir_offset = 4, dir_len = 3, the non-root * UUID kmemdup reads 16 bytes from byte 16, so bytes 28..31 fall * in the redzone and trip a KASAN slab-out-of-bounds report on * the pre-fix kernel. Sizing the buffer at a power of two (32, * 64, ...) puts the over-read into the slab cache tail where * KASAN's generic shadow does not flag it, and the test reduces * to the downstream content_len = dir_len - 4 underflow path * which also returns NULL. */ u32 *block = kunit_kzalloc(test, 7 * sizeof(u32), GFP_KERNEL); struct tb_property_dir *dir;
KUNIT_ASSERT_NOT_NULL(test, block);
block[0] = 0x55584401; /* "UXD" v1 magic */ block[1] = 0x00000004; /* Root directory length: one entry */
/* * DIRECTORY entry with length = 3 pointing at dir_offset = 4. * tb_property_entry_valid() permits value(4) + length(3) <= * block_len(7). Non-root parse begins with a kmemdup of 4 * dwords from dir_offset for the UUID; that read runs past the * 28-byte allocation before the dir_len < 4 reject would fire. */ block[2] = 0x61616161; /* key_hi */ block[3] = 0x61616161; /* key_lo */ block[4] = 0x44000003; /* type=DIRECTORY, reserved=0, length=3 */ block[5] = 0x00000004; /* value = dir_offset */ /* block[6] is the start of the four UUID dwords; block[7..] is OOB. */
dir = tb_property_parse_dir(block, 7); KUNIT_EXPECT_NULL(test, dir); tb_property_free_dir(dir);}
static void tb_test_property_parse_zero_length(struct kunit *test){ u32 *block = kunit_kzalloc(test, 6 * sizeof(u32), GFP_KERNEL); struct tb_property_dir *dir;
KUNIT_ASSERT_NOT_NULL(test, block);
block[0] = 0x55584401; /* rootdir magic */ block[1] = 0x00000004; /* root length: one entry */
block[2] = 0x61616161; /* key_hi */ block[3] = 0x61616161; /* key_lo */ block[4] = 0x74000000; /* type=TEXT, reserved=0, length=0 */ block[5] = 0x00000000; /* value */
dir = tb_property_parse_dir(block, 6); KUNIT_EXPECT_NULL(test, dir); tb_property_free_dir(dir);}
static void tb_test_property_parse_rootdir_overflow(struct kunit *test){ u32 *block = kunit_kzalloc(test, 4 * sizeof(u32), GFP_KERNEL); struct tb_property_dir *dir;
KUNIT_ASSERT_NOT_NULL(test, block);
block[0] = 0x55584401; /* rootdir magic */ block[1] = 0x00000004; /* root length claims 4 dwords of content */ block[2] = 0x61616161; block[3] = 0x61616161;
/* content_offset(2) + content_len(4) = 6 > block_len(4) */ dir = tb_property_parse_dir(block, 4); KUNIT_EXPECT_NULL(test, dir); tb_property_free_dir(dir);}
static void tb_test_property_merge(struct kunit *test){ struct tb_property_dir *dir1, *dir2, *dir3; struct tb_property *p; uuid_t uuid; int ret;
dir1 = tb_property_create_dir(&network_dir_uuid); KUNIT_ASSERT_NOT_NULL(test, dir1); ret = tb_property_add_immediate(dir1, "prtcid", 1); KUNIT_EXPECT_EQ(test, ret, 0); ret = tb_property_add_immediate(dir1, "prtcvers", 1); KUNIT_EXPECT_EQ(test, ret, 0); ret = tb_property_add_immediate(dir1, "prtcrevs", 0); KUNIT_EXPECT_EQ(test, ret, 0); ret = tb_property_add_immediate(dir1, "prtcstns", 0); KUNIT_EXPECT_EQ(test, ret, 0);
dir2 = tb_property_create_dir(&network_dir_uuid); KUNIT_ASSERT_NOT_NULL(test, dir2); ret = tb_property_add_text(dir2, "descr", "This is text"); KUNIT_EXPECT_EQ(test, ret, 0); /* This replaces the value in dir1 */ ret = tb_property_add_immediate(dir2, "prtcvers", 0x1234); KUNIT_EXPECT_EQ(test, ret, 0);
uuid_gen(&uuid); dir3 = tb_property_create_dir(&uuid); KUNIT_ASSERT_NOT_NULL(test, dir3); ret = tb_property_add_immediate(dir3, "value0", 0); KUNIT_EXPECT_EQ(test, ret, 0); ret = tb_property_add_text(dir3, "value1", "Text value"); KUNIT_EXPECT_EQ(test, ret, 0); ret = tb_property_add_dir(dir2, "my", dir3); KUNIT_EXPECT_EQ(test, ret, 0);
ret = tb_property_merge_dir(dir1, dir2, true); KUNIT_EXPECT_EQ(test, ret, 0);
p = tb_property_get_next(dir1, NULL); KUNIT_ASSERT_NOT_NULL(test, p); KUNIT_ASSERT_STREQ(test, &p->key[0], "prtcid"); KUNIT_ASSERT_EQ(test, p->type, TB_PROPERTY_TYPE_VALUE); KUNIT_ASSERT_EQ(test, p->length, 1); KUNIT_ASSERT_EQ(test, p->value.immediate, 1); p = tb_property_get_next(dir1, p); KUNIT_ASSERT_NOT_NULL(test, p); KUNIT_ASSERT_STREQ(test, &p->key[0], "prtcvers"); KUNIT_ASSERT_EQ(test, p->type, TB_PROPERTY_TYPE_VALUE); KUNIT_ASSERT_EQ(test, p->length, 1); KUNIT_ASSERT_EQ(test, p->value.immediate, 0x1234); p = tb_property_get_next(dir1, p); KUNIT_ASSERT_NOT_NULL(test, p); KUNIT_ASSERT_STREQ(test, &p->key[0], "prtcrevs"); KUNIT_ASSERT_EQ(test, p->type, TB_PROPERTY_TYPE_VALUE); KUNIT_ASSERT_EQ(test, p->length, 1); KUNIT_ASSERT_EQ(test, p->value.immediate, 0); p = tb_property_get_next(dir1, p); KUNIT_ASSERT_NOT_NULL(test, p); KUNIT_ASSERT_STREQ(test, &p->key[0], "prtcstns"); KUNIT_ASSERT_EQ(test, p->type, TB_PROPERTY_TYPE_VALUE); KUNIT_ASSERT_EQ(test, p->length, 1); KUNIT_ASSERT_EQ(test, p->value.immediate, 0); p = tb_property_get_next(dir1, p); KUNIT_ASSERT_NOT_NULL(test, p); KUNIT_ASSERT_STREQ(test, &p->key[0], "descr"); KUNIT_ASSERT_EQ(test, p->type, TB_PROPERTY_TYPE_TEXT); KUNIT_ASSERT_EQ(test, p->length, 4); KUNIT_ASSERT_STREQ(test, p->value.text, "This is text"); p = tb_property_get_next(dir1, p); KUNIT_ASSERT_NOT_NULL(test, p); KUNIT_ASSERT_STREQ(test, &p->key[0], "my"); KUNIT_ASSERT_EQ(test, p->type, TB_PROPERTY_TYPE_DIRECTORY); compare_dirs(test, p->value.dir, dir3); p = tb_property_get_next(dir1, p); KUNIT_ASSERT_NULL(test, p);
tb_property_free_dir(dir2); tb_property_free_dir(dir1);}
static struct kunit_case tb_test_cases[] = { KUNIT_CASE(tb_test_property_parse_u32_wrap), KUNIT_CASE(tb_test_property_parse_recursion), KUNIT_CASE(tb_test_property_parse_dir_len_underflow), KUNIT_CASE(tb_test_path_basic), KUNIT_CASE(tb_test_path_not_connected_walk), KUNIT_CASE(tb_test_path_single_hop_walk), KUNIT_CASE(tb_test_path_daisy_chain_walk), KUNIT_CASE(tb_test_path_simple_tree_walk), KUNIT_CASE(tb_test_path_complex_tree_walk), KUNIT_CASE(tb_test_path_max_length_walk), KUNIT_CASE(tb_test_path_not_connected), KUNIT_CASE(tb_test_path_not_bonded_lane0), KUNIT_CASE(tb_test_path_not_bonded_lane1), KUNIT_CASE(tb_test_path_not_bonded_lane1_chain), KUNIT_CASE(tb_test_path_not_bonded_lane1_chain_reverse), KUNIT_CASE(tb_test_path_mixed_chain), KUNIT_CASE(tb_test_path_mixed_chain_reverse), KUNIT_CASE(tb_test_tunnel_pcie), KUNIT_CASE(tb_test_tunnel_dp), KUNIT_CASE(tb_test_tunnel_dp_chain), KUNIT_CASE(tb_test_tunnel_dp_tree), KUNIT_CASE(tb_test_tunnel_dp_max_length), KUNIT_CASE(tb_test_tunnel_3dp), KUNIT_CASE(tb_test_tunnel_port_on_path), KUNIT_CASE(tb_test_tunnel_usb3), KUNIT_CASE(tb_test_tunnel_dma), KUNIT_CASE(tb_test_tunnel_dma_rx), KUNIT_CASE(tb_test_tunnel_dma_tx), KUNIT_CASE(tb_test_tunnel_dma_chain), KUNIT_CASE(tb_test_tunnel_dma_match), KUNIT_CASE(tb_test_credit_alloc_legacy_not_bonded), KUNIT_CASE(tb_test_credit_alloc_legacy_bonded), KUNIT_CASE(tb_test_credit_alloc_pcie), KUNIT_CASE(tb_test_credit_alloc_without_dp), KUNIT_CASE(tb_test_credit_alloc_dp), KUNIT_CASE(tb_test_credit_alloc_usb3), KUNIT_CASE(tb_test_credit_alloc_dma), KUNIT_CASE(tb_test_credit_alloc_dma_multiple), KUNIT_CASE(tb_test_credit_alloc_all), KUNIT_CASE(tb_test_property_parse), KUNIT_CASE(tb_test_property_format), KUNIT_CASE(tb_test_property_copy), KUNIT_CASE(tb_test_property_parse_zero_length), KUNIT_CASE(tb_test_property_parse_rootdir_overflow), KUNIT_CASE(tb_test_property_merge), { }};
static struct kunit_suite tb_test_suite = { .name = "thunderbolt", .test_cases = tb_test_cases,};
kunit_test_suite(tb_test_suite);