123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297 |
- // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
- /* Copyright (c) 2020 Mellanox Technologies Ltd. */
- #include <linux/module.h>
- #include <linux/vdpa.h>
- #include <linux/vringh.h>
- #include <uapi/linux/virtio_net.h>
- #include <uapi/linux/virtio_ids.h>
- #include <uapi/linux/vdpa.h>
- #include <linux/virtio_config.h>
- #include <linux/auxiliary_bus.h>
- #include <linux/mlx5/cq.h>
- #include <linux/mlx5/qp.h>
- #include <linux/mlx5/device.h>
- #include <linux/mlx5/driver.h>
- #include <linux/mlx5/vport.h>
- #include <linux/mlx5/fs.h>
- #include <linux/mlx5/mlx5_ifc_vdpa.h>
- #include <linux/mlx5/mpfs.h>
- #include "mlx5_vdpa.h"
- MODULE_AUTHOR("Eli Cohen <[email protected]>");
- MODULE_DESCRIPTION("Mellanox VDPA driver");
- MODULE_LICENSE("Dual BSD/GPL");
- #define to_mlx5_vdpa_ndev(__mvdev) \
- container_of(__mvdev, struct mlx5_vdpa_net, mvdev)
- #define to_mvdev(__vdev) container_of((__vdev), struct mlx5_vdpa_dev, vdev)
- #define VALID_FEATURES_MASK \
- (BIT_ULL(VIRTIO_NET_F_CSUM) | BIT_ULL(VIRTIO_NET_F_GUEST_CSUM) | \
- BIT_ULL(VIRTIO_NET_F_CTRL_GUEST_OFFLOADS) | BIT_ULL(VIRTIO_NET_F_MTU) | BIT_ULL(VIRTIO_NET_F_MAC) | \
- BIT_ULL(VIRTIO_NET_F_GUEST_TSO4) | BIT_ULL(VIRTIO_NET_F_GUEST_TSO6) | \
- BIT_ULL(VIRTIO_NET_F_GUEST_ECN) | BIT_ULL(VIRTIO_NET_F_GUEST_UFO) | BIT_ULL(VIRTIO_NET_F_HOST_TSO4) | \
- BIT_ULL(VIRTIO_NET_F_HOST_TSO6) | BIT_ULL(VIRTIO_NET_F_HOST_ECN) | BIT_ULL(VIRTIO_NET_F_HOST_UFO) | \
- BIT_ULL(VIRTIO_NET_F_MRG_RXBUF) | BIT_ULL(VIRTIO_NET_F_STATUS) | BIT_ULL(VIRTIO_NET_F_CTRL_VQ) | \
- BIT_ULL(VIRTIO_NET_F_CTRL_RX) | BIT_ULL(VIRTIO_NET_F_CTRL_VLAN) | \
- BIT_ULL(VIRTIO_NET_F_CTRL_RX_EXTRA) | BIT_ULL(VIRTIO_NET_F_GUEST_ANNOUNCE) | \
- BIT_ULL(VIRTIO_NET_F_MQ) | BIT_ULL(VIRTIO_NET_F_CTRL_MAC_ADDR) | BIT_ULL(VIRTIO_NET_F_HASH_REPORT) | \
- BIT_ULL(VIRTIO_NET_F_RSS) | BIT_ULL(VIRTIO_NET_F_RSC_EXT) | BIT_ULL(VIRTIO_NET_F_STANDBY) | \
- BIT_ULL(VIRTIO_NET_F_SPEED_DUPLEX) | BIT_ULL(VIRTIO_F_NOTIFY_ON_EMPTY) | \
- BIT_ULL(VIRTIO_F_ANY_LAYOUT) | BIT_ULL(VIRTIO_F_VERSION_1) | BIT_ULL(VIRTIO_F_ACCESS_PLATFORM) | \
- BIT_ULL(VIRTIO_F_RING_PACKED) | BIT_ULL(VIRTIO_F_ORDER_PLATFORM) | BIT_ULL(VIRTIO_F_SR_IOV))
- #define VALID_STATUS_MASK \
- (VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK | \
- VIRTIO_CONFIG_S_FEATURES_OK | VIRTIO_CONFIG_S_NEEDS_RESET | VIRTIO_CONFIG_S_FAILED)
- #define MLX5_FEATURE(_mvdev, _feature) (!!((_mvdev)->actual_features & BIT_ULL(_feature)))
- #define MLX5V_UNTAGGED 0x1000
- struct mlx5_vdpa_net_resources {
- u32 tisn;
- u32 tdn;
- u32 tirn;
- u32 rqtn;
- bool valid;
- };
- struct mlx5_vdpa_cq_buf {
- struct mlx5_frag_buf_ctrl fbc;
- struct mlx5_frag_buf frag_buf;
- int cqe_size;
- int nent;
- };
- struct mlx5_vdpa_cq {
- struct mlx5_core_cq mcq;
- struct mlx5_vdpa_cq_buf buf;
- struct mlx5_db db;
- int cqe;
- };
- struct mlx5_vdpa_umem {
- struct mlx5_frag_buf_ctrl fbc;
- struct mlx5_frag_buf frag_buf;
- int size;
- u32 id;
- };
- struct mlx5_vdpa_qp {
- struct mlx5_core_qp mqp;
- struct mlx5_frag_buf frag_buf;
- struct mlx5_db db;
- u16 head;
- bool fw;
- };
- struct mlx5_vq_restore_info {
- u32 num_ent;
- u64 desc_addr;
- u64 device_addr;
- u64 driver_addr;
- u16 avail_index;
- u16 used_index;
- bool ready;
- bool restore;
- };
- struct mlx5_vdpa_virtqueue {
- bool ready;
- u64 desc_addr;
- u64 device_addr;
- u64 driver_addr;
- u32 num_ent;
- /* Resources for implementing the notification channel from the device
- * to the driver. fwqp is the firmware end of an RC connection; the
- * other end is vqqp used by the driver. cq is where completions are
- * reported.
- */
- struct mlx5_vdpa_cq cq;
- struct mlx5_vdpa_qp fwqp;
- struct mlx5_vdpa_qp vqqp;
- /* umem resources are required for the virtqueue operation. They're use
- * is internal and they must be provided by the driver.
- */
- struct mlx5_vdpa_umem umem1;
- struct mlx5_vdpa_umem umem2;
- struct mlx5_vdpa_umem umem3;
- u32 counter_set_id;
- bool initialized;
- int index;
- u32 virtq_id;
- struct mlx5_vdpa_net *ndev;
- u16 avail_idx;
- u16 used_idx;
- int fw_state;
- /* keep last in the struct */
- struct mlx5_vq_restore_info ri;
- };
- static bool is_index_valid(struct mlx5_vdpa_dev *mvdev, u16 idx)
- {
- if (!(mvdev->actual_features & BIT_ULL(VIRTIO_NET_F_MQ))) {
- if (!(mvdev->actual_features & BIT_ULL(VIRTIO_NET_F_CTRL_VQ)))
- return idx < 2;
- else
- return idx < 3;
- }
- return idx <= mvdev->max_idx;
- }
- #define MLX5V_MACVLAN_SIZE 256
- struct mlx5_vdpa_net {
- struct mlx5_vdpa_dev mvdev;
- struct mlx5_vdpa_net_resources res;
- struct virtio_net_config config;
- struct mlx5_vdpa_virtqueue *vqs;
- struct vdpa_callback *event_cbs;
- /* Serialize vq resources creation and destruction. This is required
- * since memory map might change and we need to destroy and create
- * resources while driver in operational.
- */
- struct rw_semaphore reslock;
- struct mlx5_flow_table *rxft;
- bool setup;
- u32 cur_num_vqs;
- u32 rqt_size;
- bool nb_registered;
- struct notifier_block nb;
- struct vdpa_callback config_cb;
- struct mlx5_vdpa_wq_ent cvq_ent;
- struct hlist_head macvlan_hash[MLX5V_MACVLAN_SIZE];
- };
- struct macvlan_node {
- struct hlist_node hlist;
- struct mlx5_flow_handle *ucast_rule;
- struct mlx5_flow_handle *mcast_rule;
- u64 macvlan;
- };
- static void free_resources(struct mlx5_vdpa_net *ndev);
- static void init_mvqs(struct mlx5_vdpa_net *ndev);
- static int setup_driver(struct mlx5_vdpa_dev *mvdev);
- static void teardown_driver(struct mlx5_vdpa_net *ndev);
- static bool mlx5_vdpa_debug;
- #define MLX5_CVQ_MAX_ENT 16
- #define MLX5_LOG_VIO_FLAG(_feature) \
- do { \
- if (features & BIT_ULL(_feature)) \
- mlx5_vdpa_info(mvdev, "%s\n", #_feature); \
- } while (0)
- #define MLX5_LOG_VIO_STAT(_status) \
- do { \
- if (status & (_status)) \
- mlx5_vdpa_info(mvdev, "%s\n", #_status); \
- } while (0)
- /* TODO: cross-endian support */
- static inline bool mlx5_vdpa_is_little_endian(struct mlx5_vdpa_dev *mvdev)
- {
- return virtio_legacy_is_little_endian() ||
- (mvdev->actual_features & BIT_ULL(VIRTIO_F_VERSION_1));
- }
- static u16 mlx5vdpa16_to_cpu(struct mlx5_vdpa_dev *mvdev, __virtio16 val)
- {
- return __virtio16_to_cpu(mlx5_vdpa_is_little_endian(mvdev), val);
- }
- static __virtio16 cpu_to_mlx5vdpa16(struct mlx5_vdpa_dev *mvdev, u16 val)
- {
- return __cpu_to_virtio16(mlx5_vdpa_is_little_endian(mvdev), val);
- }
- static u16 ctrl_vq_idx(struct mlx5_vdpa_dev *mvdev)
- {
- if (!(mvdev->actual_features & BIT_ULL(VIRTIO_NET_F_MQ)))
- return 2;
- return mvdev->max_vqs;
- }
- static bool is_ctrl_vq_idx(struct mlx5_vdpa_dev *mvdev, u16 idx)
- {
- return idx == ctrl_vq_idx(mvdev);
- }
- static void print_status(struct mlx5_vdpa_dev *mvdev, u8 status, bool set)
- {
- if (status & ~VALID_STATUS_MASK)
- mlx5_vdpa_warn(mvdev, "Warning: there are invalid status bits 0x%x\n",
- status & ~VALID_STATUS_MASK);
- if (!mlx5_vdpa_debug)
- return;
- mlx5_vdpa_info(mvdev, "driver status %s", set ? "set" : "get");
- if (set && !status) {
- mlx5_vdpa_info(mvdev, "driver resets the device\n");
- return;
- }
- MLX5_LOG_VIO_STAT(VIRTIO_CONFIG_S_ACKNOWLEDGE);
- MLX5_LOG_VIO_STAT(VIRTIO_CONFIG_S_DRIVER);
- MLX5_LOG_VIO_STAT(VIRTIO_CONFIG_S_DRIVER_OK);
- MLX5_LOG_VIO_STAT(VIRTIO_CONFIG_S_FEATURES_OK);
- MLX5_LOG_VIO_STAT(VIRTIO_CONFIG_S_NEEDS_RESET);
- MLX5_LOG_VIO_STAT(VIRTIO_CONFIG_S_FAILED);
- }
- static void print_features(struct mlx5_vdpa_dev *mvdev, u64 features, bool set)
- {
- if (features & ~VALID_FEATURES_MASK)
- mlx5_vdpa_warn(mvdev, "There are invalid feature bits 0x%llx\n",
- features & ~VALID_FEATURES_MASK);
- if (!mlx5_vdpa_debug)
- return;
- mlx5_vdpa_info(mvdev, "driver %s feature bits:\n", set ? "sets" : "reads");
- if (!features)
- mlx5_vdpa_info(mvdev, "all feature bits are cleared\n");
- MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_CSUM);
- MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_GUEST_CSUM);
- MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_CTRL_GUEST_OFFLOADS);
- MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_MTU);
- MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_MAC);
- MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_GUEST_TSO4);
- MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_GUEST_TSO6);
- MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_GUEST_ECN);
- MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_GUEST_UFO);
- MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_HOST_TSO4);
- MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_HOST_TSO6);
- MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_HOST_ECN);
- MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_HOST_UFO);
- MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_MRG_RXBUF);
- MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_STATUS);
- MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_CTRL_VQ);
- MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_CTRL_RX);
- MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_CTRL_VLAN);
- MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_CTRL_RX_EXTRA);
- MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_GUEST_ANNOUNCE);
- MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_MQ);
- MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_CTRL_MAC_ADDR);
- MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_HASH_REPORT);
- MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_RSS);
- MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_RSC_EXT);
- MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_STANDBY);
- MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_SPEED_DUPLEX);
- MLX5_LOG_VIO_FLAG(VIRTIO_F_NOTIFY_ON_EMPTY);
- MLX5_LOG_VIO_FLAG(VIRTIO_F_ANY_LAYOUT);
- MLX5_LOG_VIO_FLAG(VIRTIO_F_VERSION_1);
- MLX5_LOG_VIO_FLAG(VIRTIO_F_ACCESS_PLATFORM);
- MLX5_LOG_VIO_FLAG(VIRTIO_F_RING_PACKED);
- MLX5_LOG_VIO_FLAG(VIRTIO_F_ORDER_PLATFORM);
- MLX5_LOG_VIO_FLAG(VIRTIO_F_SR_IOV);
- }
- static int create_tis(struct mlx5_vdpa_net *ndev)
- {
- struct mlx5_vdpa_dev *mvdev = &ndev->mvdev;
- u32 in[MLX5_ST_SZ_DW(create_tis_in)] = {};
- void *tisc;
- int err;
- tisc = MLX5_ADDR_OF(create_tis_in, in, ctx);
- MLX5_SET(tisc, tisc, transport_domain, ndev->res.tdn);
- err = mlx5_vdpa_create_tis(mvdev, in, &ndev->res.tisn);
- if (err)
- mlx5_vdpa_warn(mvdev, "create TIS (%d)\n", err);
- return err;
- }
- static void destroy_tis(struct mlx5_vdpa_net *ndev)
- {
- mlx5_vdpa_destroy_tis(&ndev->mvdev, ndev->res.tisn);
- }
- #define MLX5_VDPA_CQE_SIZE 64
- #define MLX5_VDPA_LOG_CQE_SIZE ilog2(MLX5_VDPA_CQE_SIZE)
- static int cq_frag_buf_alloc(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_cq_buf *buf, int nent)
- {
- struct mlx5_frag_buf *frag_buf = &buf->frag_buf;
- u8 log_wq_stride = MLX5_VDPA_LOG_CQE_SIZE;
- u8 log_wq_sz = MLX5_VDPA_LOG_CQE_SIZE;
- int err;
- err = mlx5_frag_buf_alloc_node(ndev->mvdev.mdev, nent * MLX5_VDPA_CQE_SIZE, frag_buf,
- ndev->mvdev.mdev->priv.numa_node);
- if (err)
- return err;
- mlx5_init_fbc(frag_buf->frags, log_wq_stride, log_wq_sz, &buf->fbc);
- buf->cqe_size = MLX5_VDPA_CQE_SIZE;
- buf->nent = nent;
- return 0;
- }
- static int umem_frag_buf_alloc(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_umem *umem, int size)
- {
- struct mlx5_frag_buf *frag_buf = &umem->frag_buf;
- return mlx5_frag_buf_alloc_node(ndev->mvdev.mdev, size, frag_buf,
- ndev->mvdev.mdev->priv.numa_node);
- }
- static void cq_frag_buf_free(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_cq_buf *buf)
- {
- mlx5_frag_buf_free(ndev->mvdev.mdev, &buf->frag_buf);
- }
- static void *get_cqe(struct mlx5_vdpa_cq *vcq, int n)
- {
- return mlx5_frag_buf_get_wqe(&vcq->buf.fbc, n);
- }
- static void cq_frag_buf_init(struct mlx5_vdpa_cq *vcq, struct mlx5_vdpa_cq_buf *buf)
- {
- struct mlx5_cqe64 *cqe64;
- void *cqe;
- int i;
- for (i = 0; i < buf->nent; i++) {
- cqe = get_cqe(vcq, i);
- cqe64 = cqe;
- cqe64->op_own = MLX5_CQE_INVALID << 4;
- }
- }
- static void *get_sw_cqe(struct mlx5_vdpa_cq *cq, int n)
- {
- struct mlx5_cqe64 *cqe64 = get_cqe(cq, n & (cq->cqe - 1));
- if (likely(get_cqe_opcode(cqe64) != MLX5_CQE_INVALID) &&
- !((cqe64->op_own & MLX5_CQE_OWNER_MASK) ^ !!(n & cq->cqe)))
- return cqe64;
- return NULL;
- }
- static void rx_post(struct mlx5_vdpa_qp *vqp, int n)
- {
- vqp->head += n;
- vqp->db.db[0] = cpu_to_be32(vqp->head);
- }
- static void qp_prepare(struct mlx5_vdpa_net *ndev, bool fw, void *in,
- struct mlx5_vdpa_virtqueue *mvq, u32 num_ent)
- {
- struct mlx5_vdpa_qp *vqp;
- __be64 *pas;
- void *qpc;
- vqp = fw ? &mvq->fwqp : &mvq->vqqp;
- MLX5_SET(create_qp_in, in, uid, ndev->mvdev.res.uid);
- qpc = MLX5_ADDR_OF(create_qp_in, in, qpc);
- if (vqp->fw) {
- /* Firmware QP is allocated by the driver for the firmware's
- * use so we can skip part of the params as they will be chosen by firmware
- */
- qpc = MLX5_ADDR_OF(create_qp_in, in, qpc);
- MLX5_SET(qpc, qpc, rq_type, MLX5_ZERO_LEN_RQ);
- MLX5_SET(qpc, qpc, no_sq, 1);
- return;
- }
- MLX5_SET(qpc, qpc, st, MLX5_QP_ST_RC);
- MLX5_SET(qpc, qpc, pm_state, MLX5_QP_PM_MIGRATED);
- MLX5_SET(qpc, qpc, pd, ndev->mvdev.res.pdn);
- MLX5_SET(qpc, qpc, mtu, MLX5_QPC_MTU_256_BYTES);
- MLX5_SET(qpc, qpc, uar_page, ndev->mvdev.res.uar->index);
- MLX5_SET(qpc, qpc, log_page_size, vqp->frag_buf.page_shift - MLX5_ADAPTER_PAGE_SHIFT);
- MLX5_SET(qpc, qpc, no_sq, 1);
- MLX5_SET(qpc, qpc, cqn_rcv, mvq->cq.mcq.cqn);
- MLX5_SET(qpc, qpc, log_rq_size, ilog2(num_ent));
- MLX5_SET(qpc, qpc, rq_type, MLX5_NON_ZERO_RQ);
- pas = (__be64 *)MLX5_ADDR_OF(create_qp_in, in, pas);
- mlx5_fill_page_frag_array(&vqp->frag_buf, pas);
- }
- static int rq_buf_alloc(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_qp *vqp, u32 num_ent)
- {
- return mlx5_frag_buf_alloc_node(ndev->mvdev.mdev,
- num_ent * sizeof(struct mlx5_wqe_data_seg), &vqp->frag_buf,
- ndev->mvdev.mdev->priv.numa_node);
- }
- static void rq_buf_free(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_qp *vqp)
- {
- mlx5_frag_buf_free(ndev->mvdev.mdev, &vqp->frag_buf);
- }
- static int qp_create(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq,
- struct mlx5_vdpa_qp *vqp)
- {
- struct mlx5_core_dev *mdev = ndev->mvdev.mdev;
- int inlen = MLX5_ST_SZ_BYTES(create_qp_in);
- u32 out[MLX5_ST_SZ_DW(create_qp_out)] = {};
- void *qpc;
- void *in;
- int err;
- if (!vqp->fw) {
- vqp = &mvq->vqqp;
- err = rq_buf_alloc(ndev, vqp, mvq->num_ent);
- if (err)
- return err;
- err = mlx5_db_alloc(ndev->mvdev.mdev, &vqp->db);
- if (err)
- goto err_db;
- inlen += vqp->frag_buf.npages * sizeof(__be64);
- }
- in = kzalloc(inlen, GFP_KERNEL);
- if (!in) {
- err = -ENOMEM;
- goto err_kzalloc;
- }
- qp_prepare(ndev, vqp->fw, in, mvq, mvq->num_ent);
- qpc = MLX5_ADDR_OF(create_qp_in, in, qpc);
- MLX5_SET(qpc, qpc, st, MLX5_QP_ST_RC);
- MLX5_SET(qpc, qpc, pm_state, MLX5_QP_PM_MIGRATED);
- MLX5_SET(qpc, qpc, pd, ndev->mvdev.res.pdn);
- MLX5_SET(qpc, qpc, mtu, MLX5_QPC_MTU_256_BYTES);
- if (!vqp->fw)
- MLX5_SET64(qpc, qpc, dbr_addr, vqp->db.dma);
- MLX5_SET(create_qp_in, in, opcode, MLX5_CMD_OP_CREATE_QP);
- err = mlx5_cmd_exec(mdev, in, inlen, out, sizeof(out));
- kfree(in);
- if (err)
- goto err_kzalloc;
- vqp->mqp.uid = ndev->mvdev.res.uid;
- vqp->mqp.qpn = MLX5_GET(create_qp_out, out, qpn);
- if (!vqp->fw)
- rx_post(vqp, mvq->num_ent);
- return 0;
- err_kzalloc:
- if (!vqp->fw)
- mlx5_db_free(ndev->mvdev.mdev, &vqp->db);
- err_db:
- if (!vqp->fw)
- rq_buf_free(ndev, vqp);
- return err;
- }
- static void qp_destroy(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_qp *vqp)
- {
- u32 in[MLX5_ST_SZ_DW(destroy_qp_in)] = {};
- MLX5_SET(destroy_qp_in, in, opcode, MLX5_CMD_OP_DESTROY_QP);
- MLX5_SET(destroy_qp_in, in, qpn, vqp->mqp.qpn);
- MLX5_SET(destroy_qp_in, in, uid, ndev->mvdev.res.uid);
- if (mlx5_cmd_exec_in(ndev->mvdev.mdev, destroy_qp, in))
- mlx5_vdpa_warn(&ndev->mvdev, "destroy qp 0x%x\n", vqp->mqp.qpn);
- if (!vqp->fw) {
- mlx5_db_free(ndev->mvdev.mdev, &vqp->db);
- rq_buf_free(ndev, vqp);
- }
- }
- static void *next_cqe_sw(struct mlx5_vdpa_cq *cq)
- {
- return get_sw_cqe(cq, cq->mcq.cons_index);
- }
- static int mlx5_vdpa_poll_one(struct mlx5_vdpa_cq *vcq)
- {
- struct mlx5_cqe64 *cqe64;
- cqe64 = next_cqe_sw(vcq);
- if (!cqe64)
- return -EAGAIN;
- vcq->mcq.cons_index++;
- return 0;
- }
- static void mlx5_vdpa_handle_completions(struct mlx5_vdpa_virtqueue *mvq, int num)
- {
- struct mlx5_vdpa_net *ndev = mvq->ndev;
- struct vdpa_callback *event_cb;
- event_cb = &ndev->event_cbs[mvq->index];
- mlx5_cq_set_ci(&mvq->cq.mcq);
- /* make sure CQ cosumer update is visible to the hardware before updating
- * RX doorbell record.
- */
- dma_wmb();
- rx_post(&mvq->vqqp, num);
- if (event_cb->callback)
- event_cb->callback(event_cb->private);
- }
- static void mlx5_vdpa_cq_comp(struct mlx5_core_cq *mcq, struct mlx5_eqe *eqe)
- {
- struct mlx5_vdpa_virtqueue *mvq = container_of(mcq, struct mlx5_vdpa_virtqueue, cq.mcq);
- struct mlx5_vdpa_net *ndev = mvq->ndev;
- void __iomem *uar_page = ndev->mvdev.res.uar->map;
- int num = 0;
- while (!mlx5_vdpa_poll_one(&mvq->cq)) {
- num++;
- if (num > mvq->num_ent / 2) {
- /* If completions keep coming while we poll, we want to
- * let the hardware know that we consumed them by
- * updating the doorbell record. We also let vdpa core
- * know about this so it passes it on the virtio driver
- * on the guest.
- */
- mlx5_vdpa_handle_completions(mvq, num);
- num = 0;
- }
- }
- if (num)
- mlx5_vdpa_handle_completions(mvq, num);
- mlx5_cq_arm(&mvq->cq.mcq, MLX5_CQ_DB_REQ_NOT, uar_page, mvq->cq.mcq.cons_index);
- }
- static int cq_create(struct mlx5_vdpa_net *ndev, u16 idx, u32 num_ent)
- {
- struct mlx5_vdpa_virtqueue *mvq = &ndev->vqs[idx];
- struct mlx5_core_dev *mdev = ndev->mvdev.mdev;
- void __iomem *uar_page = ndev->mvdev.res.uar->map;
- u32 out[MLX5_ST_SZ_DW(create_cq_out)];
- struct mlx5_vdpa_cq *vcq = &mvq->cq;
- __be64 *pas;
- int inlen;
- void *cqc;
- void *in;
- int err;
- int eqn;
- err = mlx5_db_alloc(mdev, &vcq->db);
- if (err)
- return err;
- vcq->mcq.set_ci_db = vcq->db.db;
- vcq->mcq.arm_db = vcq->db.db + 1;
- vcq->mcq.cqe_sz = 64;
- err = cq_frag_buf_alloc(ndev, &vcq->buf, num_ent);
- if (err)
- goto err_db;
- cq_frag_buf_init(vcq, &vcq->buf);
- inlen = MLX5_ST_SZ_BYTES(create_cq_in) +
- MLX5_FLD_SZ_BYTES(create_cq_in, pas[0]) * vcq->buf.frag_buf.npages;
- in = kzalloc(inlen, GFP_KERNEL);
- if (!in) {
- err = -ENOMEM;
- goto err_vzalloc;
- }
- MLX5_SET(create_cq_in, in, uid, ndev->mvdev.res.uid);
- pas = (__be64 *)MLX5_ADDR_OF(create_cq_in, in, pas);
- mlx5_fill_page_frag_array(&vcq->buf.frag_buf, pas);
- cqc = MLX5_ADDR_OF(create_cq_in, in, cq_context);
- MLX5_SET(cqc, cqc, log_page_size, vcq->buf.frag_buf.page_shift - MLX5_ADAPTER_PAGE_SHIFT);
- /* Use vector 0 by default. Consider adding code to choose least used
- * vector.
- */
- err = mlx5_vector2eqn(mdev, 0, &eqn);
- if (err)
- goto err_vec;
- cqc = MLX5_ADDR_OF(create_cq_in, in, cq_context);
- MLX5_SET(cqc, cqc, log_cq_size, ilog2(num_ent));
- MLX5_SET(cqc, cqc, uar_page, ndev->mvdev.res.uar->index);
- MLX5_SET(cqc, cqc, c_eqn_or_apu_element, eqn);
- MLX5_SET64(cqc, cqc, dbr_addr, vcq->db.dma);
- err = mlx5_core_create_cq(mdev, &vcq->mcq, in, inlen, out, sizeof(out));
- if (err)
- goto err_vec;
- vcq->mcq.comp = mlx5_vdpa_cq_comp;
- vcq->cqe = num_ent;
- vcq->mcq.set_ci_db = vcq->db.db;
- vcq->mcq.arm_db = vcq->db.db + 1;
- mlx5_cq_arm(&mvq->cq.mcq, MLX5_CQ_DB_REQ_NOT, uar_page, mvq->cq.mcq.cons_index);
- kfree(in);
- return 0;
- err_vec:
- kfree(in);
- err_vzalloc:
- cq_frag_buf_free(ndev, &vcq->buf);
- err_db:
- mlx5_db_free(ndev->mvdev.mdev, &vcq->db);
- return err;
- }
- static void cq_destroy(struct mlx5_vdpa_net *ndev, u16 idx)
- {
- struct mlx5_vdpa_virtqueue *mvq = &ndev->vqs[idx];
- struct mlx5_core_dev *mdev = ndev->mvdev.mdev;
- struct mlx5_vdpa_cq *vcq = &mvq->cq;
- if (mlx5_core_destroy_cq(mdev, &vcq->mcq)) {
- mlx5_vdpa_warn(&ndev->mvdev, "destroy CQ 0x%x\n", vcq->mcq.cqn);
- return;
- }
- cq_frag_buf_free(ndev, &vcq->buf);
- mlx5_db_free(ndev->mvdev.mdev, &vcq->db);
- }
- static void set_umem_size(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq, int num,
- struct mlx5_vdpa_umem **umemp)
- {
- struct mlx5_core_dev *mdev = ndev->mvdev.mdev;
- int p_a;
- int p_b;
- switch (num) {
- case 1:
- p_a = MLX5_CAP_DEV_VDPA_EMULATION(mdev, umem_1_buffer_param_a);
- p_b = MLX5_CAP_DEV_VDPA_EMULATION(mdev, umem_1_buffer_param_b);
- *umemp = &mvq->umem1;
- break;
- case 2:
- p_a = MLX5_CAP_DEV_VDPA_EMULATION(mdev, umem_2_buffer_param_a);
- p_b = MLX5_CAP_DEV_VDPA_EMULATION(mdev, umem_2_buffer_param_b);
- *umemp = &mvq->umem2;
- break;
- case 3:
- p_a = MLX5_CAP_DEV_VDPA_EMULATION(mdev, umem_3_buffer_param_a);
- p_b = MLX5_CAP_DEV_VDPA_EMULATION(mdev, umem_3_buffer_param_b);
- *umemp = &mvq->umem3;
- break;
- }
- (*umemp)->size = p_a * mvq->num_ent + p_b;
- }
- static void umem_frag_buf_free(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_umem *umem)
- {
- mlx5_frag_buf_free(ndev->mvdev.mdev, &umem->frag_buf);
- }
- static int create_umem(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq, int num)
- {
- int inlen;
- u32 out[MLX5_ST_SZ_DW(create_umem_out)] = {};
- void *um;
- void *in;
- int err;
- __be64 *pas;
- struct mlx5_vdpa_umem *umem;
- set_umem_size(ndev, mvq, num, &umem);
- err = umem_frag_buf_alloc(ndev, umem, umem->size);
- if (err)
- return err;
- inlen = MLX5_ST_SZ_BYTES(create_umem_in) + MLX5_ST_SZ_BYTES(mtt) * umem->frag_buf.npages;
- in = kzalloc(inlen, GFP_KERNEL);
- if (!in) {
- err = -ENOMEM;
- goto err_in;
- }
- MLX5_SET(create_umem_in, in, opcode, MLX5_CMD_OP_CREATE_UMEM);
- MLX5_SET(create_umem_in, in, uid, ndev->mvdev.res.uid);
- um = MLX5_ADDR_OF(create_umem_in, in, umem);
- MLX5_SET(umem, um, log_page_size, umem->frag_buf.page_shift - MLX5_ADAPTER_PAGE_SHIFT);
- MLX5_SET64(umem, um, num_of_mtt, umem->frag_buf.npages);
- pas = (__be64 *)MLX5_ADDR_OF(umem, um, mtt[0]);
- mlx5_fill_page_frag_array_perm(&umem->frag_buf, pas, MLX5_MTT_PERM_RW);
- err = mlx5_cmd_exec(ndev->mvdev.mdev, in, inlen, out, sizeof(out));
- if (err) {
- mlx5_vdpa_warn(&ndev->mvdev, "create umem(%d)\n", err);
- goto err_cmd;
- }
- kfree(in);
- umem->id = MLX5_GET(create_umem_out, out, umem_id);
- return 0;
- err_cmd:
- kfree(in);
- err_in:
- umem_frag_buf_free(ndev, umem);
- return err;
- }
- static void umem_destroy(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq, int num)
- {
- u32 in[MLX5_ST_SZ_DW(destroy_umem_in)] = {};
- u32 out[MLX5_ST_SZ_DW(destroy_umem_out)] = {};
- struct mlx5_vdpa_umem *umem;
- switch (num) {
- case 1:
- umem = &mvq->umem1;
- break;
- case 2:
- umem = &mvq->umem2;
- break;
- case 3:
- umem = &mvq->umem3;
- break;
- }
- MLX5_SET(destroy_umem_in, in, opcode, MLX5_CMD_OP_DESTROY_UMEM);
- MLX5_SET(destroy_umem_in, in, umem_id, umem->id);
- if (mlx5_cmd_exec(ndev->mvdev.mdev, in, sizeof(in), out, sizeof(out)))
- return;
- umem_frag_buf_free(ndev, umem);
- }
- static int umems_create(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq)
- {
- int num;
- int err;
- for (num = 1; num <= 3; num++) {
- err = create_umem(ndev, mvq, num);
- if (err)
- goto err_umem;
- }
- return 0;
- err_umem:
- for (num--; num > 0; num--)
- umem_destroy(ndev, mvq, num);
- return err;
- }
- static void umems_destroy(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq)
- {
- int num;
- for (num = 3; num > 0; num--)
- umem_destroy(ndev, mvq, num);
- }
- static int get_queue_type(struct mlx5_vdpa_net *ndev)
- {
- u32 type_mask;
- type_mask = MLX5_CAP_DEV_VDPA_EMULATION(ndev->mvdev.mdev, virtio_queue_type);
- /* prefer split queue */
- if (type_mask & MLX5_VIRTIO_EMULATION_CAP_VIRTIO_QUEUE_TYPE_SPLIT)
- return MLX5_VIRTIO_EMULATION_VIRTIO_QUEUE_TYPE_SPLIT;
- WARN_ON(!(type_mask & MLX5_VIRTIO_EMULATION_CAP_VIRTIO_QUEUE_TYPE_PACKED));
- return MLX5_VIRTIO_EMULATION_VIRTIO_QUEUE_TYPE_PACKED;
- }
- static bool vq_is_tx(u16 idx)
- {
- return idx % 2;
- }
- static u16 get_features_12_3(u64 features)
- {
- return (!!(features & BIT_ULL(VIRTIO_NET_F_HOST_TSO4)) << 9) |
- (!!(features & BIT_ULL(VIRTIO_NET_F_HOST_TSO6)) << 8) |
- (!!(features & BIT_ULL(VIRTIO_NET_F_CSUM)) << 7) |
- (!!(features & BIT_ULL(VIRTIO_NET_F_GUEST_CSUM)) << 6);
- }
- static bool counters_supported(const struct mlx5_vdpa_dev *mvdev)
- {
- return MLX5_CAP_GEN_64(mvdev->mdev, general_obj_types) &
- BIT_ULL(MLX5_OBJ_TYPE_VIRTIO_Q_COUNTERS);
- }
- static int create_virtqueue(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq)
- {
- int inlen = MLX5_ST_SZ_BYTES(create_virtio_net_q_in);
- u32 out[MLX5_ST_SZ_DW(create_virtio_net_q_out)] = {};
- void *obj_context;
- void *cmd_hdr;
- void *vq_ctx;
- void *in;
- int err;
- err = umems_create(ndev, mvq);
- if (err)
- return err;
- in = kzalloc(inlen, GFP_KERNEL);
- if (!in) {
- err = -ENOMEM;
- goto err_alloc;
- }
- cmd_hdr = MLX5_ADDR_OF(create_virtio_net_q_in, in, general_obj_in_cmd_hdr);
- MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, opcode, MLX5_CMD_OP_CREATE_GENERAL_OBJECT);
- MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, obj_type, MLX5_OBJ_TYPE_VIRTIO_NET_Q);
- MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, uid, ndev->mvdev.res.uid);
- obj_context = MLX5_ADDR_OF(create_virtio_net_q_in, in, obj_context);
- MLX5_SET(virtio_net_q_object, obj_context, hw_available_index, mvq->avail_idx);
- MLX5_SET(virtio_net_q_object, obj_context, hw_used_index, mvq->used_idx);
- MLX5_SET(virtio_net_q_object, obj_context, queue_feature_bit_mask_12_3,
- get_features_12_3(ndev->mvdev.actual_features));
- vq_ctx = MLX5_ADDR_OF(virtio_net_q_object, obj_context, virtio_q_context);
- MLX5_SET(virtio_q, vq_ctx, virtio_q_type, get_queue_type(ndev));
- if (vq_is_tx(mvq->index))
- MLX5_SET(virtio_net_q_object, obj_context, tisn_or_qpn, ndev->res.tisn);
- MLX5_SET(virtio_q, vq_ctx, event_mode, MLX5_VIRTIO_Q_EVENT_MODE_QP_MODE);
- MLX5_SET(virtio_q, vq_ctx, queue_index, mvq->index);
- MLX5_SET(virtio_q, vq_ctx, event_qpn_or_msix, mvq->fwqp.mqp.qpn);
- MLX5_SET(virtio_q, vq_ctx, queue_size, mvq->num_ent);
- MLX5_SET(virtio_q, vq_ctx, virtio_version_1_0,
- !!(ndev->mvdev.actual_features & BIT_ULL(VIRTIO_F_VERSION_1)));
- MLX5_SET64(virtio_q, vq_ctx, desc_addr, mvq->desc_addr);
- MLX5_SET64(virtio_q, vq_ctx, used_addr, mvq->device_addr);
- MLX5_SET64(virtio_q, vq_ctx, available_addr, mvq->driver_addr);
- MLX5_SET(virtio_q, vq_ctx, virtio_q_mkey, ndev->mvdev.mr.mkey);
- MLX5_SET(virtio_q, vq_ctx, umem_1_id, mvq->umem1.id);
- MLX5_SET(virtio_q, vq_ctx, umem_1_size, mvq->umem1.size);
- MLX5_SET(virtio_q, vq_ctx, umem_2_id, mvq->umem2.id);
- MLX5_SET(virtio_q, vq_ctx, umem_2_size, mvq->umem2.size);
- MLX5_SET(virtio_q, vq_ctx, umem_3_id, mvq->umem3.id);
- MLX5_SET(virtio_q, vq_ctx, umem_3_size, mvq->umem3.size);
- MLX5_SET(virtio_q, vq_ctx, pd, ndev->mvdev.res.pdn);
- if (counters_supported(&ndev->mvdev))
- MLX5_SET(virtio_q, vq_ctx, counter_set_id, mvq->counter_set_id);
- err = mlx5_cmd_exec(ndev->mvdev.mdev, in, inlen, out, sizeof(out));
- if (err)
- goto err_cmd;
- mvq->fw_state = MLX5_VIRTIO_NET_Q_OBJECT_STATE_INIT;
- kfree(in);
- mvq->virtq_id = MLX5_GET(general_obj_out_cmd_hdr, out, obj_id);
- return 0;
- err_cmd:
- kfree(in);
- err_alloc:
- umems_destroy(ndev, mvq);
- return err;
- }
- static void destroy_virtqueue(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq)
- {
- u32 in[MLX5_ST_SZ_DW(destroy_virtio_net_q_in)] = {};
- u32 out[MLX5_ST_SZ_DW(destroy_virtio_net_q_out)] = {};
- MLX5_SET(destroy_virtio_net_q_in, in, general_obj_out_cmd_hdr.opcode,
- MLX5_CMD_OP_DESTROY_GENERAL_OBJECT);
- MLX5_SET(destroy_virtio_net_q_in, in, general_obj_out_cmd_hdr.obj_id, mvq->virtq_id);
- MLX5_SET(destroy_virtio_net_q_in, in, general_obj_out_cmd_hdr.uid, ndev->mvdev.res.uid);
- MLX5_SET(destroy_virtio_net_q_in, in, general_obj_out_cmd_hdr.obj_type,
- MLX5_OBJ_TYPE_VIRTIO_NET_Q);
- if (mlx5_cmd_exec(ndev->mvdev.mdev, in, sizeof(in), out, sizeof(out))) {
- mlx5_vdpa_warn(&ndev->mvdev, "destroy virtqueue 0x%x\n", mvq->virtq_id);
- return;
- }
- mvq->fw_state = MLX5_VIRTIO_NET_Q_OBJECT_NONE;
- umems_destroy(ndev, mvq);
- }
- static u32 get_rqpn(struct mlx5_vdpa_virtqueue *mvq, bool fw)
- {
- return fw ? mvq->vqqp.mqp.qpn : mvq->fwqp.mqp.qpn;
- }
- static u32 get_qpn(struct mlx5_vdpa_virtqueue *mvq, bool fw)
- {
- return fw ? mvq->fwqp.mqp.qpn : mvq->vqqp.mqp.qpn;
- }
- static void alloc_inout(struct mlx5_vdpa_net *ndev, int cmd, void **in, int *inlen, void **out,
- int *outlen, u32 qpn, u32 rqpn)
- {
- void *qpc;
- void *pp;
- switch (cmd) {
- case MLX5_CMD_OP_2RST_QP:
- *inlen = MLX5_ST_SZ_BYTES(qp_2rst_in);
- *outlen = MLX5_ST_SZ_BYTES(qp_2rst_out);
- *in = kzalloc(*inlen, GFP_KERNEL);
- *out = kzalloc(*outlen, GFP_KERNEL);
- if (!*in || !*out)
- goto outerr;
- MLX5_SET(qp_2rst_in, *in, opcode, cmd);
- MLX5_SET(qp_2rst_in, *in, uid, ndev->mvdev.res.uid);
- MLX5_SET(qp_2rst_in, *in, qpn, qpn);
- break;
- case MLX5_CMD_OP_RST2INIT_QP:
- *inlen = MLX5_ST_SZ_BYTES(rst2init_qp_in);
- *outlen = MLX5_ST_SZ_BYTES(rst2init_qp_out);
- *in = kzalloc(*inlen, GFP_KERNEL);
- *out = kzalloc(MLX5_ST_SZ_BYTES(rst2init_qp_out), GFP_KERNEL);
- if (!*in || !*out)
- goto outerr;
- MLX5_SET(rst2init_qp_in, *in, opcode, cmd);
- MLX5_SET(rst2init_qp_in, *in, uid, ndev->mvdev.res.uid);
- MLX5_SET(rst2init_qp_in, *in, qpn, qpn);
- qpc = MLX5_ADDR_OF(rst2init_qp_in, *in, qpc);
- MLX5_SET(qpc, qpc, remote_qpn, rqpn);
- MLX5_SET(qpc, qpc, rwe, 1);
- pp = MLX5_ADDR_OF(qpc, qpc, primary_address_path);
- MLX5_SET(ads, pp, vhca_port_num, 1);
- break;
- case MLX5_CMD_OP_INIT2RTR_QP:
- *inlen = MLX5_ST_SZ_BYTES(init2rtr_qp_in);
- *outlen = MLX5_ST_SZ_BYTES(init2rtr_qp_out);
- *in = kzalloc(*inlen, GFP_KERNEL);
- *out = kzalloc(MLX5_ST_SZ_BYTES(init2rtr_qp_out), GFP_KERNEL);
- if (!*in || !*out)
- goto outerr;
- MLX5_SET(init2rtr_qp_in, *in, opcode, cmd);
- MLX5_SET(init2rtr_qp_in, *in, uid, ndev->mvdev.res.uid);
- MLX5_SET(init2rtr_qp_in, *in, qpn, qpn);
- qpc = MLX5_ADDR_OF(rst2init_qp_in, *in, qpc);
- MLX5_SET(qpc, qpc, mtu, MLX5_QPC_MTU_256_BYTES);
- MLX5_SET(qpc, qpc, log_msg_max, 30);
- MLX5_SET(qpc, qpc, remote_qpn, rqpn);
- pp = MLX5_ADDR_OF(qpc, qpc, primary_address_path);
- MLX5_SET(ads, pp, fl, 1);
- break;
- case MLX5_CMD_OP_RTR2RTS_QP:
- *inlen = MLX5_ST_SZ_BYTES(rtr2rts_qp_in);
- *outlen = MLX5_ST_SZ_BYTES(rtr2rts_qp_out);
- *in = kzalloc(*inlen, GFP_KERNEL);
- *out = kzalloc(MLX5_ST_SZ_BYTES(rtr2rts_qp_out), GFP_KERNEL);
- if (!*in || !*out)
- goto outerr;
- MLX5_SET(rtr2rts_qp_in, *in, opcode, cmd);
- MLX5_SET(rtr2rts_qp_in, *in, uid, ndev->mvdev.res.uid);
- MLX5_SET(rtr2rts_qp_in, *in, qpn, qpn);
- qpc = MLX5_ADDR_OF(rst2init_qp_in, *in, qpc);
- pp = MLX5_ADDR_OF(qpc, qpc, primary_address_path);
- MLX5_SET(ads, pp, ack_timeout, 14);
- MLX5_SET(qpc, qpc, retry_count, 7);
- MLX5_SET(qpc, qpc, rnr_retry, 7);
- break;
- default:
- goto outerr_nullify;
- }
- return;
- outerr:
- kfree(*in);
- kfree(*out);
- outerr_nullify:
- *in = NULL;
- *out = NULL;
- }
- static void free_inout(void *in, void *out)
- {
- kfree(in);
- kfree(out);
- }
- /* Two QPs are used by each virtqueue. One is used by the driver and one by
- * firmware. The fw argument indicates whether the subjected QP is the one used
- * by firmware.
- */
- static int modify_qp(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq, bool fw, int cmd)
- {
- int outlen;
- int inlen;
- void *out;
- void *in;
- int err;
- alloc_inout(ndev, cmd, &in, &inlen, &out, &outlen, get_qpn(mvq, fw), get_rqpn(mvq, fw));
- if (!in || !out)
- return -ENOMEM;
- err = mlx5_cmd_exec(ndev->mvdev.mdev, in, inlen, out, outlen);
- free_inout(in, out);
- return err;
- }
- static int connect_qps(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq)
- {
- int err;
- err = modify_qp(ndev, mvq, true, MLX5_CMD_OP_2RST_QP);
- if (err)
- return err;
- err = modify_qp(ndev, mvq, false, MLX5_CMD_OP_2RST_QP);
- if (err)
- return err;
- err = modify_qp(ndev, mvq, true, MLX5_CMD_OP_RST2INIT_QP);
- if (err)
- return err;
- err = modify_qp(ndev, mvq, false, MLX5_CMD_OP_RST2INIT_QP);
- if (err)
- return err;
- err = modify_qp(ndev, mvq, true, MLX5_CMD_OP_INIT2RTR_QP);
- if (err)
- return err;
- err = modify_qp(ndev, mvq, false, MLX5_CMD_OP_INIT2RTR_QP);
- if (err)
- return err;
- return modify_qp(ndev, mvq, true, MLX5_CMD_OP_RTR2RTS_QP);
- }
- struct mlx5_virtq_attr {
- u8 state;
- u16 available_index;
- u16 used_index;
- };
- static int query_virtqueue(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq,
- struct mlx5_virtq_attr *attr)
- {
- int outlen = MLX5_ST_SZ_BYTES(query_virtio_net_q_out);
- u32 in[MLX5_ST_SZ_DW(query_virtio_net_q_in)] = {};
- void *out;
- void *obj_context;
- void *cmd_hdr;
- int err;
- out = kzalloc(outlen, GFP_KERNEL);
- if (!out)
- return -ENOMEM;
- cmd_hdr = MLX5_ADDR_OF(query_virtio_net_q_in, in, general_obj_in_cmd_hdr);
- MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, opcode, MLX5_CMD_OP_QUERY_GENERAL_OBJECT);
- MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, obj_type, MLX5_OBJ_TYPE_VIRTIO_NET_Q);
- MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, obj_id, mvq->virtq_id);
- MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, uid, ndev->mvdev.res.uid);
- err = mlx5_cmd_exec(ndev->mvdev.mdev, in, sizeof(in), out, outlen);
- if (err)
- goto err_cmd;
- obj_context = MLX5_ADDR_OF(query_virtio_net_q_out, out, obj_context);
- memset(attr, 0, sizeof(*attr));
- attr->state = MLX5_GET(virtio_net_q_object, obj_context, state);
- attr->available_index = MLX5_GET(virtio_net_q_object, obj_context, hw_available_index);
- attr->used_index = MLX5_GET(virtio_net_q_object, obj_context, hw_used_index);
- kfree(out);
- return 0;
- err_cmd:
- kfree(out);
- return err;
- }
- static bool is_valid_state_change(int oldstate, int newstate)
- {
- switch (oldstate) {
- case MLX5_VIRTIO_NET_Q_OBJECT_STATE_INIT:
- return newstate == MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY;
- case MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY:
- return newstate == MLX5_VIRTIO_NET_Q_OBJECT_STATE_SUSPEND;
- case MLX5_VIRTIO_NET_Q_OBJECT_STATE_SUSPEND:
- case MLX5_VIRTIO_NET_Q_OBJECT_STATE_ERR:
- default:
- return false;
- }
- }
- static int modify_virtqueue(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq, int state)
- {
- int inlen = MLX5_ST_SZ_BYTES(modify_virtio_net_q_in);
- u32 out[MLX5_ST_SZ_DW(modify_virtio_net_q_out)] = {};
- void *obj_context;
- void *cmd_hdr;
- void *in;
- int err;
- if (mvq->fw_state == MLX5_VIRTIO_NET_Q_OBJECT_NONE)
- return 0;
- if (!is_valid_state_change(mvq->fw_state, state))
- return -EINVAL;
- in = kzalloc(inlen, GFP_KERNEL);
- if (!in)
- return -ENOMEM;
- cmd_hdr = MLX5_ADDR_OF(modify_virtio_net_q_in, in, general_obj_in_cmd_hdr);
- MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, opcode, MLX5_CMD_OP_MODIFY_GENERAL_OBJECT);
- MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, obj_type, MLX5_OBJ_TYPE_VIRTIO_NET_Q);
- MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, obj_id, mvq->virtq_id);
- MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, uid, ndev->mvdev.res.uid);
- obj_context = MLX5_ADDR_OF(modify_virtio_net_q_in, in, obj_context);
- MLX5_SET64(virtio_net_q_object, obj_context, modify_field_select,
- MLX5_VIRTQ_MODIFY_MASK_STATE);
- MLX5_SET(virtio_net_q_object, obj_context, state, state);
- err = mlx5_cmd_exec(ndev->mvdev.mdev, in, inlen, out, sizeof(out));
- kfree(in);
- if (!err)
- mvq->fw_state = state;
- return err;
- }
- static int counter_set_alloc(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq)
- {
- u32 in[MLX5_ST_SZ_DW(create_virtio_q_counters_in)] = {};
- u32 out[MLX5_ST_SZ_DW(create_virtio_q_counters_out)] = {};
- void *cmd_hdr;
- int err;
- if (!counters_supported(&ndev->mvdev))
- return 0;
- cmd_hdr = MLX5_ADDR_OF(create_virtio_q_counters_in, in, hdr);
- MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, opcode, MLX5_CMD_OP_CREATE_GENERAL_OBJECT);
- MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, obj_type, MLX5_OBJ_TYPE_VIRTIO_Q_COUNTERS);
- MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, uid, ndev->mvdev.res.uid);
- err = mlx5_cmd_exec(ndev->mvdev.mdev, in, sizeof(in), out, sizeof(out));
- if (err)
- return err;
- mvq->counter_set_id = MLX5_GET(general_obj_out_cmd_hdr, out, obj_id);
- return 0;
- }
- static void counter_set_dealloc(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq)
- {
- u32 in[MLX5_ST_SZ_DW(destroy_virtio_q_counters_in)] = {};
- u32 out[MLX5_ST_SZ_DW(destroy_virtio_q_counters_out)] = {};
- if (!counters_supported(&ndev->mvdev))
- return;
- MLX5_SET(destroy_virtio_q_counters_in, in, hdr.opcode, MLX5_CMD_OP_DESTROY_GENERAL_OBJECT);
- MLX5_SET(destroy_virtio_q_counters_in, in, hdr.obj_id, mvq->counter_set_id);
- MLX5_SET(destroy_virtio_q_counters_in, in, hdr.uid, ndev->mvdev.res.uid);
- MLX5_SET(destroy_virtio_q_counters_in, in, hdr.obj_type, MLX5_OBJ_TYPE_VIRTIO_Q_COUNTERS);
- if (mlx5_cmd_exec(ndev->mvdev.mdev, in, sizeof(in), out, sizeof(out)))
- mlx5_vdpa_warn(&ndev->mvdev, "dealloc counter set 0x%x\n", mvq->counter_set_id);
- }
- static int setup_vq(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq)
- {
- u16 idx = mvq->index;
- int err;
- if (!mvq->num_ent)
- return 0;
- if (mvq->initialized)
- return 0;
- err = cq_create(ndev, idx, mvq->num_ent);
- if (err)
- return err;
- err = qp_create(ndev, mvq, &mvq->fwqp);
- if (err)
- goto err_fwqp;
- err = qp_create(ndev, mvq, &mvq->vqqp);
- if (err)
- goto err_vqqp;
- err = connect_qps(ndev, mvq);
- if (err)
- goto err_connect;
- err = counter_set_alloc(ndev, mvq);
- if (err)
- goto err_counter;
- err = create_virtqueue(ndev, mvq);
- if (err)
- goto err_connect;
- if (mvq->ready) {
- err = modify_virtqueue(ndev, mvq, MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY);
- if (err) {
- mlx5_vdpa_warn(&ndev->mvdev, "failed to modify to ready vq idx %d(%d)\n",
- idx, err);
- goto err_connect;
- }
- }
- mvq->initialized = true;
- return 0;
- err_connect:
- counter_set_dealloc(ndev, mvq);
- err_counter:
- qp_destroy(ndev, &mvq->vqqp);
- err_vqqp:
- qp_destroy(ndev, &mvq->fwqp);
- err_fwqp:
- cq_destroy(ndev, idx);
- return err;
- }
- static void suspend_vq(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq)
- {
- struct mlx5_virtq_attr attr;
- if (!mvq->initialized)
- return;
- if (mvq->fw_state != MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY)
- return;
- if (modify_virtqueue(ndev, mvq, MLX5_VIRTIO_NET_Q_OBJECT_STATE_SUSPEND))
- mlx5_vdpa_warn(&ndev->mvdev, "modify to suspend failed\n");
- if (query_virtqueue(ndev, mvq, &attr)) {
- mlx5_vdpa_warn(&ndev->mvdev, "failed to query virtqueue\n");
- return;
- }
- mvq->avail_idx = attr.available_index;
- mvq->used_idx = attr.used_index;
- }
- static void suspend_vqs(struct mlx5_vdpa_net *ndev)
- {
- int i;
- for (i = 0; i < ndev->mvdev.max_vqs; i++)
- suspend_vq(ndev, &ndev->vqs[i]);
- }
- static void teardown_vq(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq)
- {
- if (!mvq->initialized)
- return;
- suspend_vq(ndev, mvq);
- destroy_virtqueue(ndev, mvq);
- counter_set_dealloc(ndev, mvq);
- qp_destroy(ndev, &mvq->vqqp);
- qp_destroy(ndev, &mvq->fwqp);
- cq_destroy(ndev, mvq->index);
- mvq->initialized = false;
- }
- static int create_rqt(struct mlx5_vdpa_net *ndev)
- {
- int rqt_table_size = roundup_pow_of_two(ndev->rqt_size);
- int act_sz = roundup_pow_of_two(ndev->cur_num_vqs / 2);
- __be32 *list;
- void *rqtc;
- int inlen;
- void *in;
- int i, j;
- int err;
- inlen = MLX5_ST_SZ_BYTES(create_rqt_in) + rqt_table_size * MLX5_ST_SZ_BYTES(rq_num);
- in = kzalloc(inlen, GFP_KERNEL);
- if (!in)
- return -ENOMEM;
- MLX5_SET(create_rqt_in, in, uid, ndev->mvdev.res.uid);
- rqtc = MLX5_ADDR_OF(create_rqt_in, in, rqt_context);
- MLX5_SET(rqtc, rqtc, list_q_type, MLX5_RQTC_LIST_Q_TYPE_VIRTIO_NET_Q);
- MLX5_SET(rqtc, rqtc, rqt_max_size, rqt_table_size);
- list = MLX5_ADDR_OF(rqtc, rqtc, rq_num[0]);
- for (i = 0, j = 0; i < act_sz; i++, j += 2)
- list[i] = cpu_to_be32(ndev->vqs[j % ndev->cur_num_vqs].virtq_id);
- MLX5_SET(rqtc, rqtc, rqt_actual_size, act_sz);
- err = mlx5_vdpa_create_rqt(&ndev->mvdev, in, inlen, &ndev->res.rqtn);
- kfree(in);
- if (err)
- return err;
- return 0;
- }
- #define MLX5_MODIFY_RQT_NUM_RQS ((u64)1)
- static int modify_rqt(struct mlx5_vdpa_net *ndev, int num)
- {
- int act_sz = roundup_pow_of_two(num / 2);
- __be32 *list;
- void *rqtc;
- int inlen;
- void *in;
- int i, j;
- int err;
- inlen = MLX5_ST_SZ_BYTES(modify_rqt_in) + act_sz * MLX5_ST_SZ_BYTES(rq_num);
- in = kzalloc(inlen, GFP_KERNEL);
- if (!in)
- return -ENOMEM;
- MLX5_SET(modify_rqt_in, in, uid, ndev->mvdev.res.uid);
- MLX5_SET64(modify_rqt_in, in, bitmask, MLX5_MODIFY_RQT_NUM_RQS);
- rqtc = MLX5_ADDR_OF(modify_rqt_in, in, ctx);
- MLX5_SET(rqtc, rqtc, list_q_type, MLX5_RQTC_LIST_Q_TYPE_VIRTIO_NET_Q);
- list = MLX5_ADDR_OF(rqtc, rqtc, rq_num[0]);
- for (i = 0, j = 0; i < act_sz; i++, j = j + 2)
- list[i] = cpu_to_be32(ndev->vqs[j % num].virtq_id);
- MLX5_SET(rqtc, rqtc, rqt_actual_size, act_sz);
- err = mlx5_vdpa_modify_rqt(&ndev->mvdev, in, inlen, ndev->res.rqtn);
- kfree(in);
- if (err)
- return err;
- return 0;
- }
- static void destroy_rqt(struct mlx5_vdpa_net *ndev)
- {
- mlx5_vdpa_destroy_rqt(&ndev->mvdev, ndev->res.rqtn);
- }
- static int create_tir(struct mlx5_vdpa_net *ndev)
- {
- #define HASH_IP_L4PORTS \
- (MLX5_HASH_FIELD_SEL_SRC_IP | MLX5_HASH_FIELD_SEL_DST_IP | MLX5_HASH_FIELD_SEL_L4_SPORT | \
- MLX5_HASH_FIELD_SEL_L4_DPORT)
- static const u8 rx_hash_toeplitz_key[] = { 0x2c, 0xc6, 0x81, 0xd1, 0x5b, 0xdb, 0xf4, 0xf7,
- 0xfc, 0xa2, 0x83, 0x19, 0xdb, 0x1a, 0x3e, 0x94,
- 0x6b, 0x9e, 0x38, 0xd9, 0x2c, 0x9c, 0x03, 0xd1,
- 0xad, 0x99, 0x44, 0xa7, 0xd9, 0x56, 0x3d, 0x59,
- 0x06, 0x3c, 0x25, 0xf3, 0xfc, 0x1f, 0xdc, 0x2a };
- void *rss_key;
- void *outer;
- void *tirc;
- void *in;
- int err;
- in = kzalloc(MLX5_ST_SZ_BYTES(create_tir_in), GFP_KERNEL);
- if (!in)
- return -ENOMEM;
- MLX5_SET(create_tir_in, in, uid, ndev->mvdev.res.uid);
- tirc = MLX5_ADDR_OF(create_tir_in, in, ctx);
- MLX5_SET(tirc, tirc, disp_type, MLX5_TIRC_DISP_TYPE_INDIRECT);
- MLX5_SET(tirc, tirc, rx_hash_symmetric, 1);
- MLX5_SET(tirc, tirc, rx_hash_fn, MLX5_RX_HASH_FN_TOEPLITZ);
- rss_key = MLX5_ADDR_OF(tirc, tirc, rx_hash_toeplitz_key);
- memcpy(rss_key, rx_hash_toeplitz_key, sizeof(rx_hash_toeplitz_key));
- outer = MLX5_ADDR_OF(tirc, tirc, rx_hash_field_selector_outer);
- MLX5_SET(rx_hash_field_select, outer, l3_prot_type, MLX5_L3_PROT_TYPE_IPV4);
- MLX5_SET(rx_hash_field_select, outer, l4_prot_type, MLX5_L4_PROT_TYPE_TCP);
- MLX5_SET(rx_hash_field_select, outer, selected_fields, HASH_IP_L4PORTS);
- MLX5_SET(tirc, tirc, indirect_table, ndev->res.rqtn);
- MLX5_SET(tirc, tirc, transport_domain, ndev->res.tdn);
- err = mlx5_vdpa_create_tir(&ndev->mvdev, in, &ndev->res.tirn);
- kfree(in);
- return err;
- }
- static void destroy_tir(struct mlx5_vdpa_net *ndev)
- {
- mlx5_vdpa_destroy_tir(&ndev->mvdev, ndev->res.tirn);
- }
- #define MAX_STEERING_ENT 0x8000
- #define MAX_STEERING_GROUPS 2
- static int mlx5_vdpa_add_mac_vlan_rules(struct mlx5_vdpa_net *ndev, u8 *mac,
- u16 vid, bool tagged,
- struct mlx5_flow_handle **ucast,
- struct mlx5_flow_handle **mcast)
- {
- struct mlx5_flow_destination dest = {};
- struct mlx5_flow_act flow_act = {};
- struct mlx5_flow_handle *rule;
- struct mlx5_flow_spec *spec;
- void *headers_c;
- void *headers_v;
- u8 *dmac_c;
- u8 *dmac_v;
- int err;
- spec = kvzalloc(sizeof(*spec), GFP_KERNEL);
- if (!spec)
- return -ENOMEM;
- spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- headers_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, outer_headers);
- headers_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, outer_headers);
- dmac_c = MLX5_ADDR_OF(fte_match_param, headers_c, outer_headers.dmac_47_16);
- dmac_v = MLX5_ADDR_OF(fte_match_param, headers_v, outer_headers.dmac_47_16);
- eth_broadcast_addr(dmac_c);
- ether_addr_copy(dmac_v, mac);
- if (ndev->mvdev.actual_features & BIT_ULL(VIRTIO_NET_F_CTRL_VLAN)) {
- MLX5_SET(fte_match_set_lyr_2_4, headers_c, cvlan_tag, 1);
- MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, first_vid);
- }
- if (tagged) {
- MLX5_SET(fte_match_set_lyr_2_4, headers_v, cvlan_tag, 1);
- MLX5_SET(fte_match_set_lyr_2_4, headers_v, first_vid, vid);
- }
- flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
- dest.type = MLX5_FLOW_DESTINATION_TYPE_TIR;
- dest.tir_num = ndev->res.tirn;
- rule = mlx5_add_flow_rules(ndev->rxft, spec, &flow_act, &dest, 1);
- if (IS_ERR(rule))
- return PTR_ERR(rule);
- *ucast = rule;
- memset(dmac_c, 0, ETH_ALEN);
- memset(dmac_v, 0, ETH_ALEN);
- dmac_c[0] = 1;
- dmac_v[0] = 1;
- rule = mlx5_add_flow_rules(ndev->rxft, spec, &flow_act, &dest, 1);
- kvfree(spec);
- if (IS_ERR(rule)) {
- err = PTR_ERR(rule);
- goto err_mcast;
- }
- *mcast = rule;
- return 0;
- err_mcast:
- mlx5_del_flow_rules(*ucast);
- return err;
- }
- static void mlx5_vdpa_del_mac_vlan_rules(struct mlx5_vdpa_net *ndev,
- struct mlx5_flow_handle *ucast,
- struct mlx5_flow_handle *mcast)
- {
- mlx5_del_flow_rules(ucast);
- mlx5_del_flow_rules(mcast);
- }
- static u64 search_val(u8 *mac, u16 vlan, bool tagged)
- {
- u64 val;
- if (!tagged)
- vlan = MLX5V_UNTAGGED;
- val = (u64)vlan << 48 |
- (u64)mac[0] << 40 |
- (u64)mac[1] << 32 |
- (u64)mac[2] << 24 |
- (u64)mac[3] << 16 |
- (u64)mac[4] << 8 |
- (u64)mac[5];
- return val;
- }
- static struct macvlan_node *mac_vlan_lookup(struct mlx5_vdpa_net *ndev, u64 value)
- {
- struct macvlan_node *pos;
- u32 idx;
- idx = hash_64(value, 8); // tbd 8
- hlist_for_each_entry(pos, &ndev->macvlan_hash[idx], hlist) {
- if (pos->macvlan == value)
- return pos;
- }
- return NULL;
- }
- static int mac_vlan_add(struct mlx5_vdpa_net *ndev, u8 *mac, u16 vlan, bool tagged) // vlan -> vid
- {
- struct macvlan_node *ptr;
- u64 val;
- u32 idx;
- int err;
- val = search_val(mac, vlan, tagged);
- if (mac_vlan_lookup(ndev, val))
- return -EEXIST;
- ptr = kzalloc(sizeof(*ptr), GFP_KERNEL);
- if (!ptr)
- return -ENOMEM;
- err = mlx5_vdpa_add_mac_vlan_rules(ndev, ndev->config.mac, vlan, tagged,
- &ptr->ucast_rule, &ptr->mcast_rule);
- if (err)
- goto err_add;
- ptr->macvlan = val;
- idx = hash_64(val, 8);
- hlist_add_head(&ptr->hlist, &ndev->macvlan_hash[idx]);
- return 0;
- err_add:
- kfree(ptr);
- return err;
- }
- static void mac_vlan_del(struct mlx5_vdpa_net *ndev, u8 *mac, u16 vlan, bool tagged)
- {
- struct macvlan_node *ptr;
- ptr = mac_vlan_lookup(ndev, search_val(mac, vlan, tagged));
- if (!ptr)
- return;
- hlist_del(&ptr->hlist);
- mlx5_vdpa_del_mac_vlan_rules(ndev, ptr->ucast_rule, ptr->mcast_rule);
- kfree(ptr);
- }
- static void clear_mac_vlan_table(struct mlx5_vdpa_net *ndev)
- {
- struct macvlan_node *pos;
- struct hlist_node *n;
- int i;
- for (i = 0; i < MLX5V_MACVLAN_SIZE; i++) {
- hlist_for_each_entry_safe(pos, n, &ndev->macvlan_hash[i], hlist) {
- hlist_del(&pos->hlist);
- mlx5_vdpa_del_mac_vlan_rules(ndev, pos->ucast_rule, pos->mcast_rule);
- kfree(pos);
- }
- }
- }
- static int setup_steering(struct mlx5_vdpa_net *ndev)
- {
- struct mlx5_flow_table_attr ft_attr = {};
- struct mlx5_flow_namespace *ns;
- int err;
- ft_attr.max_fte = MAX_STEERING_ENT;
- ft_attr.autogroup.max_num_groups = MAX_STEERING_GROUPS;
- ns = mlx5_get_flow_namespace(ndev->mvdev.mdev, MLX5_FLOW_NAMESPACE_BYPASS);
- if (!ns) {
- mlx5_vdpa_warn(&ndev->mvdev, "failed to get flow namespace\n");
- return -EOPNOTSUPP;
- }
- ndev->rxft = mlx5_create_auto_grouped_flow_table(ns, &ft_attr);
- if (IS_ERR(ndev->rxft)) {
- mlx5_vdpa_warn(&ndev->mvdev, "failed to create flow table\n");
- return PTR_ERR(ndev->rxft);
- }
- err = mac_vlan_add(ndev, ndev->config.mac, 0, false);
- if (err)
- goto err_add;
- return 0;
- err_add:
- mlx5_destroy_flow_table(ndev->rxft);
- return err;
- }
- static void teardown_steering(struct mlx5_vdpa_net *ndev)
- {
- clear_mac_vlan_table(ndev);
- mlx5_destroy_flow_table(ndev->rxft);
- }
- static virtio_net_ctrl_ack handle_ctrl_mac(struct mlx5_vdpa_dev *mvdev, u8 cmd)
- {
- struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
- struct mlx5_control_vq *cvq = &mvdev->cvq;
- virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
- struct mlx5_core_dev *pfmdev;
- size_t read;
- u8 mac[ETH_ALEN], mac_back[ETH_ALEN];
- pfmdev = pci_get_drvdata(pci_physfn(mvdev->mdev->pdev));
- switch (cmd) {
- case VIRTIO_NET_CTRL_MAC_ADDR_SET:
- read = vringh_iov_pull_iotlb(&cvq->vring, &cvq->riov, (void *)mac, ETH_ALEN);
- if (read != ETH_ALEN)
- break;
- if (!memcmp(ndev->config.mac, mac, 6)) {
- status = VIRTIO_NET_OK;
- break;
- }
- if (is_zero_ether_addr(mac))
- break;
- if (!is_zero_ether_addr(ndev->config.mac)) {
- if (mlx5_mpfs_del_mac(pfmdev, ndev->config.mac)) {
- mlx5_vdpa_warn(mvdev, "failed to delete old MAC %pM from MPFS table\n",
- ndev->config.mac);
- break;
- }
- }
- if (mlx5_mpfs_add_mac(pfmdev, mac)) {
- mlx5_vdpa_warn(mvdev, "failed to insert new MAC %pM into MPFS table\n",
- mac);
- break;
- }
- /* backup the original mac address so that if failed to add the forward rules
- * we could restore it
- */
- memcpy(mac_back, ndev->config.mac, ETH_ALEN);
- memcpy(ndev->config.mac, mac, ETH_ALEN);
- /* Need recreate the flow table entry, so that the packet could forward back
- */
- mac_vlan_del(ndev, mac_back, 0, false);
- if (mac_vlan_add(ndev, ndev->config.mac, 0, false)) {
- mlx5_vdpa_warn(mvdev, "failed to insert forward rules, try to restore\n");
- /* Although it hardly run here, we still need double check */
- if (is_zero_ether_addr(mac_back)) {
- mlx5_vdpa_warn(mvdev, "restore mac failed: Original MAC is zero\n");
- break;
- }
- /* Try to restore original mac address to MFPS table, and try to restore
- * the forward rule entry.
- */
- if (mlx5_mpfs_del_mac(pfmdev, ndev->config.mac)) {
- mlx5_vdpa_warn(mvdev, "restore mac failed: delete MAC %pM from MPFS table failed\n",
- ndev->config.mac);
- }
- if (mlx5_mpfs_add_mac(pfmdev, mac_back)) {
- mlx5_vdpa_warn(mvdev, "restore mac failed: insert old MAC %pM into MPFS table failed\n",
- mac_back);
- }
- memcpy(ndev->config.mac, mac_back, ETH_ALEN);
- if (mac_vlan_add(ndev, ndev->config.mac, 0, false))
- mlx5_vdpa_warn(mvdev, "restore forward rules failed: insert forward rules failed\n");
- break;
- }
- status = VIRTIO_NET_OK;
- break;
- default:
- break;
- }
- return status;
- }
- static int change_num_qps(struct mlx5_vdpa_dev *mvdev, int newqps)
- {
- struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
- int cur_qps = ndev->cur_num_vqs / 2;
- int err;
- int i;
- if (cur_qps > newqps) {
- err = modify_rqt(ndev, 2 * newqps);
- if (err)
- return err;
- for (i = ndev->cur_num_vqs - 1; i >= 2 * newqps; i--)
- teardown_vq(ndev, &ndev->vqs[i]);
- ndev->cur_num_vqs = 2 * newqps;
- } else {
- ndev->cur_num_vqs = 2 * newqps;
- for (i = cur_qps * 2; i < 2 * newqps; i++) {
- err = setup_vq(ndev, &ndev->vqs[i]);
- if (err)
- goto clean_added;
- }
- err = modify_rqt(ndev, 2 * newqps);
- if (err)
- goto clean_added;
- }
- return 0;
- clean_added:
- for (--i; i >= 2 * cur_qps; --i)
- teardown_vq(ndev, &ndev->vqs[i]);
- ndev->cur_num_vqs = 2 * cur_qps;
- return err;
- }
- static virtio_net_ctrl_ack handle_ctrl_mq(struct mlx5_vdpa_dev *mvdev, u8 cmd)
- {
- struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
- virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
- struct mlx5_control_vq *cvq = &mvdev->cvq;
- struct virtio_net_ctrl_mq mq;
- size_t read;
- u16 newqps;
- switch (cmd) {
- case VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET:
- /* This mq feature check aligns with pre-existing userspace
- * implementation.
- *
- * Without it, an untrusted driver could fake a multiqueue config
- * request down to a non-mq device that may cause kernel to
- * panic due to uninitialized resources for extra vqs. Even with
- * a well behaving guest driver, it is not expected to allow
- * changing the number of vqs on a non-mq device.
- */
- if (!MLX5_FEATURE(mvdev, VIRTIO_NET_F_MQ))
- break;
- read = vringh_iov_pull_iotlb(&cvq->vring, &cvq->riov, (void *)&mq, sizeof(mq));
- if (read != sizeof(mq))
- break;
- newqps = mlx5vdpa16_to_cpu(mvdev, mq.virtqueue_pairs);
- if (newqps < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN ||
- newqps > ndev->rqt_size)
- break;
- if (ndev->cur_num_vqs == 2 * newqps) {
- status = VIRTIO_NET_OK;
- break;
- }
- if (!change_num_qps(mvdev, newqps))
- status = VIRTIO_NET_OK;
- break;
- default:
- break;
- }
- return status;
- }
- static virtio_net_ctrl_ack handle_ctrl_vlan(struct mlx5_vdpa_dev *mvdev, u8 cmd)
- {
- struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
- virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
- struct mlx5_control_vq *cvq = &mvdev->cvq;
- __virtio16 vlan;
- size_t read;
- u16 id;
- if (!(ndev->mvdev.actual_features & BIT_ULL(VIRTIO_NET_F_CTRL_VLAN)))
- return status;
- switch (cmd) {
- case VIRTIO_NET_CTRL_VLAN_ADD:
- read = vringh_iov_pull_iotlb(&cvq->vring, &cvq->riov, &vlan, sizeof(vlan));
- if (read != sizeof(vlan))
- break;
- id = mlx5vdpa16_to_cpu(mvdev, vlan);
- if (mac_vlan_add(ndev, ndev->config.mac, id, true))
- break;
- status = VIRTIO_NET_OK;
- break;
- case VIRTIO_NET_CTRL_VLAN_DEL:
- read = vringh_iov_pull_iotlb(&cvq->vring, &cvq->riov, &vlan, sizeof(vlan));
- if (read != sizeof(vlan))
- break;
- id = mlx5vdpa16_to_cpu(mvdev, vlan);
- mac_vlan_del(ndev, ndev->config.mac, id, true);
- status = VIRTIO_NET_OK;
- break;
- default:
- break;
- }
- return status;
- }
- static void mlx5_cvq_kick_handler(struct work_struct *work)
- {
- virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
- struct virtio_net_ctrl_hdr ctrl;
- struct mlx5_vdpa_wq_ent *wqent;
- struct mlx5_vdpa_dev *mvdev;
- struct mlx5_control_vq *cvq;
- struct mlx5_vdpa_net *ndev;
- size_t read, write;
- int err;
- wqent = container_of(work, struct mlx5_vdpa_wq_ent, work);
- mvdev = wqent->mvdev;
- ndev = to_mlx5_vdpa_ndev(mvdev);
- cvq = &mvdev->cvq;
- down_write(&ndev->reslock);
- if (!(mvdev->status & VIRTIO_CONFIG_S_DRIVER_OK))
- goto out;
- if (!(ndev->mvdev.actual_features & BIT_ULL(VIRTIO_NET_F_CTRL_VQ)))
- goto out;
- if (!cvq->ready)
- goto out;
- while (true) {
- err = vringh_getdesc_iotlb(&cvq->vring, &cvq->riov, &cvq->wiov, &cvq->head,
- GFP_ATOMIC);
- if (err <= 0)
- break;
- read = vringh_iov_pull_iotlb(&cvq->vring, &cvq->riov, &ctrl, sizeof(ctrl));
- if (read != sizeof(ctrl))
- break;
- cvq->received_desc++;
- switch (ctrl.class) {
- case VIRTIO_NET_CTRL_MAC:
- status = handle_ctrl_mac(mvdev, ctrl.cmd);
- break;
- case VIRTIO_NET_CTRL_MQ:
- status = handle_ctrl_mq(mvdev, ctrl.cmd);
- break;
- case VIRTIO_NET_CTRL_VLAN:
- status = handle_ctrl_vlan(mvdev, ctrl.cmd);
- break;
- default:
- break;
- }
- /* Make sure data is written before advancing index */
- smp_wmb();
- write = vringh_iov_push_iotlb(&cvq->vring, &cvq->wiov, &status, sizeof(status));
- vringh_complete_iotlb(&cvq->vring, cvq->head, write);
- vringh_kiov_cleanup(&cvq->riov);
- vringh_kiov_cleanup(&cvq->wiov);
- if (vringh_need_notify_iotlb(&cvq->vring))
- vringh_notify(&cvq->vring);
- cvq->completed_desc++;
- queue_work(mvdev->wq, &wqent->work);
- break;
- }
- out:
- up_write(&ndev->reslock);
- }
- static void mlx5_vdpa_kick_vq(struct vdpa_device *vdev, u16 idx)
- {
- struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
- struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
- struct mlx5_vdpa_virtqueue *mvq;
- if (!is_index_valid(mvdev, idx))
- return;
- if (unlikely(is_ctrl_vq_idx(mvdev, idx))) {
- if (!mvdev->wq || !mvdev->cvq.ready)
- return;
- queue_work(mvdev->wq, &ndev->cvq_ent.work);
- return;
- }
- mvq = &ndev->vqs[idx];
- if (unlikely(!mvq->ready))
- return;
- iowrite16(idx, ndev->mvdev.res.kick_addr);
- }
- static int mlx5_vdpa_set_vq_address(struct vdpa_device *vdev, u16 idx, u64 desc_area,
- u64 driver_area, u64 device_area)
- {
- struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
- struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
- struct mlx5_vdpa_virtqueue *mvq;
- if (!is_index_valid(mvdev, idx))
- return -EINVAL;
- if (is_ctrl_vq_idx(mvdev, idx)) {
- mvdev->cvq.desc_addr = desc_area;
- mvdev->cvq.device_addr = device_area;
- mvdev->cvq.driver_addr = driver_area;
- return 0;
- }
- mvq = &ndev->vqs[idx];
- mvq->desc_addr = desc_area;
- mvq->device_addr = device_area;
- mvq->driver_addr = driver_area;
- return 0;
- }
- static void mlx5_vdpa_set_vq_num(struct vdpa_device *vdev, u16 idx, u32 num)
- {
- struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
- struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
- struct mlx5_vdpa_virtqueue *mvq;
- if (!is_index_valid(mvdev, idx) || is_ctrl_vq_idx(mvdev, idx))
- return;
- mvq = &ndev->vqs[idx];
- mvq->num_ent = num;
- }
- static void mlx5_vdpa_set_vq_cb(struct vdpa_device *vdev, u16 idx, struct vdpa_callback *cb)
- {
- struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
- struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
- ndev->event_cbs[idx] = *cb;
- if (is_ctrl_vq_idx(mvdev, idx))
- mvdev->cvq.event_cb = *cb;
- }
- static void mlx5_cvq_notify(struct vringh *vring)
- {
- struct mlx5_control_vq *cvq = container_of(vring, struct mlx5_control_vq, vring);
- if (!cvq->event_cb.callback)
- return;
- cvq->event_cb.callback(cvq->event_cb.private);
- }
- static void set_cvq_ready(struct mlx5_vdpa_dev *mvdev, bool ready)
- {
- struct mlx5_control_vq *cvq = &mvdev->cvq;
- cvq->ready = ready;
- if (!ready)
- return;
- cvq->vring.notify = mlx5_cvq_notify;
- }
- static void mlx5_vdpa_set_vq_ready(struct vdpa_device *vdev, u16 idx, bool ready)
- {
- struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
- struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
- struct mlx5_vdpa_virtqueue *mvq;
- int err;
- if (!mvdev->actual_features)
- return;
- if (!is_index_valid(mvdev, idx))
- return;
- if (is_ctrl_vq_idx(mvdev, idx)) {
- set_cvq_ready(mvdev, ready);
- return;
- }
- mvq = &ndev->vqs[idx];
- if (!ready) {
- suspend_vq(ndev, mvq);
- } else {
- err = modify_virtqueue(ndev, mvq, MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY);
- if (err) {
- mlx5_vdpa_warn(mvdev, "modify VQ %d to ready failed (%d)\n", idx, err);
- ready = false;
- }
- }
- mvq->ready = ready;
- }
- static bool mlx5_vdpa_get_vq_ready(struct vdpa_device *vdev, u16 idx)
- {
- struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
- struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
- if (!is_index_valid(mvdev, idx))
- return false;
- if (is_ctrl_vq_idx(mvdev, idx))
- return mvdev->cvq.ready;
- return ndev->vqs[idx].ready;
- }
- static int mlx5_vdpa_set_vq_state(struct vdpa_device *vdev, u16 idx,
- const struct vdpa_vq_state *state)
- {
- struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
- struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
- struct mlx5_vdpa_virtqueue *mvq;
- if (!is_index_valid(mvdev, idx))
- return -EINVAL;
- if (is_ctrl_vq_idx(mvdev, idx)) {
- mvdev->cvq.vring.last_avail_idx = state->split.avail_index;
- return 0;
- }
- mvq = &ndev->vqs[idx];
- if (mvq->fw_state == MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY) {
- mlx5_vdpa_warn(mvdev, "can't modify available index\n");
- return -EINVAL;
- }
- mvq->used_idx = state->split.avail_index;
- mvq->avail_idx = state->split.avail_index;
- return 0;
- }
- static int mlx5_vdpa_get_vq_state(struct vdpa_device *vdev, u16 idx, struct vdpa_vq_state *state)
- {
- struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
- struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
- struct mlx5_vdpa_virtqueue *mvq;
- struct mlx5_virtq_attr attr;
- int err;
- if (!is_index_valid(mvdev, idx))
- return -EINVAL;
- if (is_ctrl_vq_idx(mvdev, idx)) {
- state->split.avail_index = mvdev->cvq.vring.last_avail_idx;
- return 0;
- }
- mvq = &ndev->vqs[idx];
- /* If the virtq object was destroyed, use the value saved at
- * the last minute of suspend_vq. This caters for userspace
- * that cares about emulating the index after vq is stopped.
- */
- if (!mvq->initialized) {
- /* Firmware returns a wrong value for the available index.
- * Since both values should be identical, we take the value of
- * used_idx which is reported correctly.
- */
- state->split.avail_index = mvq->used_idx;
- return 0;
- }
- err = query_virtqueue(ndev, mvq, &attr);
- if (err) {
- mlx5_vdpa_warn(mvdev, "failed to query virtqueue\n");
- return err;
- }
- state->split.avail_index = attr.used_index;
- return 0;
- }
- static u32 mlx5_vdpa_get_vq_align(struct vdpa_device *vdev)
- {
- return PAGE_SIZE;
- }
- static u32 mlx5_vdpa_get_vq_group(struct vdpa_device *vdev, u16 idx)
- {
- struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
- if (is_ctrl_vq_idx(mvdev, idx))
- return MLX5_VDPA_CVQ_GROUP;
- return MLX5_VDPA_DATAVQ_GROUP;
- }
- enum { MLX5_VIRTIO_NET_F_GUEST_CSUM = 1 << 9,
- MLX5_VIRTIO_NET_F_CSUM = 1 << 10,
- MLX5_VIRTIO_NET_F_HOST_TSO6 = 1 << 11,
- MLX5_VIRTIO_NET_F_HOST_TSO4 = 1 << 12,
- };
- static u64 mlx_to_vritio_features(u16 dev_features)
- {
- u64 result = 0;
- if (dev_features & MLX5_VIRTIO_NET_F_GUEST_CSUM)
- result |= BIT_ULL(VIRTIO_NET_F_GUEST_CSUM);
- if (dev_features & MLX5_VIRTIO_NET_F_CSUM)
- result |= BIT_ULL(VIRTIO_NET_F_CSUM);
- if (dev_features & MLX5_VIRTIO_NET_F_HOST_TSO6)
- result |= BIT_ULL(VIRTIO_NET_F_HOST_TSO6);
- if (dev_features & MLX5_VIRTIO_NET_F_HOST_TSO4)
- result |= BIT_ULL(VIRTIO_NET_F_HOST_TSO4);
- return result;
- }
- static u64 get_supported_features(struct mlx5_core_dev *mdev)
- {
- u64 mlx_vdpa_features = 0;
- u16 dev_features;
- dev_features = MLX5_CAP_DEV_VDPA_EMULATION(mdev, device_features_bits_mask);
- mlx_vdpa_features |= mlx_to_vritio_features(dev_features);
- if (MLX5_CAP_DEV_VDPA_EMULATION(mdev, virtio_version_1_0))
- mlx_vdpa_features |= BIT_ULL(VIRTIO_F_VERSION_1);
- mlx_vdpa_features |= BIT_ULL(VIRTIO_F_ACCESS_PLATFORM);
- mlx_vdpa_features |= BIT_ULL(VIRTIO_NET_F_CTRL_VQ);
- mlx_vdpa_features |= BIT_ULL(VIRTIO_NET_F_CTRL_MAC_ADDR);
- mlx_vdpa_features |= BIT_ULL(VIRTIO_NET_F_MQ);
- mlx_vdpa_features |= BIT_ULL(VIRTIO_NET_F_STATUS);
- mlx_vdpa_features |= BIT_ULL(VIRTIO_NET_F_MTU);
- mlx_vdpa_features |= BIT_ULL(VIRTIO_NET_F_CTRL_VLAN);
- return mlx_vdpa_features;
- }
- static u64 mlx5_vdpa_get_device_features(struct vdpa_device *vdev)
- {
- struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
- struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
- print_features(mvdev, ndev->mvdev.mlx_features, false);
- return ndev->mvdev.mlx_features;
- }
- static int verify_driver_features(struct mlx5_vdpa_dev *mvdev, u64 features)
- {
- /* Minimum features to expect */
- if (!(features & BIT_ULL(VIRTIO_F_ACCESS_PLATFORM)))
- return -EOPNOTSUPP;
- /* Double check features combination sent down by the driver.
- * Fail invalid features due to absence of the depended feature.
- *
- * Per VIRTIO v1.1 specification, section 5.1.3.1 Feature bit
- * requirements: "VIRTIO_NET_F_MQ Requires VIRTIO_NET_F_CTRL_VQ".
- * By failing the invalid features sent down by untrusted drivers,
- * we're assured the assumption made upon is_index_valid() and
- * is_ctrl_vq_idx() will not be compromised.
- */
- if ((features & (BIT_ULL(VIRTIO_NET_F_MQ) | BIT_ULL(VIRTIO_NET_F_CTRL_VQ))) ==
- BIT_ULL(VIRTIO_NET_F_MQ))
- return -EINVAL;
- return 0;
- }
- static int setup_virtqueues(struct mlx5_vdpa_dev *mvdev)
- {
- struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
- int err;
- int i;
- for (i = 0; i < mvdev->max_vqs; i++) {
- err = setup_vq(ndev, &ndev->vqs[i]);
- if (err)
- goto err_vq;
- }
- return 0;
- err_vq:
- for (--i; i >= 0; i--)
- teardown_vq(ndev, &ndev->vqs[i]);
- return err;
- }
- static void teardown_virtqueues(struct mlx5_vdpa_net *ndev)
- {
- struct mlx5_vdpa_virtqueue *mvq;
- int i;
- for (i = ndev->mvdev.max_vqs - 1; i >= 0; i--) {
- mvq = &ndev->vqs[i];
- if (!mvq->initialized)
- continue;
- teardown_vq(ndev, mvq);
- }
- }
- static void update_cvq_info(struct mlx5_vdpa_dev *mvdev)
- {
- if (MLX5_FEATURE(mvdev, VIRTIO_NET_F_CTRL_VQ)) {
- if (MLX5_FEATURE(mvdev, VIRTIO_NET_F_MQ)) {
- /* MQ supported. CVQ index is right above the last data virtqueue's */
- mvdev->max_idx = mvdev->max_vqs;
- } else {
- /* Only CVQ supportted. data virtqueues occupy indices 0 and 1.
- * CVQ gets index 2
- */
- mvdev->max_idx = 2;
- }
- } else {
- /* Two data virtqueues only: one for rx and one for tx */
- mvdev->max_idx = 1;
- }
- }
- static int mlx5_vdpa_set_driver_features(struct vdpa_device *vdev, u64 features)
- {
- struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
- struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
- int err;
- print_features(mvdev, features, true);
- err = verify_driver_features(mvdev, features);
- if (err)
- return err;
- ndev->mvdev.actual_features = features & ndev->mvdev.mlx_features;
- if (ndev->mvdev.actual_features & BIT_ULL(VIRTIO_NET_F_MQ))
- ndev->rqt_size = mlx5vdpa16_to_cpu(mvdev, ndev->config.max_virtqueue_pairs);
- else
- ndev->rqt_size = 1;
- ndev->cur_num_vqs = 2 * ndev->rqt_size;
- update_cvq_info(mvdev);
- return err;
- }
- static void mlx5_vdpa_set_config_cb(struct vdpa_device *vdev, struct vdpa_callback *cb)
- {
- struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
- struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
- ndev->config_cb = *cb;
- }
- #define MLX5_VDPA_MAX_VQ_ENTRIES 256
- static u16 mlx5_vdpa_get_vq_num_max(struct vdpa_device *vdev)
- {
- return MLX5_VDPA_MAX_VQ_ENTRIES;
- }
- static u32 mlx5_vdpa_get_device_id(struct vdpa_device *vdev)
- {
- return VIRTIO_ID_NET;
- }
- static u32 mlx5_vdpa_get_vendor_id(struct vdpa_device *vdev)
- {
- return PCI_VENDOR_ID_MELLANOX;
- }
- static u8 mlx5_vdpa_get_status(struct vdpa_device *vdev)
- {
- struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
- struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
- print_status(mvdev, ndev->mvdev.status, false);
- return ndev->mvdev.status;
- }
- static int save_channel_info(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq)
- {
- struct mlx5_vq_restore_info *ri = &mvq->ri;
- struct mlx5_virtq_attr attr = {};
- int err;
- if (mvq->initialized) {
- err = query_virtqueue(ndev, mvq, &attr);
- if (err)
- return err;
- }
- ri->avail_index = attr.available_index;
- ri->used_index = attr.used_index;
- ri->ready = mvq->ready;
- ri->num_ent = mvq->num_ent;
- ri->desc_addr = mvq->desc_addr;
- ri->device_addr = mvq->device_addr;
- ri->driver_addr = mvq->driver_addr;
- ri->restore = true;
- return 0;
- }
- static int save_channels_info(struct mlx5_vdpa_net *ndev)
- {
- int i;
- for (i = 0; i < ndev->mvdev.max_vqs; i++) {
- memset(&ndev->vqs[i].ri, 0, sizeof(ndev->vqs[i].ri));
- save_channel_info(ndev, &ndev->vqs[i]);
- }
- return 0;
- }
- static void mlx5_clear_vqs(struct mlx5_vdpa_net *ndev)
- {
- int i;
- for (i = 0; i < ndev->mvdev.max_vqs; i++)
- memset(&ndev->vqs[i], 0, offsetof(struct mlx5_vdpa_virtqueue, ri));
- }
- static void restore_channels_info(struct mlx5_vdpa_net *ndev)
- {
- struct mlx5_vdpa_virtqueue *mvq;
- struct mlx5_vq_restore_info *ri;
- int i;
- mlx5_clear_vqs(ndev);
- init_mvqs(ndev);
- for (i = 0; i < ndev->mvdev.max_vqs; i++) {
- mvq = &ndev->vqs[i];
- ri = &mvq->ri;
- if (!ri->restore)
- continue;
- mvq->avail_idx = ri->avail_index;
- mvq->used_idx = ri->used_index;
- mvq->ready = ri->ready;
- mvq->num_ent = ri->num_ent;
- mvq->desc_addr = ri->desc_addr;
- mvq->device_addr = ri->device_addr;
- mvq->driver_addr = ri->driver_addr;
- }
- }
- static int mlx5_vdpa_change_map(struct mlx5_vdpa_dev *mvdev,
- struct vhost_iotlb *iotlb, unsigned int asid)
- {
- struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
- int err;
- suspend_vqs(ndev);
- err = save_channels_info(ndev);
- if (err)
- goto err_mr;
- teardown_driver(ndev);
- mlx5_vdpa_destroy_mr_asid(mvdev, asid);
- err = mlx5_vdpa_create_mr(mvdev, iotlb, asid);
- if (err)
- goto err_mr;
- if (!(mvdev->status & VIRTIO_CONFIG_S_DRIVER_OK) || mvdev->suspended)
- goto err_mr;
- restore_channels_info(ndev);
- err = setup_driver(mvdev);
- if (err)
- goto err_setup;
- return 0;
- err_setup:
- mlx5_vdpa_destroy_mr_asid(mvdev, asid);
- err_mr:
- return err;
- }
- /* reslock must be held for this function */
- static int setup_driver(struct mlx5_vdpa_dev *mvdev)
- {
- struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
- int err;
- WARN_ON(!rwsem_is_locked(&ndev->reslock));
- if (ndev->setup) {
- mlx5_vdpa_warn(mvdev, "setup driver called for already setup driver\n");
- err = 0;
- goto out;
- }
- err = setup_virtqueues(mvdev);
- if (err) {
- mlx5_vdpa_warn(mvdev, "setup_virtqueues\n");
- goto out;
- }
- err = create_rqt(ndev);
- if (err) {
- mlx5_vdpa_warn(mvdev, "create_rqt\n");
- goto err_rqt;
- }
- err = create_tir(ndev);
- if (err) {
- mlx5_vdpa_warn(mvdev, "create_tir\n");
- goto err_tir;
- }
- err = setup_steering(ndev);
- if (err) {
- mlx5_vdpa_warn(mvdev, "setup_steering\n");
- goto err_fwd;
- }
- ndev->setup = true;
- return 0;
- err_fwd:
- destroy_tir(ndev);
- err_tir:
- destroy_rqt(ndev);
- err_rqt:
- teardown_virtqueues(ndev);
- out:
- return err;
- }
- /* reslock must be held for this function */
- static void teardown_driver(struct mlx5_vdpa_net *ndev)
- {
- WARN_ON(!rwsem_is_locked(&ndev->reslock));
- if (!ndev->setup)
- return;
- teardown_steering(ndev);
- destroy_tir(ndev);
- destroy_rqt(ndev);
- teardown_virtqueues(ndev);
- ndev->setup = false;
- }
- static void clear_vqs_ready(struct mlx5_vdpa_net *ndev)
- {
- int i;
- for (i = 0; i < ndev->mvdev.max_vqs; i++)
- ndev->vqs[i].ready = false;
- ndev->mvdev.cvq.ready = false;
- }
- static int setup_cvq_vring(struct mlx5_vdpa_dev *mvdev)
- {
- struct mlx5_control_vq *cvq = &mvdev->cvq;
- int err = 0;
- if (mvdev->actual_features & BIT_ULL(VIRTIO_NET_F_CTRL_VQ)) {
- u16 idx = cvq->vring.last_avail_idx;
- err = vringh_init_iotlb(&cvq->vring, mvdev->actual_features,
- MLX5_CVQ_MAX_ENT, false,
- (struct vring_desc *)(uintptr_t)cvq->desc_addr,
- (struct vring_avail *)(uintptr_t)cvq->driver_addr,
- (struct vring_used *)(uintptr_t)cvq->device_addr);
- if (!err)
- cvq->vring.last_avail_idx = cvq->vring.last_used_idx = idx;
- }
- return err;
- }
- static void mlx5_vdpa_set_status(struct vdpa_device *vdev, u8 status)
- {
- struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
- struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
- int err;
- print_status(mvdev, status, true);
- down_write(&ndev->reslock);
- if ((status ^ ndev->mvdev.status) & VIRTIO_CONFIG_S_DRIVER_OK) {
- if (status & VIRTIO_CONFIG_S_DRIVER_OK) {
- err = setup_cvq_vring(mvdev);
- if (err) {
- mlx5_vdpa_warn(mvdev, "failed to setup control VQ vring\n");
- goto err_setup;
- }
- err = setup_driver(mvdev);
- if (err) {
- mlx5_vdpa_warn(mvdev, "failed to setup driver\n");
- goto err_setup;
- }
- } else {
- mlx5_vdpa_warn(mvdev, "did not expect DRIVER_OK to be cleared\n");
- goto err_clear;
- }
- }
- ndev->mvdev.status = status;
- up_write(&ndev->reslock);
- return;
- err_setup:
- mlx5_vdpa_destroy_mr(&ndev->mvdev);
- ndev->mvdev.status |= VIRTIO_CONFIG_S_FAILED;
- err_clear:
- up_write(&ndev->reslock);
- }
- static void init_group_to_asid_map(struct mlx5_vdpa_dev *mvdev)
- {
- int i;
- /* default mapping all groups are mapped to asid 0 */
- for (i = 0; i < MLX5_VDPA_NUMVQ_GROUPS; i++)
- mvdev->group2asid[i] = 0;
- }
- static int mlx5_vdpa_reset(struct vdpa_device *vdev)
- {
- struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
- struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
- print_status(mvdev, 0, true);
- mlx5_vdpa_info(mvdev, "performing device reset\n");
- down_write(&ndev->reslock);
- teardown_driver(ndev);
- clear_vqs_ready(ndev);
- mlx5_vdpa_destroy_mr(&ndev->mvdev);
- ndev->mvdev.status = 0;
- ndev->mvdev.suspended = false;
- ndev->cur_num_vqs = 0;
- ndev->mvdev.cvq.received_desc = 0;
- ndev->mvdev.cvq.completed_desc = 0;
- memset(ndev->event_cbs, 0, sizeof(*ndev->event_cbs) * (mvdev->max_vqs + 1));
- ndev->mvdev.actual_features = 0;
- init_group_to_asid_map(mvdev);
- ++mvdev->generation;
- if (MLX5_CAP_GEN(mvdev->mdev, umem_uid_0)) {
- if (mlx5_vdpa_create_mr(mvdev, NULL, 0))
- mlx5_vdpa_warn(mvdev, "create MR failed\n");
- }
- up_write(&ndev->reslock);
- return 0;
- }
- static size_t mlx5_vdpa_get_config_size(struct vdpa_device *vdev)
- {
- return sizeof(struct virtio_net_config);
- }
- static void mlx5_vdpa_get_config(struct vdpa_device *vdev, unsigned int offset, void *buf,
- unsigned int len)
- {
- struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
- struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
- if (offset + len <= sizeof(struct virtio_net_config))
- memcpy(buf, (u8 *)&ndev->config + offset, len);
- }
- static void mlx5_vdpa_set_config(struct vdpa_device *vdev, unsigned int offset, const void *buf,
- unsigned int len)
- {
- /* not supported */
- }
- static u32 mlx5_vdpa_get_generation(struct vdpa_device *vdev)
- {
- struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
- return mvdev->generation;
- }
- static int set_map_data(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb *iotlb,
- unsigned int asid)
- {
- bool change_map;
- int err;
- err = mlx5_vdpa_handle_set_map(mvdev, iotlb, &change_map, asid);
- if (err) {
- mlx5_vdpa_warn(mvdev, "set map failed(%d)\n", err);
- return err;
- }
- if (change_map)
- err = mlx5_vdpa_change_map(mvdev, iotlb, asid);
- return err;
- }
- static int mlx5_vdpa_set_map(struct vdpa_device *vdev, unsigned int asid,
- struct vhost_iotlb *iotlb)
- {
- struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
- struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
- int err = -EINVAL;
- down_write(&ndev->reslock);
- err = set_map_data(mvdev, iotlb, asid);
- up_write(&ndev->reslock);
- return err;
- }
- static void mlx5_vdpa_free(struct vdpa_device *vdev)
- {
- struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
- struct mlx5_core_dev *pfmdev;
- struct mlx5_vdpa_net *ndev;
- ndev = to_mlx5_vdpa_ndev(mvdev);
- free_resources(ndev);
- mlx5_vdpa_destroy_mr(mvdev);
- if (!is_zero_ether_addr(ndev->config.mac)) {
- pfmdev = pci_get_drvdata(pci_physfn(mvdev->mdev->pdev));
- mlx5_mpfs_del_mac(pfmdev, ndev->config.mac);
- }
- mlx5_vdpa_free_resources(&ndev->mvdev);
- kfree(ndev->event_cbs);
- kfree(ndev->vqs);
- }
- static struct vdpa_notification_area mlx5_get_vq_notification(struct vdpa_device *vdev, u16 idx)
- {
- struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
- struct vdpa_notification_area ret = {};
- struct mlx5_vdpa_net *ndev;
- phys_addr_t addr;
- if (!is_index_valid(mvdev, idx) || is_ctrl_vq_idx(mvdev, idx))
- return ret;
- /* If SF BAR size is smaller than PAGE_SIZE, do not use direct
- * notification to avoid the risk of mapping pages that contain BAR of more
- * than one SF
- */
- if (MLX5_CAP_GEN(mvdev->mdev, log_min_sf_size) + 12 < PAGE_SHIFT)
- return ret;
- ndev = to_mlx5_vdpa_ndev(mvdev);
- addr = (phys_addr_t)ndev->mvdev.res.phys_kick_addr;
- ret.addr = addr;
- ret.size = PAGE_SIZE;
- return ret;
- }
- static int mlx5_get_vq_irq(struct vdpa_device *vdv, u16 idx)
- {
- return -EOPNOTSUPP;
- }
- static u64 mlx5_vdpa_get_driver_features(struct vdpa_device *vdev)
- {
- struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
- return mvdev->actual_features;
- }
- static int counter_set_query(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq,
- u64 *received_desc, u64 *completed_desc)
- {
- u32 in[MLX5_ST_SZ_DW(query_virtio_q_counters_in)] = {};
- u32 out[MLX5_ST_SZ_DW(query_virtio_q_counters_out)] = {};
- void *cmd_hdr;
- void *ctx;
- int err;
- if (!counters_supported(&ndev->mvdev))
- return -EOPNOTSUPP;
- if (mvq->fw_state != MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY)
- return -EAGAIN;
- cmd_hdr = MLX5_ADDR_OF(query_virtio_q_counters_in, in, hdr);
- MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, opcode, MLX5_CMD_OP_QUERY_GENERAL_OBJECT);
- MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, obj_type, MLX5_OBJ_TYPE_VIRTIO_Q_COUNTERS);
- MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, uid, ndev->mvdev.res.uid);
- MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, obj_id, mvq->counter_set_id);
- err = mlx5_cmd_exec(ndev->mvdev.mdev, in, sizeof(in), out, sizeof(out));
- if (err)
- return err;
- ctx = MLX5_ADDR_OF(query_virtio_q_counters_out, out, counters);
- *received_desc = MLX5_GET64(virtio_q_counters, ctx, received_desc);
- *completed_desc = MLX5_GET64(virtio_q_counters, ctx, completed_desc);
- return 0;
- }
- static int mlx5_vdpa_get_vendor_vq_stats(struct vdpa_device *vdev, u16 idx,
- struct sk_buff *msg,
- struct netlink_ext_ack *extack)
- {
- struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
- struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
- struct mlx5_vdpa_virtqueue *mvq;
- struct mlx5_control_vq *cvq;
- u64 received_desc;
- u64 completed_desc;
- int err = 0;
- down_read(&ndev->reslock);
- if (!is_index_valid(mvdev, idx)) {
- NL_SET_ERR_MSG_MOD(extack, "virtqueue index is not valid");
- err = -EINVAL;
- goto out_err;
- }
- if (idx == ctrl_vq_idx(mvdev)) {
- cvq = &mvdev->cvq;
- received_desc = cvq->received_desc;
- completed_desc = cvq->completed_desc;
- goto out;
- }
- mvq = &ndev->vqs[idx];
- err = counter_set_query(ndev, mvq, &received_desc, &completed_desc);
- if (err) {
- NL_SET_ERR_MSG_MOD(extack, "failed to query hardware");
- goto out_err;
- }
- out:
- err = -EMSGSIZE;
- if (nla_put_string(msg, VDPA_ATTR_DEV_VENDOR_ATTR_NAME, "received_desc"))
- goto out_err;
- if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_VENDOR_ATTR_VALUE, received_desc,
- VDPA_ATTR_PAD))
- goto out_err;
- if (nla_put_string(msg, VDPA_ATTR_DEV_VENDOR_ATTR_NAME, "completed_desc"))
- goto out_err;
- if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_VENDOR_ATTR_VALUE, completed_desc,
- VDPA_ATTR_PAD))
- goto out_err;
- err = 0;
- out_err:
- up_read(&ndev->reslock);
- return err;
- }
- static void mlx5_vdpa_cvq_suspend(struct mlx5_vdpa_dev *mvdev)
- {
- struct mlx5_control_vq *cvq;
- if (!(mvdev->actual_features & BIT_ULL(VIRTIO_NET_F_CTRL_VQ)))
- return;
- cvq = &mvdev->cvq;
- cvq->ready = false;
- }
- static int mlx5_vdpa_suspend(struct vdpa_device *vdev)
- {
- struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
- struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
- struct mlx5_vdpa_virtqueue *mvq;
- int i;
- mlx5_vdpa_info(mvdev, "suspending device\n");
- down_write(&ndev->reslock);
- ndev->nb_registered = false;
- mlx5_notifier_unregister(mvdev->mdev, &ndev->nb);
- flush_workqueue(ndev->mvdev.wq);
- for (i = 0; i < ndev->cur_num_vqs; i++) {
- mvq = &ndev->vqs[i];
- suspend_vq(ndev, mvq);
- }
- mlx5_vdpa_cvq_suspend(mvdev);
- mvdev->suspended = true;
- up_write(&ndev->reslock);
- return 0;
- }
- static int mlx5_set_group_asid(struct vdpa_device *vdev, u32 group,
- unsigned int asid)
- {
- struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
- if (group >= MLX5_VDPA_NUMVQ_GROUPS)
- return -EINVAL;
- mvdev->group2asid[group] = asid;
- return 0;
- }
- static const struct vdpa_config_ops mlx5_vdpa_ops = {
- .set_vq_address = mlx5_vdpa_set_vq_address,
- .set_vq_num = mlx5_vdpa_set_vq_num,
- .kick_vq = mlx5_vdpa_kick_vq,
- .set_vq_cb = mlx5_vdpa_set_vq_cb,
- .set_vq_ready = mlx5_vdpa_set_vq_ready,
- .get_vq_ready = mlx5_vdpa_get_vq_ready,
- .set_vq_state = mlx5_vdpa_set_vq_state,
- .get_vq_state = mlx5_vdpa_get_vq_state,
- .get_vendor_vq_stats = mlx5_vdpa_get_vendor_vq_stats,
- .get_vq_notification = mlx5_get_vq_notification,
- .get_vq_irq = mlx5_get_vq_irq,
- .get_vq_align = mlx5_vdpa_get_vq_align,
- .get_vq_group = mlx5_vdpa_get_vq_group,
- .get_device_features = mlx5_vdpa_get_device_features,
- .set_driver_features = mlx5_vdpa_set_driver_features,
- .get_driver_features = mlx5_vdpa_get_driver_features,
- .set_config_cb = mlx5_vdpa_set_config_cb,
- .get_vq_num_max = mlx5_vdpa_get_vq_num_max,
- .get_device_id = mlx5_vdpa_get_device_id,
- .get_vendor_id = mlx5_vdpa_get_vendor_id,
- .get_status = mlx5_vdpa_get_status,
- .set_status = mlx5_vdpa_set_status,
- .reset = mlx5_vdpa_reset,
- .get_config_size = mlx5_vdpa_get_config_size,
- .get_config = mlx5_vdpa_get_config,
- .set_config = mlx5_vdpa_set_config,
- .get_generation = mlx5_vdpa_get_generation,
- .set_map = mlx5_vdpa_set_map,
- .set_group_asid = mlx5_set_group_asid,
- .free = mlx5_vdpa_free,
- .suspend = mlx5_vdpa_suspend,
- };
- static int query_mtu(struct mlx5_core_dev *mdev, u16 *mtu)
- {
- u16 hw_mtu;
- int err;
- err = mlx5_query_nic_vport_mtu(mdev, &hw_mtu);
- if (err)
- return err;
- *mtu = hw_mtu - MLX5V_ETH_HARD_MTU;
- return 0;
- }
- static int alloc_resources(struct mlx5_vdpa_net *ndev)
- {
- struct mlx5_vdpa_net_resources *res = &ndev->res;
- int err;
- if (res->valid) {
- mlx5_vdpa_warn(&ndev->mvdev, "resources already allocated\n");
- return -EEXIST;
- }
- err = mlx5_vdpa_alloc_transport_domain(&ndev->mvdev, &res->tdn);
- if (err)
- return err;
- err = create_tis(ndev);
- if (err)
- goto err_tis;
- res->valid = true;
- return 0;
- err_tis:
- mlx5_vdpa_dealloc_transport_domain(&ndev->mvdev, res->tdn);
- return err;
- }
- static void free_resources(struct mlx5_vdpa_net *ndev)
- {
- struct mlx5_vdpa_net_resources *res = &ndev->res;
- if (!res->valid)
- return;
- destroy_tis(ndev);
- mlx5_vdpa_dealloc_transport_domain(&ndev->mvdev, res->tdn);
- res->valid = false;
- }
- static void init_mvqs(struct mlx5_vdpa_net *ndev)
- {
- struct mlx5_vdpa_virtqueue *mvq;
- int i;
- for (i = 0; i < ndev->mvdev.max_vqs; ++i) {
- mvq = &ndev->vqs[i];
- memset(mvq, 0, offsetof(struct mlx5_vdpa_virtqueue, ri));
- mvq->index = i;
- mvq->ndev = ndev;
- mvq->fwqp.fw = true;
- mvq->fw_state = MLX5_VIRTIO_NET_Q_OBJECT_NONE;
- }
- for (; i < ndev->mvdev.max_vqs; i++) {
- mvq = &ndev->vqs[i];
- memset(mvq, 0, offsetof(struct mlx5_vdpa_virtqueue, ri));
- mvq->index = i;
- mvq->ndev = ndev;
- }
- }
- struct mlx5_vdpa_mgmtdev {
- struct vdpa_mgmt_dev mgtdev;
- struct mlx5_adev *madev;
- struct mlx5_vdpa_net *ndev;
- };
- static u8 query_vport_state(struct mlx5_core_dev *mdev, u8 opmod, u16 vport)
- {
- u32 out[MLX5_ST_SZ_DW(query_vport_state_out)] = {};
- u32 in[MLX5_ST_SZ_DW(query_vport_state_in)] = {};
- int err;
- MLX5_SET(query_vport_state_in, in, opcode, MLX5_CMD_OP_QUERY_VPORT_STATE);
- MLX5_SET(query_vport_state_in, in, op_mod, opmod);
- MLX5_SET(query_vport_state_in, in, vport_number, vport);
- if (vport)
- MLX5_SET(query_vport_state_in, in, other_vport, 1);
- err = mlx5_cmd_exec_inout(mdev, query_vport_state, in, out);
- if (err)
- return 0;
- return MLX5_GET(query_vport_state_out, out, state);
- }
- static bool get_link_state(struct mlx5_vdpa_dev *mvdev)
- {
- if (query_vport_state(mvdev->mdev, MLX5_VPORT_STATE_OP_MOD_VNIC_VPORT, 0) ==
- VPORT_STATE_UP)
- return true;
- return false;
- }
- static void update_carrier(struct work_struct *work)
- {
- struct mlx5_vdpa_wq_ent *wqent;
- struct mlx5_vdpa_dev *mvdev;
- struct mlx5_vdpa_net *ndev;
- wqent = container_of(work, struct mlx5_vdpa_wq_ent, work);
- mvdev = wqent->mvdev;
- ndev = to_mlx5_vdpa_ndev(mvdev);
- if (get_link_state(mvdev))
- ndev->config.status |= cpu_to_mlx5vdpa16(mvdev, VIRTIO_NET_S_LINK_UP);
- else
- ndev->config.status &= cpu_to_mlx5vdpa16(mvdev, ~VIRTIO_NET_S_LINK_UP);
- if (ndev->nb_registered && ndev->config_cb.callback)
- ndev->config_cb.callback(ndev->config_cb.private);
- kfree(wqent);
- }
- static int event_handler(struct notifier_block *nb, unsigned long event, void *param)
- {
- struct mlx5_vdpa_net *ndev = container_of(nb, struct mlx5_vdpa_net, nb);
- struct mlx5_eqe *eqe = param;
- int ret = NOTIFY_DONE;
- struct mlx5_vdpa_wq_ent *wqent;
- if (event == MLX5_EVENT_TYPE_PORT_CHANGE) {
- switch (eqe->sub_type) {
- case MLX5_PORT_CHANGE_SUBTYPE_DOWN:
- case MLX5_PORT_CHANGE_SUBTYPE_ACTIVE:
- wqent = kzalloc(sizeof(*wqent), GFP_ATOMIC);
- if (!wqent)
- return NOTIFY_DONE;
- wqent->mvdev = &ndev->mvdev;
- INIT_WORK(&wqent->work, update_carrier);
- queue_work(ndev->mvdev.wq, &wqent->work);
- ret = NOTIFY_OK;
- break;
- default:
- return NOTIFY_DONE;
- }
- return ret;
- }
- return ret;
- }
- static int config_func_mtu(struct mlx5_core_dev *mdev, u16 mtu)
- {
- int inlen = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in);
- void *in;
- int err;
- in = kvzalloc(inlen, GFP_KERNEL);
- if (!in)
- return -ENOMEM;
- MLX5_SET(modify_nic_vport_context_in, in, field_select.mtu, 1);
- MLX5_SET(modify_nic_vport_context_in, in, nic_vport_context.mtu,
- mtu + MLX5V_ETH_HARD_MTU);
- MLX5_SET(modify_nic_vport_context_in, in, opcode,
- MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT);
- err = mlx5_cmd_exec_in(mdev, modify_nic_vport_context, in);
- kvfree(in);
- return err;
- }
- static int mlx5_vdpa_dev_add(struct vdpa_mgmt_dev *v_mdev, const char *name,
- const struct vdpa_dev_set_config *add_config)
- {
- struct mlx5_vdpa_mgmtdev *mgtdev = container_of(v_mdev, struct mlx5_vdpa_mgmtdev, mgtdev);
- struct virtio_net_config *config;
- struct mlx5_core_dev *pfmdev;
- struct mlx5_vdpa_dev *mvdev;
- struct mlx5_vdpa_net *ndev;
- struct mlx5_core_dev *mdev;
- u32 max_vqs;
- u16 mtu;
- int err;
- if (mgtdev->ndev)
- return -ENOSPC;
- mdev = mgtdev->madev->mdev;
- if (!(MLX5_CAP_DEV_VDPA_EMULATION(mdev, virtio_queue_type) &
- MLX5_VIRTIO_EMULATION_CAP_VIRTIO_QUEUE_TYPE_SPLIT)) {
- dev_warn(mdev->device, "missing support for split virtqueues\n");
- return -EOPNOTSUPP;
- }
- max_vqs = min_t(int, MLX5_CAP_DEV_VDPA_EMULATION(mdev, max_num_virtio_queues),
- 1 << MLX5_CAP_GEN(mdev, log_max_rqt_size));
- if (max_vqs < 2) {
- dev_warn(mdev->device,
- "%d virtqueues are supported. At least 2 are required\n",
- max_vqs);
- return -EAGAIN;
- }
- if (add_config->mask & BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MAX_VQP)) {
- if (add_config->net.max_vq_pairs > max_vqs / 2)
- return -EINVAL;
- max_vqs = min_t(u32, max_vqs, 2 * add_config->net.max_vq_pairs);
- } else {
- max_vqs = 2;
- }
- ndev = vdpa_alloc_device(struct mlx5_vdpa_net, mvdev.vdev, mdev->device, &mlx5_vdpa_ops,
- MLX5_VDPA_NUMVQ_GROUPS, MLX5_VDPA_NUM_AS, name, false);
- if (IS_ERR(ndev))
- return PTR_ERR(ndev);
- ndev->mvdev.mlx_features = mgtdev->mgtdev.supported_features;
- ndev->mvdev.max_vqs = max_vqs;
- mvdev = &ndev->mvdev;
- mvdev->mdev = mdev;
- ndev->vqs = kcalloc(max_vqs, sizeof(*ndev->vqs), GFP_KERNEL);
- ndev->event_cbs = kcalloc(max_vqs + 1, sizeof(*ndev->event_cbs), GFP_KERNEL);
- if (!ndev->vqs || !ndev->event_cbs) {
- err = -ENOMEM;
- goto err_alloc;
- }
- init_mvqs(ndev);
- init_rwsem(&ndev->reslock);
- config = &ndev->config;
- if (add_config->mask & BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MTU)) {
- err = config_func_mtu(mdev, add_config->net.mtu);
- if (err)
- goto err_alloc;
- }
- err = query_mtu(mdev, &mtu);
- if (err)
- goto err_alloc;
- ndev->config.mtu = cpu_to_mlx5vdpa16(mvdev, mtu);
- if (get_link_state(mvdev))
- ndev->config.status |= cpu_to_mlx5vdpa16(mvdev, VIRTIO_NET_S_LINK_UP);
- else
- ndev->config.status &= cpu_to_mlx5vdpa16(mvdev, ~VIRTIO_NET_S_LINK_UP);
- if (add_config->mask & (1 << VDPA_ATTR_DEV_NET_CFG_MACADDR)) {
- memcpy(ndev->config.mac, add_config->net.mac, ETH_ALEN);
- } else {
- err = mlx5_query_nic_vport_mac_address(mdev, 0, 0, config->mac);
- if (err)
- goto err_alloc;
- }
- if (!is_zero_ether_addr(config->mac)) {
- pfmdev = pci_get_drvdata(pci_physfn(mdev->pdev));
- err = mlx5_mpfs_add_mac(pfmdev, config->mac);
- if (err)
- goto err_alloc;
- ndev->mvdev.mlx_features |= BIT_ULL(VIRTIO_NET_F_MAC);
- }
- config->max_virtqueue_pairs = cpu_to_mlx5vdpa16(mvdev, max_vqs / 2);
- mvdev->vdev.dma_dev = &mdev->pdev->dev;
- err = mlx5_vdpa_alloc_resources(&ndev->mvdev);
- if (err)
- goto err_mpfs;
- if (MLX5_CAP_GEN(mvdev->mdev, umem_uid_0)) {
- err = mlx5_vdpa_create_mr(mvdev, NULL, 0);
- if (err)
- goto err_res;
- }
- err = alloc_resources(ndev);
- if (err)
- goto err_mr;
- ndev->cvq_ent.mvdev = mvdev;
- INIT_WORK(&ndev->cvq_ent.work, mlx5_cvq_kick_handler);
- mvdev->wq = create_singlethread_workqueue("mlx5_vdpa_wq");
- if (!mvdev->wq) {
- err = -ENOMEM;
- goto err_res2;
- }
- ndev->nb.notifier_call = event_handler;
- mlx5_notifier_register(mdev, &ndev->nb);
- ndev->nb_registered = true;
- mvdev->vdev.mdev = &mgtdev->mgtdev;
- err = _vdpa_register_device(&mvdev->vdev, max_vqs + 1);
- if (err)
- goto err_reg;
- mgtdev->ndev = ndev;
- return 0;
- err_reg:
- destroy_workqueue(mvdev->wq);
- err_res2:
- free_resources(ndev);
- err_mr:
- mlx5_vdpa_destroy_mr(mvdev);
- err_res:
- mlx5_vdpa_free_resources(&ndev->mvdev);
- err_mpfs:
- if (!is_zero_ether_addr(config->mac))
- mlx5_mpfs_del_mac(pfmdev, config->mac);
- err_alloc:
- put_device(&mvdev->vdev.dev);
- return err;
- }
- static void mlx5_vdpa_dev_del(struct vdpa_mgmt_dev *v_mdev, struct vdpa_device *dev)
- {
- struct mlx5_vdpa_mgmtdev *mgtdev = container_of(v_mdev, struct mlx5_vdpa_mgmtdev, mgtdev);
- struct mlx5_vdpa_dev *mvdev = to_mvdev(dev);
- struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
- struct workqueue_struct *wq;
- if (ndev->nb_registered) {
- ndev->nb_registered = false;
- mlx5_notifier_unregister(mvdev->mdev, &ndev->nb);
- }
- wq = mvdev->wq;
- mvdev->wq = NULL;
- destroy_workqueue(wq);
- _vdpa_unregister_device(dev);
- mgtdev->ndev = NULL;
- }
- static const struct vdpa_mgmtdev_ops mdev_ops = {
- .dev_add = mlx5_vdpa_dev_add,
- .dev_del = mlx5_vdpa_dev_del,
- };
- static struct virtio_device_id id_table[] = {
- { VIRTIO_ID_NET, VIRTIO_DEV_ANY_ID },
- { 0 },
- };
- static int mlx5v_probe(struct auxiliary_device *adev,
- const struct auxiliary_device_id *id)
- {
- struct mlx5_adev *madev = container_of(adev, struct mlx5_adev, adev);
- struct mlx5_core_dev *mdev = madev->mdev;
- struct mlx5_vdpa_mgmtdev *mgtdev;
- int err;
- mgtdev = kzalloc(sizeof(*mgtdev), GFP_KERNEL);
- if (!mgtdev)
- return -ENOMEM;
- mgtdev->mgtdev.ops = &mdev_ops;
- mgtdev->mgtdev.device = mdev->device;
- mgtdev->mgtdev.id_table = id_table;
- mgtdev->mgtdev.config_attr_mask = BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MACADDR) |
- BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MAX_VQP) |
- BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MTU);
- mgtdev->mgtdev.max_supported_vqs =
- MLX5_CAP_DEV_VDPA_EMULATION(mdev, max_num_virtio_queues) + 1;
- mgtdev->mgtdev.supported_features = get_supported_features(mdev);
- mgtdev->madev = madev;
- err = vdpa_mgmtdev_register(&mgtdev->mgtdev);
- if (err)
- goto reg_err;
- auxiliary_set_drvdata(adev, mgtdev);
- return 0;
- reg_err:
- kfree(mgtdev);
- return err;
- }
- static void mlx5v_remove(struct auxiliary_device *adev)
- {
- struct mlx5_vdpa_mgmtdev *mgtdev;
- mgtdev = auxiliary_get_drvdata(adev);
- vdpa_mgmtdev_unregister(&mgtdev->mgtdev);
- kfree(mgtdev);
- }
- static const struct auxiliary_device_id mlx5v_id_table[] = {
- { .name = MLX5_ADEV_NAME ".vnet", },
- {},
- };
- MODULE_DEVICE_TABLE(auxiliary, mlx5v_id_table);
- static struct auxiliary_driver mlx5v_driver = {
- .name = "vnet",
- .probe = mlx5v_probe,
- .remove = mlx5v_remove,
- .id_table = mlx5v_id_table,
- };
- module_auxiliary_driver(mlx5v_driver);
|