123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688 |
- /*
- * QEMU Hyper-V VMBus
- *
- * Copyright (c) 2017-2018 Virtuozzo International GmbH.
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
- #include "qemu/osdep.h"
- #include "qemu/error-report.h"
- #include "qemu/main-loop.h"
- #include "qapi/error.h"
- #include "migration/vmstate.h"
- #include "hw/qdev-properties.h"
- #include "hw/qdev-properties-system.h"
- #include "hw/hyperv/hyperv.h"
- #include "hw/hyperv/vmbus.h"
- #include "hw/hyperv/vmbus-bridge.h"
- #include "hw/sysbus.h"
- #include "cpu.h"
- #include "trace.h"
- enum {
- VMGPADL_INIT,
- VMGPADL_ALIVE,
- VMGPADL_TEARINGDOWN,
- VMGPADL_TORNDOWN,
- };
- struct VMBusGpadl {
- /* GPADL id */
- uint32_t id;
- /* associated channel id (rudimentary?) */
- uint32_t child_relid;
- /* number of pages in the GPADL as declared in GPADL_HEADER message */
- uint32_t num_gfns;
- /*
- * Due to limited message size, GPADL may not fit fully in a single
- * GPADL_HEADER message, and is further popluated using GPADL_BODY
- * messages. @seen_gfns is the number of pages seen so far; once it
- * reaches @num_gfns, the GPADL is ready to use.
- */
- uint32_t seen_gfns;
- /* array of GFNs (of size @num_gfns once allocated) */
- uint64_t *gfns;
- uint8_t state;
- QTAILQ_ENTRY(VMBusGpadl) link;
- VMBus *vmbus;
- unsigned refcount;
- };
- /*
- * Wrap sequential read from / write to GPADL.
- */
- typedef struct GpadlIter {
- VMBusGpadl *gpadl;
- AddressSpace *as;
- DMADirection dir;
- /* offset into GPADL where the next i/o will be performed */
- uint32_t off;
- /*
- * Cached mapping of the currently accessed page, up to page boundary.
- * Updated lazily on i/o.
- * Note: MemoryRegionCache can not be used here because pages in the GPADL
- * are non-contiguous and may belong to different memory regions.
- */
- void *map;
- /* offset after last i/o (i.e. not affected by seek) */
- uint32_t last_off;
- /*
- * Indicator that the iterator is active and may have a cached mapping.
- * Allows to enforce bracketing of all i/o (which may create cached
- * mappings) and thus exclude mapping leaks.
- */
- bool active;
- } GpadlIter;
- /*
- * Ring buffer. There are two of them, sitting in the same GPADL, for each
- * channel.
- * Each ring buffer consists of a set of pages, with the first page containing
- * the ring buffer header, and the remaining pages being for data packets.
- */
- typedef struct VMBusRingBufCommon {
- AddressSpace *as;
- /* GPA of the ring buffer header */
- dma_addr_t rb_addr;
- /* start and length of the ring buffer data area within GPADL */
- uint32_t base;
- uint32_t len;
- GpadlIter iter;
- } VMBusRingBufCommon;
- typedef struct VMBusSendRingBuf {
- VMBusRingBufCommon common;
- /* current write index, to be committed at the end of send */
- uint32_t wr_idx;
- /* write index at the start of send */
- uint32_t last_wr_idx;
- /* space to be requested from the guest */
- uint32_t wanted;
- /* space reserved for planned sends */
- uint32_t reserved;
- /* last seen read index */
- uint32_t last_seen_rd_idx;
- } VMBusSendRingBuf;
- typedef struct VMBusRecvRingBuf {
- VMBusRingBufCommon common;
- /* current read index, to be committed at the end of receive */
- uint32_t rd_idx;
- /* read index at the start of receive */
- uint32_t last_rd_idx;
- /* last seen write index */
- uint32_t last_seen_wr_idx;
- } VMBusRecvRingBuf;
- enum {
- VMOFFER_INIT,
- VMOFFER_SENDING,
- VMOFFER_SENT,
- };
- enum {
- VMCHAN_INIT,
- VMCHAN_OPENING,
- VMCHAN_OPEN,
- };
- struct VMBusChannel {
- VMBusDevice *dev;
- /* channel id */
- uint32_t id;
- /*
- * subchannel index within the device; subchannel #0 is "primary" and
- * always exists
- */
- uint16_t subchan_idx;
- uint32_t open_id;
- /* VP_INDEX of the vCPU to notify with (synthetic) interrupts */
- uint32_t target_vp;
- /* GPADL id to use for the ring buffers */
- uint32_t ringbuf_gpadl;
- /* start (in pages) of the send ring buffer within @ringbuf_gpadl */
- uint32_t ringbuf_send_offset;
- uint8_t offer_state;
- uint8_t state;
- bool is_open;
- /* main device worker; copied from the device class */
- VMBusChannelNotifyCb notify_cb;
- /*
- * guest->host notifications, either sent directly or dispatched via
- * interrupt page (older VMBus)
- */
- EventNotifier notifier;
- VMBus *vmbus;
- /*
- * SINT route to signal with host->guest notifications; may be shared with
- * the main VMBus SINT route
- */
- HvSintRoute *notify_route;
- VMBusGpadl *gpadl;
- VMBusSendRingBuf send_ringbuf;
- VMBusRecvRingBuf recv_ringbuf;
- QTAILQ_ENTRY(VMBusChannel) link;
- };
- /*
- * Hyper-V spec mandates that every message port has 16 buffers, which means
- * that the guest can post up to this many messages without blocking.
- * Therefore a queue for incoming messages has to be provided.
- * For outgoing (i.e. host->guest) messages there's no queue; the VMBus just
- * doesn't transition to a new state until the message is known to have been
- * successfully delivered to the respective SynIC message slot.
- */
- #define HV_MSG_QUEUE_LEN 16
- /* Hyper-V devices never use channel #0. Must be something special. */
- #define VMBUS_FIRST_CHANID 1
- /* Each channel occupies one bit within a single event page sint slot. */
- #define VMBUS_CHANID_COUNT (HV_EVENT_FLAGS_COUNT - VMBUS_FIRST_CHANID)
- /* Leave a few connection numbers for other purposes. */
- #define VMBUS_CHAN_CONNECTION_OFFSET 16
- /*
- * Since the success or failure of sending a message is reported
- * asynchronously, the VMBus state machine has effectively two entry points:
- * vmbus_run and vmbus_msg_cb (the latter is called when the host->guest
- * message delivery status becomes known). Both are run as oneshot BHs on the
- * main aio context, ensuring serialization.
- */
- enum {
- VMBUS_LISTEN,
- VMBUS_HANDSHAKE,
- VMBUS_OFFER,
- VMBUS_CREATE_GPADL,
- VMBUS_TEARDOWN_GPADL,
- VMBUS_OPEN_CHANNEL,
- VMBUS_UNLOAD,
- VMBUS_STATE_MAX
- };
- struct VMBus {
- BusState parent;
- uint8_t state;
- /* protection against recursive aio_poll (see vmbus_run) */
- bool in_progress;
- /* whether there's a message being delivered to the guest */
- bool msg_in_progress;
- uint32_t version;
- /* VP_INDEX of the vCPU to send messages and interrupts to */
- uint32_t target_vp;
- HvSintRoute *sint_route;
- /*
- * interrupt page for older protocol versions; newer ones use SynIC event
- * flags directly
- */
- hwaddr int_page_gpa;
- DECLARE_BITMAP(chanid_bitmap, VMBUS_CHANID_COUNT);
- /* incoming message queue */
- struct hyperv_post_message_input rx_queue[HV_MSG_QUEUE_LEN];
- uint8_t rx_queue_head;
- uint8_t rx_queue_size;
- QemuMutex rx_queue_lock;
- QTAILQ_HEAD(, VMBusGpadl) gpadl_list;
- QTAILQ_HEAD(, VMBusChannel) channel_list;
- /*
- * guest->host notifications for older VMBus, to be dispatched via
- * interrupt page
- */
- EventNotifier notifier;
- };
- static bool gpadl_full(VMBusGpadl *gpadl)
- {
- return gpadl->seen_gfns == gpadl->num_gfns;
- }
- static VMBusGpadl *create_gpadl(VMBus *vmbus, uint32_t id,
- uint32_t child_relid, uint32_t num_gfns)
- {
- VMBusGpadl *gpadl = g_new0(VMBusGpadl, 1);
- gpadl->id = id;
- gpadl->child_relid = child_relid;
- gpadl->num_gfns = num_gfns;
- gpadl->gfns = g_new(uint64_t, num_gfns);
- QTAILQ_INSERT_HEAD(&vmbus->gpadl_list, gpadl, link);
- gpadl->vmbus = vmbus;
- gpadl->refcount = 1;
- return gpadl;
- }
- static void free_gpadl(VMBusGpadl *gpadl)
- {
- QTAILQ_REMOVE(&gpadl->vmbus->gpadl_list, gpadl, link);
- g_free(gpadl->gfns);
- g_free(gpadl);
- }
- static VMBusGpadl *find_gpadl(VMBus *vmbus, uint32_t gpadl_id)
- {
- VMBusGpadl *gpadl;
- QTAILQ_FOREACH(gpadl, &vmbus->gpadl_list, link) {
- if (gpadl->id == gpadl_id) {
- return gpadl;
- }
- }
- return NULL;
- }
- VMBusGpadl *vmbus_get_gpadl(VMBusChannel *chan, uint32_t gpadl_id)
- {
- VMBusGpadl *gpadl = find_gpadl(chan->vmbus, gpadl_id);
- if (!gpadl || !gpadl_full(gpadl)) {
- return NULL;
- }
- gpadl->refcount++;
- return gpadl;
- }
- void vmbus_put_gpadl(VMBusGpadl *gpadl)
- {
- if (!gpadl) {
- return;
- }
- if (--gpadl->refcount) {
- return;
- }
- free_gpadl(gpadl);
- }
- uint32_t vmbus_gpadl_len(VMBusGpadl *gpadl)
- {
- return gpadl->num_gfns * TARGET_PAGE_SIZE;
- }
- static void gpadl_iter_init(GpadlIter *iter, VMBusGpadl *gpadl,
- AddressSpace *as, DMADirection dir)
- {
- iter->gpadl = gpadl;
- iter->as = as;
- iter->dir = dir;
- iter->active = false;
- }
- static inline void gpadl_iter_cache_unmap(GpadlIter *iter)
- {
- uint32_t map_start_in_page = (uintptr_t)iter->map & ~TARGET_PAGE_MASK;
- uint32_t io_end_in_page = ((iter->last_off - 1) & ~TARGET_PAGE_MASK) + 1;
- /* mapping is only done to do non-zero amount of i/o */
- assert(iter->last_off > 0);
- assert(map_start_in_page < io_end_in_page);
- dma_memory_unmap(iter->as, iter->map, TARGET_PAGE_SIZE - map_start_in_page,
- iter->dir, io_end_in_page - map_start_in_page);
- }
- /*
- * Copy exactly @len bytes between the GPADL pointed to by @iter and @buf.
- * The direction of the copy is determined by @iter->dir.
- * The caller must ensure the operation overflows neither @buf nor the GPADL
- * (there's an assert for the latter).
- * Reuse the currently mapped page in the GPADL if possible.
- */
- static ssize_t gpadl_iter_io(GpadlIter *iter, void *buf, uint32_t len)
- {
- ssize_t ret = len;
- assert(iter->active);
- while (len) {
- uint32_t off_in_page = iter->off & ~TARGET_PAGE_MASK;
- uint32_t pgleft = TARGET_PAGE_SIZE - off_in_page;
- uint32_t cplen = MIN(pgleft, len);
- void *p;
- /* try to reuse the cached mapping */
- if (iter->map) {
- uint32_t map_start_in_page =
- (uintptr_t)iter->map & ~TARGET_PAGE_MASK;
- uint32_t off_base = iter->off & ~TARGET_PAGE_MASK;
- uint32_t mapped_base = (iter->last_off - 1) & ~TARGET_PAGE_MASK;
- if (off_base != mapped_base || off_in_page < map_start_in_page) {
- gpadl_iter_cache_unmap(iter);
- iter->map = NULL;
- }
- }
- if (!iter->map) {
- dma_addr_t maddr;
- dma_addr_t mlen = pgleft;
- uint32_t idx = iter->off >> TARGET_PAGE_BITS;
- assert(idx < iter->gpadl->num_gfns);
- maddr = (iter->gpadl->gfns[idx] << TARGET_PAGE_BITS) | off_in_page;
- iter->map = dma_memory_map(iter->as, maddr, &mlen, iter->dir,
- MEMTXATTRS_UNSPECIFIED);
- if (mlen != pgleft) {
- dma_memory_unmap(iter->as, iter->map, mlen, iter->dir, 0);
- iter->map = NULL;
- return -EFAULT;
- }
- }
- p = (void *)(uintptr_t)(((uintptr_t)iter->map & TARGET_PAGE_MASK) |
- off_in_page);
- if (iter->dir == DMA_DIRECTION_FROM_DEVICE) {
- memcpy(p, buf, cplen);
- } else {
- memcpy(buf, p, cplen);
- }
- buf += cplen;
- len -= cplen;
- iter->off += cplen;
- iter->last_off = iter->off;
- }
- return ret;
- }
- /*
- * Position the iterator @iter at new offset @new_off.
- * If this results in the cached mapping being unusable with the new offset,
- * unmap it.
- */
- static inline void gpadl_iter_seek(GpadlIter *iter, uint32_t new_off)
- {
- assert(iter->active);
- iter->off = new_off;
- }
- /*
- * Start a series of i/o on the GPADL.
- * After this i/o and seek operations on @iter become legal.
- */
- static inline void gpadl_iter_start_io(GpadlIter *iter)
- {
- assert(!iter->active);
- /* mapping is cached lazily on i/o */
- iter->map = NULL;
- iter->active = true;
- }
- /*
- * End the eariler started series of i/o on the GPADL and release the cached
- * mapping if any.
- */
- static inline void gpadl_iter_end_io(GpadlIter *iter)
- {
- assert(iter->active);
- if (iter->map) {
- gpadl_iter_cache_unmap(iter);
- }
- iter->active = false;
- }
- static void vmbus_resched(VMBus *vmbus);
- static void vmbus_msg_cb(void *data, int status);
- ssize_t vmbus_iov_to_gpadl(VMBusChannel *chan, VMBusGpadl *gpadl, uint32_t off,
- const struct iovec *iov, size_t iov_cnt)
- {
- GpadlIter iter;
- size_t i;
- ssize_t ret = 0;
- gpadl_iter_init(&iter, gpadl, chan->dev->dma_as,
- DMA_DIRECTION_FROM_DEVICE);
- gpadl_iter_start_io(&iter);
- gpadl_iter_seek(&iter, off);
- for (i = 0; i < iov_cnt; i++) {
- ret = gpadl_iter_io(&iter, iov[i].iov_base, iov[i].iov_len);
- if (ret < 0) {
- goto out;
- }
- }
- out:
- gpadl_iter_end_io(&iter);
- return ret;
- }
- int vmbus_map_sgl(VMBusChanReq *req, DMADirection dir, struct iovec *iov,
- unsigned iov_cnt, size_t len, size_t off)
- {
- int ret_cnt = 0, ret;
- unsigned i;
- QEMUSGList *sgl = &req->sgl;
- ScatterGatherEntry *sg = sgl->sg;
- for (i = 0; i < sgl->nsg; i++) {
- if (sg[i].len > off) {
- break;
- }
- off -= sg[i].len;
- }
- for (; len && i < sgl->nsg; i++) {
- dma_addr_t mlen = MIN(sg[i].len - off, len);
- dma_addr_t addr = sg[i].base + off;
- len -= mlen;
- off = 0;
- for (; mlen; ret_cnt++) {
- dma_addr_t l = mlen;
- dma_addr_t a = addr;
- if (ret_cnt == iov_cnt) {
- ret = -ENOBUFS;
- goto err;
- }
- iov[ret_cnt].iov_base = dma_memory_map(sgl->as, a, &l, dir,
- MEMTXATTRS_UNSPECIFIED);
- if (!l) {
- ret = -EFAULT;
- goto err;
- }
- iov[ret_cnt].iov_len = l;
- addr += l;
- mlen -= l;
- }
- }
- return ret_cnt;
- err:
- vmbus_unmap_sgl(req, dir, iov, ret_cnt, 0);
- return ret;
- }
- void vmbus_unmap_sgl(VMBusChanReq *req, DMADirection dir, struct iovec *iov,
- unsigned iov_cnt, size_t accessed)
- {
- QEMUSGList *sgl = &req->sgl;
- unsigned i;
- for (i = 0; i < iov_cnt; i++) {
- size_t acsd = MIN(accessed, iov[i].iov_len);
- dma_memory_unmap(sgl->as, iov[i].iov_base, iov[i].iov_len, dir, acsd);
- accessed -= acsd;
- }
- }
- static const VMStateDescription vmstate_gpadl = {
- .name = "vmbus/gpadl",
- .version_id = 0,
- .minimum_version_id = 0,
- .fields = (const VMStateField[]) {
- VMSTATE_UINT32(id, VMBusGpadl),
- VMSTATE_UINT32(child_relid, VMBusGpadl),
- VMSTATE_UINT32(num_gfns, VMBusGpadl),
- VMSTATE_UINT32(seen_gfns, VMBusGpadl),
- VMSTATE_VARRAY_UINT32_ALLOC(gfns, VMBusGpadl, num_gfns, 0,
- vmstate_info_uint64, uint64_t),
- VMSTATE_UINT8(state, VMBusGpadl),
- VMSTATE_END_OF_LIST()
- }
- };
- /*
- * Wrap the index into a ring buffer of @len bytes.
- * @idx is assumed not to exceed twice the size of the ringbuffer, so only
- * single wraparound is considered.
- */
- static inline uint32_t rb_idx_wrap(uint32_t idx, uint32_t len)
- {
- if (idx >= len) {
- idx -= len;
- }
- return idx;
- }
- /*
- * Circular difference between two indices into a ring buffer of @len bytes.
- * @allow_catchup - whether @idx1 may catch up @idx2; e.g. read index may catch
- * up write index but not vice versa.
- */
- static inline uint32_t rb_idx_delta(uint32_t idx1, uint32_t idx2, uint32_t len,
- bool allow_catchup)
- {
- return rb_idx_wrap(idx2 + len - idx1 - !allow_catchup, len);
- }
- static vmbus_ring_buffer *ringbuf_map_hdr(VMBusRingBufCommon *ringbuf)
- {
- vmbus_ring_buffer *rb;
- dma_addr_t mlen = sizeof(*rb);
- rb = dma_memory_map(ringbuf->as, ringbuf->rb_addr, &mlen,
- DMA_DIRECTION_FROM_DEVICE, MEMTXATTRS_UNSPECIFIED);
- if (mlen != sizeof(*rb)) {
- dma_memory_unmap(ringbuf->as, rb, mlen,
- DMA_DIRECTION_FROM_DEVICE, 0);
- return NULL;
- }
- return rb;
- }
- static void ringbuf_unmap_hdr(VMBusRingBufCommon *ringbuf,
- vmbus_ring_buffer *rb, bool dirty)
- {
- assert(rb);
- dma_memory_unmap(ringbuf->as, rb, sizeof(*rb), DMA_DIRECTION_FROM_DEVICE,
- dirty ? sizeof(*rb) : 0);
- }
- static void ringbuf_init_common(VMBusRingBufCommon *ringbuf, VMBusGpadl *gpadl,
- AddressSpace *as, DMADirection dir,
- uint32_t begin, uint32_t end)
- {
- ringbuf->as = as;
- ringbuf->rb_addr = gpadl->gfns[begin] << TARGET_PAGE_BITS;
- ringbuf->base = (begin + 1) << TARGET_PAGE_BITS;
- ringbuf->len = (end - begin - 1) << TARGET_PAGE_BITS;
- gpadl_iter_init(&ringbuf->iter, gpadl, as, dir);
- }
- static int ringbufs_init(VMBusChannel *chan)
- {
- vmbus_ring_buffer *rb;
- VMBusSendRingBuf *send_ringbuf = &chan->send_ringbuf;
- VMBusRecvRingBuf *recv_ringbuf = &chan->recv_ringbuf;
- if (chan->ringbuf_send_offset <= 1 ||
- chan->gpadl->num_gfns <= chan->ringbuf_send_offset + 1) {
- return -EINVAL;
- }
- ringbuf_init_common(&recv_ringbuf->common, chan->gpadl, chan->dev->dma_as,
- DMA_DIRECTION_TO_DEVICE, 0, chan->ringbuf_send_offset);
- ringbuf_init_common(&send_ringbuf->common, chan->gpadl, chan->dev->dma_as,
- DMA_DIRECTION_FROM_DEVICE, chan->ringbuf_send_offset,
- chan->gpadl->num_gfns);
- send_ringbuf->wanted = 0;
- send_ringbuf->reserved = 0;
- rb = ringbuf_map_hdr(&recv_ringbuf->common);
- if (!rb) {
- return -EFAULT;
- }
- recv_ringbuf->rd_idx = recv_ringbuf->last_rd_idx = rb->read_index;
- ringbuf_unmap_hdr(&recv_ringbuf->common, rb, false);
- rb = ringbuf_map_hdr(&send_ringbuf->common);
- if (!rb) {
- return -EFAULT;
- }
- send_ringbuf->wr_idx = send_ringbuf->last_wr_idx = rb->write_index;
- send_ringbuf->last_seen_rd_idx = rb->read_index;
- rb->feature_bits |= VMBUS_RING_BUFFER_FEAT_PENDING_SZ;
- ringbuf_unmap_hdr(&send_ringbuf->common, rb, true);
- if (recv_ringbuf->rd_idx >= recv_ringbuf->common.len ||
- send_ringbuf->wr_idx >= send_ringbuf->common.len) {
- return -EOVERFLOW;
- }
- return 0;
- }
- /*
- * Perform io between the GPADL-backed ringbuffer @ringbuf and @buf, wrapping
- * around if needed.
- * @len is assumed not to exceed the size of the ringbuffer, so only single
- * wraparound is considered.
- */
- static ssize_t ringbuf_io(VMBusRingBufCommon *ringbuf, void *buf, uint32_t len)
- {
- ssize_t ret1 = 0, ret2 = 0;
- uint32_t remain = ringbuf->len + ringbuf->base - ringbuf->iter.off;
- if (len >= remain) {
- ret1 = gpadl_iter_io(&ringbuf->iter, buf, remain);
- if (ret1 < 0) {
- return ret1;
- }
- gpadl_iter_seek(&ringbuf->iter, ringbuf->base);
- buf += remain;
- len -= remain;
- }
- ret2 = gpadl_iter_io(&ringbuf->iter, buf, len);
- if (ret2 < 0) {
- return ret2;
- }
- return ret1 + ret2;
- }
- /*
- * Position the circular iterator within @ringbuf to offset @new_off, wrapping
- * around if needed.
- * @new_off is assumed not to exceed twice the size of the ringbuffer, so only
- * single wraparound is considered.
- */
- static inline void ringbuf_seek(VMBusRingBufCommon *ringbuf, uint32_t new_off)
- {
- gpadl_iter_seek(&ringbuf->iter,
- ringbuf->base + rb_idx_wrap(new_off, ringbuf->len));
- }
- static inline uint32_t ringbuf_tell(VMBusRingBufCommon *ringbuf)
- {
- return ringbuf->iter.off - ringbuf->base;
- }
- static inline void ringbuf_start_io(VMBusRingBufCommon *ringbuf)
- {
- gpadl_iter_start_io(&ringbuf->iter);
- }
- static inline void ringbuf_end_io(VMBusRingBufCommon *ringbuf)
- {
- gpadl_iter_end_io(&ringbuf->iter);
- }
- VMBusDevice *vmbus_channel_device(VMBusChannel *chan)
- {
- return chan->dev;
- }
- VMBusChannel *vmbus_device_channel(VMBusDevice *dev, uint32_t chan_idx)
- {
- if (chan_idx >= dev->num_channels) {
- return NULL;
- }
- return &dev->channels[chan_idx];
- }
- uint32_t vmbus_channel_idx(VMBusChannel *chan)
- {
- return chan - chan->dev->channels;
- }
- void vmbus_channel_notify_host(VMBusChannel *chan)
- {
- event_notifier_set(&chan->notifier);
- }
- bool vmbus_channel_is_open(VMBusChannel *chan)
- {
- return chan->is_open;
- }
- /*
- * Notify the guest side about the data to work on in the channel ring buffer.
- * The notification is done by signaling a dedicated per-channel SynIC event
- * flag (more recent guests) or setting a bit in the interrupt page and firing
- * the VMBus SINT (older guests).
- */
- static int vmbus_channel_notify_guest(VMBusChannel *chan)
- {
- int res = 0;
- unsigned long *int_map, mask;
- unsigned idx;
- hwaddr addr = chan->vmbus->int_page_gpa;
- hwaddr len = TARGET_PAGE_SIZE / 2, dirty = 0;
- trace_vmbus_channel_notify_guest(chan->id);
- if (!addr) {
- return hyperv_set_event_flag(chan->notify_route, chan->id);
- }
- int_map = cpu_physical_memory_map(addr, &len, 1);
- if (len != TARGET_PAGE_SIZE / 2) {
- res = -ENXIO;
- goto unmap;
- }
- idx = BIT_WORD(chan->id);
- mask = BIT_MASK(chan->id);
- if ((qatomic_fetch_or(&int_map[idx], mask) & mask) != mask) {
- res = hyperv_sint_route_set_sint(chan->notify_route);
- dirty = len;
- }
- unmap:
- cpu_physical_memory_unmap(int_map, len, 1, dirty);
- return res;
- }
- #define VMBUS_PKT_TRAILER sizeof(uint64_t)
- static uint32_t vmbus_pkt_hdr_set_offsets(vmbus_packet_hdr *hdr,
- uint32_t desclen, uint32_t msglen)
- {
- hdr->offset_qwords = sizeof(*hdr) / sizeof(uint64_t) +
- DIV_ROUND_UP(desclen, sizeof(uint64_t));
- hdr->len_qwords = hdr->offset_qwords +
- DIV_ROUND_UP(msglen, sizeof(uint64_t));
- return hdr->len_qwords * sizeof(uint64_t) + VMBUS_PKT_TRAILER;
- }
- /*
- * Simplified ring buffer operation with paired barriers annotations in the
- * producer and consumer loops:
- *
- * producer * consumer
- * ~~~~~~~~ * ~~~~~~~~
- * write pending_send_sz * read write_index
- * smp_mb [A] * smp_mb [C]
- * read read_index * read packet
- * smp_mb [B] * read/write out-of-band data
- * read/write out-of-band data * smp_mb [B]
- * write packet * write read_index
- * smp_mb [C] * smp_mb [A]
- * write write_index * read pending_send_sz
- * smp_wmb [D] * smp_rmb [D]
- * write pending_send_sz * read write_index
- * ... * ...
- */
- static inline uint32_t ringbuf_send_avail(VMBusSendRingBuf *ringbuf)
- {
- /* don't trust guest data */
- if (ringbuf->last_seen_rd_idx >= ringbuf->common.len) {
- return 0;
- }
- return rb_idx_delta(ringbuf->wr_idx, ringbuf->last_seen_rd_idx,
- ringbuf->common.len, false);
- }
- static ssize_t ringbuf_send_update_idx(VMBusChannel *chan)
- {
- VMBusSendRingBuf *ringbuf = &chan->send_ringbuf;
- vmbus_ring_buffer *rb;
- uint32_t written;
- written = rb_idx_delta(ringbuf->last_wr_idx, ringbuf->wr_idx,
- ringbuf->common.len, true);
- if (!written) {
- return 0;
- }
- rb = ringbuf_map_hdr(&ringbuf->common);
- if (!rb) {
- return -EFAULT;
- }
- ringbuf->reserved -= written;
- /* prevent reorder with the data operation and packet write */
- smp_mb(); /* barrier pair [C] */
- rb->write_index = ringbuf->wr_idx;
- /*
- * If the producer earlier indicated that it wants to be notified when the
- * consumer frees certain amount of space in the ring buffer, that amount
- * is reduced by the size of the completed write.
- */
- if (ringbuf->wanted) {
- /* otherwise reservation would fail */
- assert(ringbuf->wanted < written);
- ringbuf->wanted -= written;
- /* prevent reorder with write_index write */
- smp_wmb(); /* barrier pair [D] */
- rb->pending_send_sz = ringbuf->wanted;
- }
- /* prevent reorder with write_index or pending_send_sz write */
- smp_mb(); /* barrier pair [A] */
- ringbuf->last_seen_rd_idx = rb->read_index;
- /*
- * The consumer may have missed the reduction of pending_send_sz and skip
- * notification, so re-check the blocking condition, and, if it's no longer
- * true, ensure processing another iteration by simulating consumer's
- * notification.
- */
- if (ringbuf_send_avail(ringbuf) >= ringbuf->wanted) {
- vmbus_channel_notify_host(chan);
- }
- /* skip notification by consumer's request */
- if (rb->interrupt_mask) {
- goto out;
- }
- /*
- * The consumer hasn't caught up with the producer's previous state so it's
- * not blocked.
- * (last_seen_rd_idx comes from the guest but it's safe to use w/o
- * validation here as it only affects notification.)
- */
- if (rb_idx_delta(ringbuf->last_seen_rd_idx, ringbuf->wr_idx,
- ringbuf->common.len, true) > written) {
- goto out;
- }
- vmbus_channel_notify_guest(chan);
- out:
- ringbuf_unmap_hdr(&ringbuf->common, rb, true);
- ringbuf->last_wr_idx = ringbuf->wr_idx;
- return written;
- }
- int vmbus_channel_reserve(VMBusChannel *chan,
- uint32_t desclen, uint32_t msglen)
- {
- VMBusSendRingBuf *ringbuf = &chan->send_ringbuf;
- vmbus_ring_buffer *rb = NULL;
- vmbus_packet_hdr hdr;
- uint32_t needed = ringbuf->reserved +
- vmbus_pkt_hdr_set_offsets(&hdr, desclen, msglen);
- /* avoid touching the guest memory if possible */
- if (likely(needed <= ringbuf_send_avail(ringbuf))) {
- goto success;
- }
- rb = ringbuf_map_hdr(&ringbuf->common);
- if (!rb) {
- return -EFAULT;
- }
- /* fetch read index from guest memory and try again */
- ringbuf->last_seen_rd_idx = rb->read_index;
- if (likely(needed <= ringbuf_send_avail(ringbuf))) {
- goto success;
- }
- rb->pending_send_sz = needed;
- /*
- * The consumer may have made progress and freed up some space before
- * seeing updated pending_send_sz, so re-read read_index (preventing
- * reorder with the pending_send_sz write) and try again.
- */
- smp_mb(); /* barrier pair [A] */
- ringbuf->last_seen_rd_idx = rb->read_index;
- if (needed > ringbuf_send_avail(ringbuf)) {
- goto out;
- }
- success:
- ringbuf->reserved = needed;
- needed = 0;
- /* clear pending_send_sz if it was set */
- if (ringbuf->wanted) {
- if (!rb) {
- rb = ringbuf_map_hdr(&ringbuf->common);
- if (!rb) {
- /* failure to clear pending_send_sz is non-fatal */
- goto out;
- }
- }
- rb->pending_send_sz = 0;
- }
- /* prevent reorder of the following data operation with read_index read */
- smp_mb(); /* barrier pair [B] */
- out:
- if (rb) {
- ringbuf_unmap_hdr(&ringbuf->common, rb, ringbuf->wanted == needed);
- }
- ringbuf->wanted = needed;
- return needed ? -ENOSPC : 0;
- }
- ssize_t vmbus_channel_send(VMBusChannel *chan, uint16_t pkt_type,
- void *desc, uint32_t desclen,
- void *msg, uint32_t msglen,
- bool need_comp, uint64_t transaction_id)
- {
- ssize_t ret = 0;
- vmbus_packet_hdr hdr;
- uint32_t totlen;
- VMBusSendRingBuf *ringbuf = &chan->send_ringbuf;
- if (!vmbus_channel_is_open(chan)) {
- return -EINVAL;
- }
- totlen = vmbus_pkt_hdr_set_offsets(&hdr, desclen, msglen);
- hdr.type = pkt_type;
- hdr.flags = need_comp ? VMBUS_PACKET_FLAG_REQUEST_COMPLETION : 0;
- hdr.transaction_id = transaction_id;
- assert(totlen <= ringbuf->reserved);
- ringbuf_start_io(&ringbuf->common);
- ringbuf_seek(&ringbuf->common, ringbuf->wr_idx);
- ret = ringbuf_io(&ringbuf->common, &hdr, sizeof(hdr));
- if (ret < 0) {
- goto out;
- }
- if (desclen) {
- assert(desc);
- ret = ringbuf_io(&ringbuf->common, desc, desclen);
- if (ret < 0) {
- goto out;
- }
- ringbuf_seek(&ringbuf->common,
- ringbuf->wr_idx + hdr.offset_qwords * sizeof(uint64_t));
- }
- ret = ringbuf_io(&ringbuf->common, msg, msglen);
- if (ret < 0) {
- goto out;
- }
- ringbuf_seek(&ringbuf->common, ringbuf->wr_idx + totlen);
- ringbuf->wr_idx = ringbuf_tell(&ringbuf->common);
- ret = 0;
- out:
- ringbuf_end_io(&ringbuf->common);
- if (ret) {
- return ret;
- }
- return ringbuf_send_update_idx(chan);
- }
- ssize_t vmbus_channel_send_completion(VMBusChanReq *req,
- void *msg, uint32_t msglen)
- {
- assert(req->need_comp);
- return vmbus_channel_send(req->chan, VMBUS_PACKET_COMP, NULL, 0,
- msg, msglen, false, req->transaction_id);
- }
- static int sgl_from_gpa_ranges(QEMUSGList *sgl, VMBusDevice *dev,
- VMBusRingBufCommon *ringbuf, uint32_t len)
- {
- int ret;
- vmbus_pkt_gpa_direct hdr;
- hwaddr curaddr = 0;
- hwaddr curlen = 0;
- int num;
- if (len < sizeof(hdr)) {
- return -EIO;
- }
- ret = ringbuf_io(ringbuf, &hdr, sizeof(hdr));
- if (ret < 0) {
- return ret;
- }
- len -= sizeof(hdr);
- num = (len - hdr.rangecount * sizeof(vmbus_gpa_range)) / sizeof(uint64_t);
- if (num < 0) {
- return -EIO;
- }
- qemu_sglist_init(sgl, DEVICE(dev), num, ringbuf->as);
- for (; hdr.rangecount; hdr.rangecount--) {
- vmbus_gpa_range range;
- if (len < sizeof(range)) {
- goto eio;
- }
- ret = ringbuf_io(ringbuf, &range, sizeof(range));
- if (ret < 0) {
- goto err;
- }
- len -= sizeof(range);
- if (range.byte_offset & TARGET_PAGE_MASK) {
- goto eio;
- }
- for (; range.byte_count; range.byte_offset = 0) {
- uint64_t paddr;
- uint32_t plen = MIN(range.byte_count,
- TARGET_PAGE_SIZE - range.byte_offset);
- if (len < sizeof(uint64_t)) {
- goto eio;
- }
- ret = ringbuf_io(ringbuf, &paddr, sizeof(paddr));
- if (ret < 0) {
- goto err;
- }
- len -= sizeof(uint64_t);
- paddr <<= TARGET_PAGE_BITS;
- paddr |= range.byte_offset;
- range.byte_count -= plen;
- if (curaddr + curlen == paddr) {
- /* consecutive fragments - join */
- curlen += plen;
- } else {
- if (curlen) {
- qemu_sglist_add(sgl, curaddr, curlen);
- }
- curaddr = paddr;
- curlen = plen;
- }
- }
- }
- if (curlen) {
- qemu_sglist_add(sgl, curaddr, curlen);
- }
- return 0;
- eio:
- ret = -EIO;
- err:
- qemu_sglist_destroy(sgl);
- return ret;
- }
- static VMBusChanReq *vmbus_alloc_req(VMBusChannel *chan,
- uint32_t size, uint16_t pkt_type,
- uint32_t msglen, uint64_t transaction_id,
- bool need_comp)
- {
- VMBusChanReq *req;
- uint32_t msgoff = QEMU_ALIGN_UP(size, __alignof__(*req->msg));
- uint32_t totlen = msgoff + msglen;
- req = g_malloc0(totlen);
- req->chan = chan;
- req->pkt_type = pkt_type;
- req->msg = (void *)req + msgoff;
- req->msglen = msglen;
- req->transaction_id = transaction_id;
- req->need_comp = need_comp;
- return req;
- }
- int vmbus_channel_recv_start(VMBusChannel *chan)
- {
- VMBusRecvRingBuf *ringbuf = &chan->recv_ringbuf;
- vmbus_ring_buffer *rb;
- rb = ringbuf_map_hdr(&ringbuf->common);
- if (!rb) {
- return -EFAULT;
- }
- ringbuf->last_seen_wr_idx = rb->write_index;
- ringbuf_unmap_hdr(&ringbuf->common, rb, false);
- if (ringbuf->last_seen_wr_idx >= ringbuf->common.len) {
- return -EOVERFLOW;
- }
- /* prevent reorder of the following data operation with write_index read */
- smp_mb(); /* barrier pair [C] */
- return 0;
- }
- void *vmbus_channel_recv_peek(VMBusChannel *chan, uint32_t size)
- {
- VMBusRecvRingBuf *ringbuf = &chan->recv_ringbuf;
- vmbus_packet_hdr hdr = {};
- VMBusChanReq *req;
- uint32_t avail;
- uint32_t totlen, pktlen, msglen, msgoff, desclen;
- assert(size >= sizeof(*req));
- /* safe as last_seen_wr_idx is validated in vmbus_channel_recv_start */
- avail = rb_idx_delta(ringbuf->rd_idx, ringbuf->last_seen_wr_idx,
- ringbuf->common.len, true);
- if (avail < sizeof(hdr)) {
- return NULL;
- }
- ringbuf_seek(&ringbuf->common, ringbuf->rd_idx);
- if (ringbuf_io(&ringbuf->common, &hdr, sizeof(hdr)) < 0) {
- return NULL;
- }
- pktlen = hdr.len_qwords * sizeof(uint64_t);
- totlen = pktlen + VMBUS_PKT_TRAILER;
- if (totlen > avail) {
- return NULL;
- }
- msgoff = hdr.offset_qwords * sizeof(uint64_t);
- if (msgoff > pktlen || msgoff < sizeof(hdr)) {
- error_report("%s: malformed packet: %u %u", __func__, msgoff, pktlen);
- return NULL;
- }
- msglen = pktlen - msgoff;
- req = vmbus_alloc_req(chan, size, hdr.type, msglen, hdr.transaction_id,
- hdr.flags & VMBUS_PACKET_FLAG_REQUEST_COMPLETION);
- switch (hdr.type) {
- case VMBUS_PACKET_DATA_USING_GPA_DIRECT:
- desclen = msgoff - sizeof(hdr);
- if (sgl_from_gpa_ranges(&req->sgl, chan->dev, &ringbuf->common,
- desclen) < 0) {
- error_report("%s: failed to convert GPA ranges to SGL", __func__);
- goto free_req;
- }
- break;
- case VMBUS_PACKET_DATA_INBAND:
- case VMBUS_PACKET_COMP:
- break;
- default:
- error_report("%s: unexpected msg type: %x", __func__, hdr.type);
- goto free_req;
- }
- ringbuf_seek(&ringbuf->common, ringbuf->rd_idx + msgoff);
- if (ringbuf_io(&ringbuf->common, req->msg, msglen) < 0) {
- goto free_req;
- }
- ringbuf_seek(&ringbuf->common, ringbuf->rd_idx + totlen);
- return req;
- free_req:
- vmbus_free_req(req);
- return NULL;
- }
- void vmbus_channel_recv_pop(VMBusChannel *chan)
- {
- VMBusRecvRingBuf *ringbuf = &chan->recv_ringbuf;
- ringbuf->rd_idx = ringbuf_tell(&ringbuf->common);
- }
- ssize_t vmbus_channel_recv_done(VMBusChannel *chan)
- {
- VMBusRecvRingBuf *ringbuf = &chan->recv_ringbuf;
- vmbus_ring_buffer *rb;
- uint32_t read;
- read = rb_idx_delta(ringbuf->last_rd_idx, ringbuf->rd_idx,
- ringbuf->common.len, true);
- if (!read) {
- return 0;
- }
- rb = ringbuf_map_hdr(&ringbuf->common);
- if (!rb) {
- return -EFAULT;
- }
- /* prevent reorder with the data operation and packet read */
- smp_mb(); /* barrier pair [B] */
- rb->read_index = ringbuf->rd_idx;
- /* prevent reorder of the following pending_send_sz read */
- smp_mb(); /* barrier pair [A] */
- if (rb->interrupt_mask) {
- goto out;
- }
- if (rb->feature_bits & VMBUS_RING_BUFFER_FEAT_PENDING_SZ) {
- uint32_t wr_idx, wr_avail;
- uint32_t wanted = rb->pending_send_sz;
- if (!wanted) {
- goto out;
- }
- /* prevent reorder with pending_send_sz read */
- smp_rmb(); /* barrier pair [D] */
- wr_idx = rb->write_index;
- wr_avail = rb_idx_delta(wr_idx, ringbuf->rd_idx, ringbuf->common.len,
- true);
- /* the producer wasn't blocked on the consumer state */
- if (wr_avail >= read + wanted) {
- goto out;
- }
- /* there's not enough space for the producer to make progress */
- if (wr_avail < wanted) {
- goto out;
- }
- }
- vmbus_channel_notify_guest(chan);
- out:
- ringbuf_unmap_hdr(&ringbuf->common, rb, true);
- ringbuf->last_rd_idx = ringbuf->rd_idx;
- return read;
- }
- void vmbus_free_req(void *req)
- {
- VMBusChanReq *r = req;
- if (!req) {
- return;
- }
- if (r->sgl.dev) {
- qemu_sglist_destroy(&r->sgl);
- }
- g_free(req);
- }
- static void channel_event_cb(EventNotifier *e)
- {
- VMBusChannel *chan = container_of(e, VMBusChannel, notifier);
- if (event_notifier_test_and_clear(e)) {
- /*
- * All receives are supposed to happen within the device worker, so
- * bracket it with ringbuf_start/end_io on the receive ringbuffer, and
- * potentially reuse the cached mapping throughout the worker.
- * Can't do this for sends as they may happen outside the device
- * worker.
- */
- VMBusRecvRingBuf *ringbuf = &chan->recv_ringbuf;
- ringbuf_start_io(&ringbuf->common);
- chan->notify_cb(chan);
- ringbuf_end_io(&ringbuf->common);
- }
- }
- static int alloc_chan_id(VMBus *vmbus)
- {
- int ret;
- ret = find_next_zero_bit(vmbus->chanid_bitmap, VMBUS_CHANID_COUNT, 0);
- if (ret == VMBUS_CHANID_COUNT) {
- return -ENOMEM;
- }
- return ret + VMBUS_FIRST_CHANID;
- }
- static int register_chan_id(VMBusChannel *chan)
- {
- return test_and_set_bit(chan->id - VMBUS_FIRST_CHANID,
- chan->vmbus->chanid_bitmap) ? -EEXIST : 0;
- }
- static void unregister_chan_id(VMBusChannel *chan)
- {
- clear_bit(chan->id - VMBUS_FIRST_CHANID, chan->vmbus->chanid_bitmap);
- }
- static uint32_t chan_connection_id(VMBusChannel *chan)
- {
- return VMBUS_CHAN_CONNECTION_OFFSET + chan->id;
- }
- static void init_channel(VMBus *vmbus, VMBusDevice *dev, VMBusDeviceClass *vdc,
- VMBusChannel *chan, uint16_t idx, Error **errp)
- {
- int res;
- chan->dev = dev;
- chan->notify_cb = vdc->chan_notify_cb;
- chan->subchan_idx = idx;
- chan->vmbus = vmbus;
- res = alloc_chan_id(vmbus);
- if (res < 0) {
- error_setg(errp, "no spare channel id");
- return;
- }
- chan->id = res;
- register_chan_id(chan);
- /*
- * The guest drivers depend on the device subchannels (idx #1+) to be
- * offered after the primary channel (idx #0) of that device. To ensure
- * that, record the channels on the channel list in the order they appear
- * within the device.
- */
- QTAILQ_INSERT_TAIL(&vmbus->channel_list, chan, link);
- }
- static void deinit_channel(VMBusChannel *chan)
- {
- assert(chan->state == VMCHAN_INIT);
- QTAILQ_REMOVE(&chan->vmbus->channel_list, chan, link);
- unregister_chan_id(chan);
- }
- static void create_channels(VMBus *vmbus, VMBusDevice *dev, Error **errp)
- {
- uint16_t i;
- VMBusDeviceClass *vdc = VMBUS_DEVICE_GET_CLASS(dev);
- Error *err = NULL;
- dev->num_channels = vdc->num_channels ? vdc->num_channels(dev) : 1;
- if (dev->num_channels < 1) {
- error_setg(errp, "invalid #channels: %u", dev->num_channels);
- return;
- }
- dev->channels = g_new0(VMBusChannel, dev->num_channels);
- for (i = 0; i < dev->num_channels; i++) {
- init_channel(vmbus, dev, vdc, &dev->channels[i], i, &err);
- if (err) {
- goto err_init;
- }
- }
- return;
- err_init:
- while (i--) {
- deinit_channel(&dev->channels[i]);
- }
- error_propagate(errp, err);
- }
- static void free_channels(VMBusDevice *dev)
- {
- uint16_t i;
- for (i = 0; i < dev->num_channels; i++) {
- deinit_channel(&dev->channels[i]);
- }
- g_free(dev->channels);
- }
- static HvSintRoute *make_sint_route(VMBus *vmbus, uint32_t vp_index)
- {
- VMBusChannel *chan;
- if (vp_index == vmbus->target_vp) {
- hyperv_sint_route_ref(vmbus->sint_route);
- return vmbus->sint_route;
- }
- QTAILQ_FOREACH(chan, &vmbus->channel_list, link) {
- if (chan->target_vp == vp_index && vmbus_channel_is_open(chan)) {
- hyperv_sint_route_ref(chan->notify_route);
- return chan->notify_route;
- }
- }
- return hyperv_sint_route_new(vp_index, VMBUS_SINT, NULL, NULL);
- }
- static void open_channel(VMBusChannel *chan)
- {
- VMBusDeviceClass *vdc = VMBUS_DEVICE_GET_CLASS(chan->dev);
- chan->gpadl = vmbus_get_gpadl(chan, chan->ringbuf_gpadl);
- if (!chan->gpadl) {
- return;
- }
- if (ringbufs_init(chan)) {
- goto put_gpadl;
- }
- if (event_notifier_init(&chan->notifier, 0)) {
- goto put_gpadl;
- }
- event_notifier_set_handler(&chan->notifier, channel_event_cb);
- if (hyperv_set_event_flag_handler(chan_connection_id(chan),
- &chan->notifier)) {
- goto cleanup_notifier;
- }
- chan->notify_route = make_sint_route(chan->vmbus, chan->target_vp);
- if (!chan->notify_route) {
- goto clear_event_flag_handler;
- }
- if (vdc->open_channel && vdc->open_channel(chan)) {
- goto unref_sint_route;
- }
- chan->is_open = true;
- return;
- unref_sint_route:
- hyperv_sint_route_unref(chan->notify_route);
- clear_event_flag_handler:
- hyperv_set_event_flag_handler(chan_connection_id(chan), NULL);
- cleanup_notifier:
- event_notifier_set_handler(&chan->notifier, NULL);
- event_notifier_cleanup(&chan->notifier);
- put_gpadl:
- vmbus_put_gpadl(chan->gpadl);
- }
- static void close_channel(VMBusChannel *chan)
- {
- VMBusDeviceClass *vdc = VMBUS_DEVICE_GET_CLASS(chan->dev);
- if (!chan->is_open) {
- return;
- }
- if (vdc->close_channel) {
- vdc->close_channel(chan);
- }
- hyperv_sint_route_unref(chan->notify_route);
- hyperv_set_event_flag_handler(chan_connection_id(chan), NULL);
- event_notifier_set_handler(&chan->notifier, NULL);
- event_notifier_cleanup(&chan->notifier);
- vmbus_put_gpadl(chan->gpadl);
- chan->is_open = false;
- }
- static int channel_post_load(void *opaque, int version_id)
- {
- VMBusChannel *chan = opaque;
- return register_chan_id(chan);
- }
- static const VMStateDescription vmstate_channel = {
- .name = "vmbus/channel",
- .version_id = 0,
- .minimum_version_id = 0,
- .post_load = channel_post_load,
- .fields = (const VMStateField[]) {
- VMSTATE_UINT32(id, VMBusChannel),
- VMSTATE_UINT16(subchan_idx, VMBusChannel),
- VMSTATE_UINT32(open_id, VMBusChannel),
- VMSTATE_UINT32(target_vp, VMBusChannel),
- VMSTATE_UINT32(ringbuf_gpadl, VMBusChannel),
- VMSTATE_UINT32(ringbuf_send_offset, VMBusChannel),
- VMSTATE_UINT8(offer_state, VMBusChannel),
- VMSTATE_UINT8(state, VMBusChannel),
- VMSTATE_END_OF_LIST()
- }
- };
- static VMBusChannel *find_channel(VMBus *vmbus, uint32_t id)
- {
- VMBusChannel *chan;
- QTAILQ_FOREACH(chan, &vmbus->channel_list, link) {
- if (chan->id == id) {
- return chan;
- }
- }
- return NULL;
- }
- static int enqueue_incoming_message(VMBus *vmbus,
- const struct hyperv_post_message_input *msg)
- {
- int ret = 0;
- uint8_t idx, prev_size;
- qemu_mutex_lock(&vmbus->rx_queue_lock);
- if (vmbus->rx_queue_size == HV_MSG_QUEUE_LEN) {
- ret = -ENOBUFS;
- goto out;
- }
- prev_size = vmbus->rx_queue_size;
- idx = (vmbus->rx_queue_head + vmbus->rx_queue_size) % HV_MSG_QUEUE_LEN;
- memcpy(&vmbus->rx_queue[idx], msg, sizeof(*msg));
- vmbus->rx_queue_size++;
- /* only need to resched if the queue was empty before */
- if (!prev_size) {
- vmbus_resched(vmbus);
- }
- out:
- qemu_mutex_unlock(&vmbus->rx_queue_lock);
- return ret;
- }
- static uint16_t vmbus_recv_message(const struct hyperv_post_message_input *msg,
- void *data)
- {
- VMBus *vmbus = data;
- struct vmbus_message_header *vmbus_msg;
- if (msg->message_type != HV_MESSAGE_VMBUS) {
- return HV_STATUS_INVALID_HYPERCALL_INPUT;
- }
- if (msg->payload_size < sizeof(struct vmbus_message_header)) {
- return HV_STATUS_INVALID_HYPERCALL_INPUT;
- }
- vmbus_msg = (struct vmbus_message_header *)msg->payload;
- trace_vmbus_recv_message(vmbus_msg->message_type, msg->payload_size);
- if (vmbus_msg->message_type == VMBUS_MSG_INVALID ||
- vmbus_msg->message_type >= VMBUS_MSG_COUNT) {
- error_report("vmbus: unknown message type %#x",
- vmbus_msg->message_type);
- return HV_STATUS_INVALID_HYPERCALL_INPUT;
- }
- if (enqueue_incoming_message(vmbus, msg)) {
- return HV_STATUS_INSUFFICIENT_BUFFERS;
- }
- return HV_STATUS_SUCCESS;
- }
- static bool vmbus_initialized(VMBus *vmbus)
- {
- return vmbus->version > 0 && vmbus->version <= VMBUS_VERSION_CURRENT;
- }
- static void vmbus_reset_all(VMBus *vmbus)
- {
- bus_cold_reset(BUS(vmbus));
- }
- static void post_msg(VMBus *vmbus, void *msgdata, uint32_t msglen)
- {
- int ret;
- struct hyperv_message msg = {
- .header.message_type = HV_MESSAGE_VMBUS,
- };
- assert(!vmbus->msg_in_progress);
- assert(msglen <= sizeof(msg.payload));
- assert(msglen >= sizeof(struct vmbus_message_header));
- vmbus->msg_in_progress = true;
- trace_vmbus_post_msg(((struct vmbus_message_header *)msgdata)->message_type,
- msglen);
- memcpy(msg.payload, msgdata, msglen);
- msg.header.payload_size = ROUND_UP(msglen, VMBUS_MESSAGE_SIZE_ALIGN);
- ret = hyperv_post_msg(vmbus->sint_route, &msg);
- if (ret == 0 || ret == -EAGAIN) {
- return;
- }
- error_report("message delivery fatal failure: %d; aborting vmbus", ret);
- vmbus_reset_all(vmbus);
- }
- static int vmbus_init(VMBus *vmbus)
- {
- if (vmbus->target_vp != (uint32_t)-1) {
- vmbus->sint_route = hyperv_sint_route_new(vmbus->target_vp, VMBUS_SINT,
- vmbus_msg_cb, vmbus);
- if (!vmbus->sint_route) {
- error_report("failed to set up SINT route");
- return -ENOMEM;
- }
- }
- return 0;
- }
- static void vmbus_deinit(VMBus *vmbus)
- {
- VMBusGpadl *gpadl, *tmp_gpadl;
- VMBusChannel *chan;
- QTAILQ_FOREACH_SAFE(gpadl, &vmbus->gpadl_list, link, tmp_gpadl) {
- if (gpadl->state == VMGPADL_TORNDOWN) {
- continue;
- }
- vmbus_put_gpadl(gpadl);
- }
- QTAILQ_FOREACH(chan, &vmbus->channel_list, link) {
- chan->offer_state = VMOFFER_INIT;
- }
- hyperv_sint_route_unref(vmbus->sint_route);
- vmbus->sint_route = NULL;
- vmbus->int_page_gpa = 0;
- vmbus->target_vp = (uint32_t)-1;
- vmbus->version = 0;
- vmbus->state = VMBUS_LISTEN;
- vmbus->msg_in_progress = false;
- }
- static void handle_initiate_contact(VMBus *vmbus,
- vmbus_message_initiate_contact *msg,
- uint32_t msglen)
- {
- if (msglen < sizeof(*msg)) {
- return;
- }
- trace_vmbus_initiate_contact(msg->version_requested >> 16,
- msg->version_requested & 0xffff,
- msg->target_vcpu, msg->monitor_page1,
- msg->monitor_page2, msg->interrupt_page);
- /*
- * Reset vmbus on INITIATE_CONTACT regardless of its previous state.
- * Useful, in particular, with vmbus-aware BIOS which can't shut vmbus down
- * before handing over to OS loader.
- */
- vmbus_reset_all(vmbus);
- vmbus->target_vp = msg->target_vcpu;
- vmbus->version = msg->version_requested;
- if (vmbus->version < VMBUS_VERSION_WIN8) {
- /* linux passes interrupt page even when it doesn't need it */
- vmbus->int_page_gpa = msg->interrupt_page;
- }
- vmbus->state = VMBUS_HANDSHAKE;
- if (vmbus_init(vmbus)) {
- error_report("failed to init vmbus; aborting");
- vmbus_deinit(vmbus);
- return;
- }
- }
- static void send_handshake(VMBus *vmbus)
- {
- struct vmbus_message_version_response msg = {
- .header.message_type = VMBUS_MSG_VERSION_RESPONSE,
- .version_supported = vmbus_initialized(vmbus),
- };
- post_msg(vmbus, &msg, sizeof(msg));
- }
- static void handle_request_offers(VMBus *vmbus, void *msgdata, uint32_t msglen)
- {
- VMBusChannel *chan;
- if (!vmbus_initialized(vmbus)) {
- return;
- }
- QTAILQ_FOREACH(chan, &vmbus->channel_list, link) {
- if (chan->offer_state == VMOFFER_INIT) {
- chan->offer_state = VMOFFER_SENDING;
- break;
- }
- }
- vmbus->state = VMBUS_OFFER;
- }
- static void send_offer(VMBus *vmbus)
- {
- VMBusChannel *chan;
- struct vmbus_message_header alloffers_msg = {
- .message_type = VMBUS_MSG_ALLOFFERS_DELIVERED,
- };
- QTAILQ_FOREACH(chan, &vmbus->channel_list, link) {
- if (chan->offer_state == VMOFFER_SENDING) {
- VMBusDeviceClass *vdc = VMBUS_DEVICE_GET_CLASS(chan->dev);
- /* Hyper-V wants LE GUIDs */
- QemuUUID classid = qemu_uuid_bswap(vdc->classid);
- QemuUUID instanceid = qemu_uuid_bswap(chan->dev->instanceid);
- struct vmbus_message_offer_channel msg = {
- .header.message_type = VMBUS_MSG_OFFERCHANNEL,
- .child_relid = chan->id,
- .connection_id = chan_connection_id(chan),
- .channel_flags = vdc->channel_flags,
- .mmio_size_mb = vdc->mmio_size_mb,
- .sub_channel_index = vmbus_channel_idx(chan),
- .interrupt_flags = VMBUS_OFFER_INTERRUPT_DEDICATED,
- };
- memcpy(msg.type_uuid, &classid, sizeof(classid));
- memcpy(msg.instance_uuid, &instanceid, sizeof(instanceid));
- trace_vmbus_send_offer(chan->id, chan->dev);
- post_msg(vmbus, &msg, sizeof(msg));
- return;
- }
- }
- /* no more offers, send terminator message */
- trace_vmbus_terminate_offers();
- post_msg(vmbus, &alloffers_msg, sizeof(alloffers_msg));
- }
- static bool complete_offer(VMBus *vmbus)
- {
- VMBusChannel *chan;
- QTAILQ_FOREACH(chan, &vmbus->channel_list, link) {
- if (chan->offer_state == VMOFFER_SENDING) {
- chan->offer_state = VMOFFER_SENT;
- goto next_offer;
- }
- }
- /*
- * no transitioning channels found so this is completing the terminator
- * message, and vmbus can move to the next state
- */
- return true;
- next_offer:
- /* try to mark another channel for offering */
- QTAILQ_FOREACH(chan, &vmbus->channel_list, link) {
- if (chan->offer_state == VMOFFER_INIT) {
- chan->offer_state = VMOFFER_SENDING;
- break;
- }
- }
- /*
- * if an offer has been sent there are more offers or the terminator yet to
- * send, so no state transition for vmbus
- */
- return false;
- }
- static void handle_gpadl_header(VMBus *vmbus, vmbus_message_gpadl_header *msg,
- uint32_t msglen)
- {
- VMBusGpadl *gpadl;
- uint32_t num_gfns, i;
- /* must include at least one gpa range */
- if (msglen < sizeof(*msg) + sizeof(msg->range[0]) ||
- !vmbus_initialized(vmbus)) {
- return;
- }
- num_gfns = (msg->range_buflen - msg->rangecount * sizeof(msg->range[0])) /
- sizeof(msg->range[0].pfn_array[0]);
- trace_vmbus_gpadl_header(msg->gpadl_id, num_gfns);
- /*
- * In theory the GPADL_HEADER message can define a GPADL with multiple GPA
- * ranges each with arbitrary size and alignment. However in practice only
- * single-range page-aligned GPADLs have been observed so just ignore
- * anything else and simplify things greatly.
- */
- if (msg->rangecount != 1 || msg->range[0].byte_offset ||
- (msg->range[0].byte_count != (num_gfns << TARGET_PAGE_BITS))) {
- return;
- }
- /* ignore requests to create already existing GPADLs */
- if (find_gpadl(vmbus, msg->gpadl_id)) {
- return;
- }
- gpadl = create_gpadl(vmbus, msg->gpadl_id, msg->child_relid, num_gfns);
- for (i = 0; i < num_gfns &&
- (void *)&msg->range[0].pfn_array[i + 1] <= (void *)msg + msglen;
- i++) {
- gpadl->gfns[gpadl->seen_gfns++] = msg->range[0].pfn_array[i];
- }
- if (gpadl_full(gpadl)) {
- vmbus->state = VMBUS_CREATE_GPADL;
- }
- }
- static void handle_gpadl_body(VMBus *vmbus, vmbus_message_gpadl_body *msg,
- uint32_t msglen)
- {
- VMBusGpadl *gpadl;
- uint32_t num_gfns_left, i;
- if (msglen < sizeof(*msg) || !vmbus_initialized(vmbus)) {
- return;
- }
- trace_vmbus_gpadl_body(msg->gpadl_id);
- gpadl = find_gpadl(vmbus, msg->gpadl_id);
- if (!gpadl) {
- return;
- }
- num_gfns_left = gpadl->num_gfns - gpadl->seen_gfns;
- assert(num_gfns_left);
- for (i = 0; i < num_gfns_left &&
- (void *)&msg->pfn_array[i + 1] <= (void *)msg + msglen; i++) {
- gpadl->gfns[gpadl->seen_gfns++] = msg->pfn_array[i];
- }
- if (gpadl_full(gpadl)) {
- vmbus->state = VMBUS_CREATE_GPADL;
- }
- }
- static void send_create_gpadl(VMBus *vmbus)
- {
- VMBusGpadl *gpadl;
- QTAILQ_FOREACH(gpadl, &vmbus->gpadl_list, link) {
- if (gpadl_full(gpadl) && gpadl->state == VMGPADL_INIT) {
- struct vmbus_message_gpadl_created msg = {
- .header.message_type = VMBUS_MSG_GPADL_CREATED,
- .gpadl_id = gpadl->id,
- .child_relid = gpadl->child_relid,
- };
- trace_vmbus_gpadl_created(gpadl->id);
- post_msg(vmbus, &msg, sizeof(msg));
- return;
- }
- }
- g_assert_not_reached();
- }
- static bool complete_create_gpadl(VMBus *vmbus)
- {
- VMBusGpadl *gpadl;
- QTAILQ_FOREACH(gpadl, &vmbus->gpadl_list, link) {
- if (gpadl_full(gpadl) && gpadl->state == VMGPADL_INIT) {
- gpadl->state = VMGPADL_ALIVE;
- return true;
- }
- }
- g_assert_not_reached();
- }
- static void handle_gpadl_teardown(VMBus *vmbus,
- vmbus_message_gpadl_teardown *msg,
- uint32_t msglen)
- {
- VMBusGpadl *gpadl;
- if (msglen < sizeof(*msg) || !vmbus_initialized(vmbus)) {
- return;
- }
- trace_vmbus_gpadl_teardown(msg->gpadl_id);
- gpadl = find_gpadl(vmbus, msg->gpadl_id);
- if (!gpadl || gpadl->state == VMGPADL_TORNDOWN) {
- return;
- }
- gpadl->state = VMGPADL_TEARINGDOWN;
- vmbus->state = VMBUS_TEARDOWN_GPADL;
- }
- static void send_teardown_gpadl(VMBus *vmbus)
- {
- VMBusGpadl *gpadl;
- QTAILQ_FOREACH(gpadl, &vmbus->gpadl_list, link) {
- if (gpadl->state == VMGPADL_TEARINGDOWN) {
- struct vmbus_message_gpadl_torndown msg = {
- .header.message_type = VMBUS_MSG_GPADL_TORNDOWN,
- .gpadl_id = gpadl->id,
- };
- trace_vmbus_gpadl_torndown(gpadl->id);
- post_msg(vmbus, &msg, sizeof(msg));
- return;
- }
- }
- g_assert_not_reached();
- }
- static bool complete_teardown_gpadl(VMBus *vmbus)
- {
- VMBusGpadl *gpadl;
- QTAILQ_FOREACH(gpadl, &vmbus->gpadl_list, link) {
- if (gpadl->state == VMGPADL_TEARINGDOWN) {
- gpadl->state = VMGPADL_TORNDOWN;
- vmbus_put_gpadl(gpadl);
- return true;
- }
- }
- g_assert_not_reached();
- }
- static void handle_open_channel(VMBus *vmbus, vmbus_message_open_channel *msg,
- uint32_t msglen)
- {
- VMBusChannel *chan;
- if (msglen < sizeof(*msg) || !vmbus_initialized(vmbus)) {
- return;
- }
- trace_vmbus_open_channel(msg->child_relid, msg->ring_buffer_gpadl_id,
- msg->target_vp);
- chan = find_channel(vmbus, msg->child_relid);
- if (!chan || chan->state != VMCHAN_INIT) {
- return;
- }
- chan->ringbuf_gpadl = msg->ring_buffer_gpadl_id;
- chan->ringbuf_send_offset = msg->ring_buffer_offset;
- chan->target_vp = msg->target_vp;
- chan->open_id = msg->open_id;
- open_channel(chan);
- chan->state = VMCHAN_OPENING;
- vmbus->state = VMBUS_OPEN_CHANNEL;
- }
- static void send_open_channel(VMBus *vmbus)
- {
- VMBusChannel *chan;
- QTAILQ_FOREACH(chan, &vmbus->channel_list, link) {
- if (chan->state == VMCHAN_OPENING) {
- struct vmbus_message_open_result msg = {
- .header.message_type = VMBUS_MSG_OPENCHANNEL_RESULT,
- .child_relid = chan->id,
- .open_id = chan->open_id,
- .status = !vmbus_channel_is_open(chan),
- };
- trace_vmbus_channel_open(chan->id, msg.status);
- post_msg(vmbus, &msg, sizeof(msg));
- return;
- }
- }
- g_assert_not_reached();
- }
- static bool complete_open_channel(VMBus *vmbus)
- {
- VMBusChannel *chan;
- QTAILQ_FOREACH(chan, &vmbus->channel_list, link) {
- if (chan->state == VMCHAN_OPENING) {
- if (vmbus_channel_is_open(chan)) {
- chan->state = VMCHAN_OPEN;
- /*
- * simulate guest notification of ringbuffer space made
- * available, for the channel protocols where the host
- * initiates the communication
- */
- vmbus_channel_notify_host(chan);
- } else {
- chan->state = VMCHAN_INIT;
- }
- return true;
- }
- }
- g_assert_not_reached();
- }
- static void vdev_reset_on_close(VMBusDevice *vdev)
- {
- uint16_t i;
- for (i = 0; i < vdev->num_channels; i++) {
- if (vmbus_channel_is_open(&vdev->channels[i])) {
- return;
- }
- }
- /* all channels closed -- reset device */
- device_cold_reset(DEVICE(vdev));
- }
- static void handle_close_channel(VMBus *vmbus, vmbus_message_close_channel *msg,
- uint32_t msglen)
- {
- VMBusChannel *chan;
- if (msglen < sizeof(*msg) || !vmbus_initialized(vmbus)) {
- return;
- }
- trace_vmbus_close_channel(msg->child_relid);
- chan = find_channel(vmbus, msg->child_relid);
- if (!chan) {
- return;
- }
- close_channel(chan);
- chan->state = VMCHAN_INIT;
- vdev_reset_on_close(chan->dev);
- }
- static void handle_unload(VMBus *vmbus, void *msg, uint32_t msglen)
- {
- vmbus->state = VMBUS_UNLOAD;
- }
- static void send_unload(VMBus *vmbus)
- {
- vmbus_message_header msg = {
- .message_type = VMBUS_MSG_UNLOAD_RESPONSE,
- };
- qemu_mutex_lock(&vmbus->rx_queue_lock);
- vmbus->rx_queue_size = 0;
- qemu_mutex_unlock(&vmbus->rx_queue_lock);
- post_msg(vmbus, &msg, sizeof(msg));
- return;
- }
- static bool complete_unload(VMBus *vmbus)
- {
- vmbus_reset_all(vmbus);
- return true;
- }
- static void process_message(VMBus *vmbus)
- {
- struct hyperv_post_message_input *hv_msg;
- struct vmbus_message_header *msg;
- void *msgdata;
- uint32_t msglen;
- qemu_mutex_lock(&vmbus->rx_queue_lock);
- if (!vmbus->rx_queue_size) {
- goto unlock;
- }
- hv_msg = &vmbus->rx_queue[vmbus->rx_queue_head];
- msglen = hv_msg->payload_size;
- if (msglen < sizeof(*msg)) {
- goto out;
- }
- msgdata = hv_msg->payload;
- msg = msgdata;
- trace_vmbus_process_incoming_message(msg->message_type);
- switch (msg->message_type) {
- case VMBUS_MSG_INITIATE_CONTACT:
- handle_initiate_contact(vmbus, msgdata, msglen);
- break;
- case VMBUS_MSG_REQUESTOFFERS:
- handle_request_offers(vmbus, msgdata, msglen);
- break;
- case VMBUS_MSG_GPADL_HEADER:
- handle_gpadl_header(vmbus, msgdata, msglen);
- break;
- case VMBUS_MSG_GPADL_BODY:
- handle_gpadl_body(vmbus, msgdata, msglen);
- break;
- case VMBUS_MSG_GPADL_TEARDOWN:
- handle_gpadl_teardown(vmbus, msgdata, msglen);
- break;
- case VMBUS_MSG_OPENCHANNEL:
- handle_open_channel(vmbus, msgdata, msglen);
- break;
- case VMBUS_MSG_CLOSECHANNEL:
- handle_close_channel(vmbus, msgdata, msglen);
- break;
- case VMBUS_MSG_UNLOAD:
- handle_unload(vmbus, msgdata, msglen);
- break;
- default:
- error_report("unknown message type %#x", msg->message_type);
- break;
- }
- out:
- vmbus->rx_queue_size--;
- vmbus->rx_queue_head++;
- vmbus->rx_queue_head %= HV_MSG_QUEUE_LEN;
- vmbus_resched(vmbus);
- unlock:
- qemu_mutex_unlock(&vmbus->rx_queue_lock);
- }
- static const struct {
- void (*run)(VMBus *vmbus);
- bool (*complete)(VMBus *vmbus);
- } state_runner[] = {
- [VMBUS_LISTEN] = {process_message, NULL},
- [VMBUS_HANDSHAKE] = {send_handshake, NULL},
- [VMBUS_OFFER] = {send_offer, complete_offer},
- [VMBUS_CREATE_GPADL] = {send_create_gpadl, complete_create_gpadl},
- [VMBUS_TEARDOWN_GPADL] = {send_teardown_gpadl, complete_teardown_gpadl},
- [VMBUS_OPEN_CHANNEL] = {send_open_channel, complete_open_channel},
- [VMBUS_UNLOAD] = {send_unload, complete_unload},
- };
- static void vmbus_do_run(VMBus *vmbus)
- {
- if (vmbus->msg_in_progress) {
- return;
- }
- assert(vmbus->state < VMBUS_STATE_MAX);
- assert(state_runner[vmbus->state].run);
- state_runner[vmbus->state].run(vmbus);
- }
- static void vmbus_run(void *opaque)
- {
- VMBus *vmbus = opaque;
- /* make sure no recursion happens (e.g. due to recursive aio_poll()) */
- if (vmbus->in_progress) {
- return;
- }
- vmbus->in_progress = true;
- /*
- * FIXME: if vmbus_resched() is called from within vmbus_do_run(), it
- * should go *after* the code that can result in aio_poll; otherwise
- * reschedules can be missed. No idea how to enforce that.
- */
- vmbus_do_run(vmbus);
- vmbus->in_progress = false;
- }
- static void vmbus_msg_cb(void *data, int status)
- {
- VMBus *vmbus = data;
- bool (*complete)(VMBus *vmbus);
- assert(vmbus->msg_in_progress);
- trace_vmbus_msg_cb(status);
- if (status == -EAGAIN) {
- goto out;
- }
- if (status) {
- error_report("message delivery fatal failure: %d; aborting vmbus",
- status);
- vmbus_reset_all(vmbus);
- return;
- }
- assert(vmbus->state < VMBUS_STATE_MAX);
- complete = state_runner[vmbus->state].complete;
- if (!complete || complete(vmbus)) {
- vmbus->state = VMBUS_LISTEN;
- }
- out:
- vmbus->msg_in_progress = false;
- vmbus_resched(vmbus);
- }
- static void vmbus_resched(VMBus *vmbus)
- {
- aio_bh_schedule_oneshot(qemu_get_aio_context(), vmbus_run, vmbus);
- }
- static void vmbus_signal_event(EventNotifier *e)
- {
- VMBusChannel *chan;
- VMBus *vmbus = container_of(e, VMBus, notifier);
- unsigned long *int_map;
- hwaddr addr, len;
- bool is_dirty = false;
- if (!event_notifier_test_and_clear(e)) {
- return;
- }
- trace_vmbus_signal_event();
- if (!vmbus->int_page_gpa) {
- return;
- }
- addr = vmbus->int_page_gpa + TARGET_PAGE_SIZE / 2;
- len = TARGET_PAGE_SIZE / 2;
- int_map = cpu_physical_memory_map(addr, &len, 1);
- if (len != TARGET_PAGE_SIZE / 2) {
- goto unmap;
- }
- QTAILQ_FOREACH(chan, &vmbus->channel_list, link) {
- if (bitmap_test_and_clear_atomic(int_map, chan->id, 1)) {
- if (!vmbus_channel_is_open(chan)) {
- continue;
- }
- vmbus_channel_notify_host(chan);
- is_dirty = true;
- }
- }
- unmap:
- cpu_physical_memory_unmap(int_map, len, 1, is_dirty);
- }
- static void vmbus_dev_realize(DeviceState *dev, Error **errp)
- {
- VMBusDevice *vdev = VMBUS_DEVICE(dev);
- VMBusDeviceClass *vdc = VMBUS_DEVICE_GET_CLASS(vdev);
- VMBus *vmbus = VMBUS(qdev_get_parent_bus(dev));
- BusChild *child;
- Error *err = NULL;
- char idstr[UUID_STR_LEN];
- assert(!qemu_uuid_is_null(&vdev->instanceid));
- if (!qemu_uuid_is_null(&vdc->instanceid)) {
- /* Class wants to only have a single instance with a fixed UUID */
- if (!qemu_uuid_is_equal(&vdev->instanceid, &vdc->instanceid)) {
- error_setg(&err, "instance id can't be changed");
- goto error_out;
- }
- }
- /* Check for instance id collision for this class id */
- QTAILQ_FOREACH(child, &BUS(vmbus)->children, sibling) {
- VMBusDevice *child_dev = VMBUS_DEVICE(child->child);
- if (child_dev == vdev) {
- continue;
- }
- if (qemu_uuid_is_equal(&child_dev->instanceid, &vdev->instanceid)) {
- qemu_uuid_unparse(&vdev->instanceid, idstr);
- error_setg(&err, "duplicate vmbus device instance id %s", idstr);
- goto error_out;
- }
- }
- vdev->dma_as = &address_space_memory;
- create_channels(vmbus, vdev, &err);
- if (err) {
- goto error_out;
- }
- if (vdc->vmdev_realize) {
- vdc->vmdev_realize(vdev, &err);
- if (err) {
- goto err_vdc_realize;
- }
- }
- return;
- err_vdc_realize:
- free_channels(vdev);
- error_out:
- error_propagate(errp, err);
- }
- static void vmbus_dev_reset(DeviceState *dev)
- {
- uint16_t i;
- VMBusDevice *vdev = VMBUS_DEVICE(dev);
- VMBusDeviceClass *vdc = VMBUS_DEVICE_GET_CLASS(vdev);
- if (vdev->channels) {
- for (i = 0; i < vdev->num_channels; i++) {
- VMBusChannel *chan = &vdev->channels[i];
- close_channel(chan);
- chan->state = VMCHAN_INIT;
- }
- }
- if (vdc->vmdev_reset) {
- vdc->vmdev_reset(vdev);
- }
- }
- static void vmbus_dev_unrealize(DeviceState *dev)
- {
- VMBusDevice *vdev = VMBUS_DEVICE(dev);
- VMBusDeviceClass *vdc = VMBUS_DEVICE_GET_CLASS(vdev);
- if (vdc->vmdev_unrealize) {
- vdc->vmdev_unrealize(vdev);
- }
- free_channels(vdev);
- }
- static const Property vmbus_dev_props[] = {
- DEFINE_PROP_UUID("instanceid", VMBusDevice, instanceid),
- };
- static void vmbus_dev_class_init(ObjectClass *klass, void *data)
- {
- DeviceClass *kdev = DEVICE_CLASS(klass);
- device_class_set_props(kdev, vmbus_dev_props);
- kdev->bus_type = TYPE_VMBUS;
- kdev->realize = vmbus_dev_realize;
- kdev->unrealize = vmbus_dev_unrealize;
- device_class_set_legacy_reset(kdev, vmbus_dev_reset);
- }
- static void vmbus_dev_instance_init(Object *obj)
- {
- VMBusDevice *vdev = VMBUS_DEVICE(obj);
- VMBusDeviceClass *vdc = VMBUS_DEVICE_GET_CLASS(vdev);
- if (!qemu_uuid_is_null(&vdc->instanceid)) {
- /* Class wants to only have a single instance with a fixed UUID */
- vdev->instanceid = vdc->instanceid;
- }
- }
- const VMStateDescription vmstate_vmbus_dev = {
- .name = TYPE_VMBUS_DEVICE,
- .version_id = 0,
- .minimum_version_id = 0,
- .fields = (const VMStateField[]) {
- VMSTATE_UINT8_ARRAY(instanceid.data, VMBusDevice, 16),
- VMSTATE_UINT16(num_channels, VMBusDevice),
- VMSTATE_STRUCT_VARRAY_POINTER_UINT16(channels, VMBusDevice,
- num_channels, vmstate_channel,
- VMBusChannel),
- VMSTATE_END_OF_LIST()
- }
- };
- /* vmbus generic device base */
- static const TypeInfo vmbus_dev_type_info = {
- .name = TYPE_VMBUS_DEVICE,
- .parent = TYPE_DEVICE,
- .abstract = true,
- .instance_size = sizeof(VMBusDevice),
- .class_size = sizeof(VMBusDeviceClass),
- .class_init = vmbus_dev_class_init,
- .instance_init = vmbus_dev_instance_init,
- };
- static void vmbus_realize(BusState *bus, Error **errp)
- {
- int ret = 0;
- VMBus *vmbus = VMBUS(bus);
- qemu_mutex_init(&vmbus->rx_queue_lock);
- QTAILQ_INIT(&vmbus->gpadl_list);
- QTAILQ_INIT(&vmbus->channel_list);
- ret = hyperv_set_msg_handler(VMBUS_MESSAGE_CONNECTION_ID,
- vmbus_recv_message, vmbus);
- if (ret != 0) {
- error_setg(errp, "hyperv set message handler failed: %d", ret);
- goto error_out;
- }
- ret = event_notifier_init(&vmbus->notifier, 0);
- if (ret != 0) {
- error_setg(errp, "event notifier failed to init with %d", ret);
- goto remove_msg_handler;
- }
- event_notifier_set_handler(&vmbus->notifier, vmbus_signal_event);
- ret = hyperv_set_event_flag_handler(VMBUS_EVENT_CONNECTION_ID,
- &vmbus->notifier);
- if (ret != 0) {
- error_setg(errp, "hyperv set event handler failed with %d", ret);
- goto clear_event_notifier;
- }
- return;
- clear_event_notifier:
- event_notifier_cleanup(&vmbus->notifier);
- remove_msg_handler:
- hyperv_set_msg_handler(VMBUS_MESSAGE_CONNECTION_ID, NULL, NULL);
- error_out:
- qemu_mutex_destroy(&vmbus->rx_queue_lock);
- }
- static void vmbus_unrealize(BusState *bus)
- {
- VMBus *vmbus = VMBUS(bus);
- hyperv_set_msg_handler(VMBUS_MESSAGE_CONNECTION_ID, NULL, NULL);
- hyperv_set_event_flag_handler(VMBUS_EVENT_CONNECTION_ID, NULL);
- event_notifier_cleanup(&vmbus->notifier);
- qemu_mutex_destroy(&vmbus->rx_queue_lock);
- }
- static void vmbus_reset_hold(Object *obj, ResetType type)
- {
- vmbus_deinit(VMBUS(obj));
- }
- static char *vmbus_get_dev_path(DeviceState *dev)
- {
- BusState *bus = qdev_get_parent_bus(dev);
- return qdev_get_dev_path(bus->parent);
- }
- static char *vmbus_get_fw_dev_path(DeviceState *dev)
- {
- VMBusDevice *vdev = VMBUS_DEVICE(dev);
- char uuid[UUID_STR_LEN];
- qemu_uuid_unparse(&vdev->instanceid, uuid);
- return g_strdup_printf("%s@%s", qdev_fw_name(dev), uuid);
- }
- static void vmbus_class_init(ObjectClass *klass, void *data)
- {
- BusClass *k = BUS_CLASS(klass);
- ResettableClass *rc = RESETTABLE_CLASS(klass);
- k->get_dev_path = vmbus_get_dev_path;
- k->get_fw_dev_path = vmbus_get_fw_dev_path;
- k->realize = vmbus_realize;
- k->unrealize = vmbus_unrealize;
- rc->phases.hold = vmbus_reset_hold;
- }
- static int vmbus_pre_load(void *opaque)
- {
- VMBusChannel *chan;
- VMBus *vmbus = VMBUS(opaque);
- /*
- * channel IDs allocated by the source will come in the migration stream
- * for each channel, so clean up the ones allocated at realize
- */
- QTAILQ_FOREACH(chan, &vmbus->channel_list, link) {
- unregister_chan_id(chan);
- }
- return 0;
- }
- static int vmbus_post_load(void *opaque, int version_id)
- {
- int ret;
- VMBus *vmbus = VMBUS(opaque);
- VMBusGpadl *gpadl;
- VMBusChannel *chan;
- ret = vmbus_init(vmbus);
- if (ret) {
- return ret;
- }
- QTAILQ_FOREACH(gpadl, &vmbus->gpadl_list, link) {
- gpadl->vmbus = vmbus;
- gpadl->refcount = 1;
- }
- /*
- * reopening channels depends on initialized vmbus so it's done here
- * instead of channel_post_load()
- */
- QTAILQ_FOREACH(chan, &vmbus->channel_list, link) {
- if (chan->state == VMCHAN_OPENING || chan->state == VMCHAN_OPEN) {
- open_channel(chan);
- }
- if (chan->state != VMCHAN_OPEN) {
- continue;
- }
- if (!vmbus_channel_is_open(chan)) {
- /* reopen failed, abort loading */
- return -1;
- }
- /* resume processing on the guest side if it missed the notification */
- hyperv_sint_route_set_sint(chan->notify_route);
- /* ditto on the host side */
- vmbus_channel_notify_host(chan);
- }
- vmbus_resched(vmbus);
- return 0;
- }
- static const VMStateDescription vmstate_post_message_input = {
- .name = "vmbus/hyperv_post_message_input",
- .version_id = 0,
- .minimum_version_id = 0,
- .fields = (const VMStateField[]) {
- /*
- * skip connection_id and message_type as they are validated before
- * queueing and ignored on dequeueing
- */
- VMSTATE_UINT32(payload_size, struct hyperv_post_message_input),
- VMSTATE_UINT8_ARRAY(payload, struct hyperv_post_message_input,
- HV_MESSAGE_PAYLOAD_SIZE),
- VMSTATE_END_OF_LIST()
- }
- };
- static bool vmbus_rx_queue_needed(void *opaque)
- {
- VMBus *vmbus = VMBUS(opaque);
- return vmbus->rx_queue_size;
- }
- static const VMStateDescription vmstate_rx_queue = {
- .name = "vmbus/rx_queue",
- .version_id = 0,
- .minimum_version_id = 0,
- .needed = vmbus_rx_queue_needed,
- .fields = (const VMStateField[]) {
- VMSTATE_UINT8(rx_queue_head, VMBus),
- VMSTATE_UINT8(rx_queue_size, VMBus),
- VMSTATE_STRUCT_ARRAY(rx_queue, VMBus,
- HV_MSG_QUEUE_LEN, 0,
- vmstate_post_message_input,
- struct hyperv_post_message_input),
- VMSTATE_END_OF_LIST()
- }
- };
- static const VMStateDescription vmstate_vmbus = {
- .name = TYPE_VMBUS,
- .version_id = 0,
- .minimum_version_id = 0,
- .pre_load = vmbus_pre_load,
- .post_load = vmbus_post_load,
- .fields = (const VMStateField[]) {
- VMSTATE_UINT8(state, VMBus),
- VMSTATE_UINT32(version, VMBus),
- VMSTATE_UINT32(target_vp, VMBus),
- VMSTATE_UINT64(int_page_gpa, VMBus),
- VMSTATE_QTAILQ_V(gpadl_list, VMBus, 0,
- vmstate_gpadl, VMBusGpadl, link),
- VMSTATE_END_OF_LIST()
- },
- .subsections = (const VMStateDescription * const []) {
- &vmstate_rx_queue,
- NULL
- }
- };
- static const TypeInfo vmbus_type_info = {
- .name = TYPE_VMBUS,
- .parent = TYPE_BUS,
- .instance_size = sizeof(VMBus),
- .class_init = vmbus_class_init,
- };
- static void vmbus_bridge_realize(DeviceState *dev, Error **errp)
- {
- VMBusBridge *bridge = VMBUS_BRIDGE(dev);
- /*
- * here there's at least one vmbus bridge that is being realized, so
- * vmbus_bridge_find can only return NULL if it's not unique
- */
- if (!vmbus_bridge_find()) {
- error_setg(errp, "there can be at most one %s in the system",
- TYPE_VMBUS_BRIDGE);
- return;
- }
- if (!hyperv_is_synic_enabled()) {
- error_report("VMBus requires usable Hyper-V SynIC and VP_INDEX");
- return;
- }
- if (!hyperv_are_vmbus_recommended_features_enabled()) {
- warn_report("VMBus enabled without the recommended set of Hyper-V features: "
- "hv-stimer, hv-vapic and hv-runtime. "
- "Some Windows versions might not boot or enable the VMBus device");
- }
- bridge->bus = VMBUS(qbus_new(TYPE_VMBUS, dev, "vmbus"));
- }
- static char *vmbus_bridge_ofw_unit_address(const SysBusDevice *dev)
- {
- /* there can be only one VMBus */
- return g_strdup("0");
- }
- static const VMStateDescription vmstate_vmbus_bridge = {
- .name = TYPE_VMBUS_BRIDGE,
- .version_id = 0,
- .minimum_version_id = 0,
- .fields = (const VMStateField[]) {
- VMSTATE_STRUCT_POINTER(bus, VMBusBridge, vmstate_vmbus, VMBus),
- VMSTATE_END_OF_LIST()
- },
- };
- static const Property vmbus_bridge_props[] = {
- DEFINE_PROP_UINT8("irq", VMBusBridge, irq, 7),
- };
- static void vmbus_bridge_class_init(ObjectClass *klass, void *data)
- {
- DeviceClass *k = DEVICE_CLASS(klass);
- SysBusDeviceClass *sk = SYS_BUS_DEVICE_CLASS(klass);
- k->realize = vmbus_bridge_realize;
- k->fw_name = "vmbus";
- sk->explicit_ofw_unit_address = vmbus_bridge_ofw_unit_address;
- set_bit(DEVICE_CATEGORY_BRIDGE, k->categories);
- k->vmsd = &vmstate_vmbus_bridge;
- device_class_set_props(k, vmbus_bridge_props);
- /* override SysBusDevice's default */
- k->user_creatable = true;
- }
- static const TypeInfo vmbus_bridge_type_info = {
- .name = TYPE_VMBUS_BRIDGE,
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(VMBusBridge),
- .class_init = vmbus_bridge_class_init,
- };
- static void vmbus_register_types(void)
- {
- type_register_static(&vmbus_bridge_type_info);
- type_register_static(&vmbus_dev_type_info);
- type_register_static(&vmbus_type_info);
- }
- type_init(vmbus_register_types)
|