123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448(*
* Copyright (c) 2010-2011 Anil Madhavapeddy <anil@recoil.org>
* Copyright (c) 2012 Citrix Systems, Inc
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*)typebuf=Cstruct.tletsubtofflen=Cstruct.subtofflenletlengtht=Cstruct.lengthtexternalmemory_barrier:unit->unit="caml_memory_barrier"[@@noalloc](* [load_uint32 c byte_offset] returns an int containing the 32-bit
word found at [byte_offset] read with a single load instruction. *)externalunsafe_load_uint32:Cstruct.t->int->int="caml_cstruct_unsafe_load_uint32"(* [save_uint32 c byte_offset newval] writes a 32-bit word at
[byte_offset] using a single store instruction. *)externalunsafe_save_uint32:Cstruct.t->int->int->unit="caml_cstruct_unsafe_save_uint32"moduleRpc=struct(*
struct sring {
RING_IDX req_prod, req_event;
RING_IDX rsp_prod, rsp_event;
uint8_t netfront_smartpoll_active;
uint8_t pad[47];
};
*)(* (* It's unsafe to use these since they use multi-byte load/stores *)
[%%cstruct
type ring_hdr = {
req_prod: uint32_t;
req_event: uint32_t;
rsp_prod: uint32_t;
rsp_event: uint32_t;
stuff: uint64_t;
} [@@little_endian]
]
*)(* offsets in the header: *)let_req_prod=0let_req_event=4let_rsp_prod=8let_rsp_event=12letinitialisering=(* initialise the *_event fields to 1, and the rest to 0 *)unsafe_save_uint32ring_req_prod0;unsafe_save_uint32ring_req_event1;unsafe_save_uint32ring_rsp_prod0;unsafe_save_uint32ring_rsp_event1;typesring={buf:Cstruct.t;(* Overall I/O buffer *)header_size:int;(* Header of shared ring variables, in bits *)idx_size:int;(* Size in bits of an index slot *)nr_ents:int;(* Number of index entries *)name:string;(* For pretty printing only *)}letof_buf_no_init~buf~idx_size~name=letheader_size=4+4+4+4+48in(* header bytes size of struct sring *)(* Round down to the nearest power of 2, so we can mask indices easily *)letround_down_to_nearest_2x=int_of_float(2.**(floor((log(floatx))/.(log2.))))in(* Free space in shared ring after header is accounted for *)letfree_bytes=lengthbuf-header_sizeinletnr_ents=round_down_to_nearest_2(free_bytes/idx_size)in{name;buf;idx_size;nr_ents;header_size}letof_buf~buf~idx_size~name=initialisebuf;of_buf_no_init~buf~idx_size~nameletto_summary_stringt=Printf.sprintf"ring %s header_size = %d; index slot size = %d; number of entries = %d"t.namet.header_sizet.idx_sizet.nr_entsletsring_rsp_prodsring=unsafe_load_uint32sring.buf_rsp_prodletsring_req_prodsring=unsafe_load_uint32sring.buf_req_prodletsring_req_eventsring=memory_barrier();unsafe_load_uint32sring.buf_req_eventletsring_rsp_eventsring=memory_barrier();unsafe_load_uint32sring.buf_rsp_eventletsring_push_requestssringreq_prod=memory_barrier();(* ensure requests are seen before the index is updated *)unsafe_save_uint32sring.buf_req_prodreq_prodletsring_push_responsessringrsp_prod=memory_barrier();(* ensure requests are seen before the index is updated *)unsafe_save_uint32sring.buf_rsp_prodrsp_prodletsring_set_rsp_eventsringrsp_event=unsafe_save_uint32sring.buf_rsp_eventrsp_event;memory_barrier()letsring_set_req_eventsringreq_event=unsafe_save_uint32sring.buf_req_eventreq_event;memory_barrier()let_nr_entssring=sring.nr_entsletslotsringidx=(* TODO should precalculate these and store in the sring? this is fast-path *)letidx=idxland(sring.nr_ents-1)inletoff=sring.header_size+(idx*sring.idx_size)insubsring.bufoffsring.idx_sizemoduleFront=structtype('a,'b)t={mutablereq_prod_pvt:int;mutablersp_cons:int;sring:sring;}letinit~sring=letreq_prod_pvt=0inletrsp_cons=0in{req_prod_pvt;rsp_cons;sring}letslottidx=slott.sringidxletnr_entst=t.sring.nr_entsletget_free_requestst=t.sring.nr_ents-(t.req_prod_pvt-t.rsp_cons)let_is_ring_fullt=get_free_requestst=0lethas_unconsumed_responsest=((sring_rsp_prodt.sring)-t.rsp_cons)>0letpush_requestst=sring_push_requestst.sringt.req_prod_pvtletpush_requests_and_check_notifyt=letold_idx=sring_req_prodt.sringinletnew_idx=t.req_prod_pvtinpush_requestst;(new_idx-(sring_req_eventt.sring))<(new_idx-old_idx)letcheck_for_responsest=ifhas_unconsumed_responsestthentrueelsebeginsring_set_rsp_eventt.sring(t.rsp_cons+1);has_unconsumed_responsestendletnext_req_idt=lets=t.req_prod_pvtint.req_prod_pvt<-t.req_prod_pvt+1;sletrecack_responsestfn=letrsp_prod=sring_rsp_prodt.sringinwhilet.rsp_cons!=rsp_proddoletslot_id=t.rsp_consinletslot=slottslot_idinfnslot;t.rsp_cons<-t.rsp_cons+1;done;ifcheck_for_responsestthenack_responsestfnletto_stringt=letnr_unconsumed_responses=(sring_rsp_prodt.sring)-t.rsp_consinletnr_free_requests=get_free_requeststinPrintf.sprintf"Front { req_prod = %d; rsp_prod = %d; req_event = %d; rsp_event = %d; rsp_cons = %d; req_prod_pvt = %d; %s; %s }"(sring_req_prodt.sring)(sring_rsp_prodt.sring)(sring_req_eventt.sring)(sring_rsp_eventt.sring)t.rsp_const.req_prod_pvt(ifnr_unconsumed_responses>0thenPrintf.sprintf"%d unconsumed responses"nr_unconsumed_responseselse"frontend has consumed all responses")(ifnr_free_requests>0thenPrintf.sprintf"%d free request slots"nr_free_requestselse"all slots are full")endmoduleBack=structtype('a,'b)t={mutablersp_prod_pvt:int;mutablereq_cons:int;sring:sring;}letinit~sring=letrsp_prod_pvt=0inletreq_cons=0in{rsp_prod_pvt;req_cons;sring}letslottidx=slott.sringidxletnr_entst=t.sring.nr_entslethas_unconsumed_requestst=letreq=(sring_req_prodt.sring)-t.req_consinletrsp=t.sring.nr_ents-(t.req_cons-t.rsp_prod_pvt)inifreq<rspthen(req>0)else(rsp>0)letpush_responsest=sring_push_responsest.sringt.rsp_prod_pvtletpush_responses_and_check_notifyt=letold_idx=sring_rsp_prodt.sringinletnew_idx=t.rsp_prod_pvtinpush_responsest;(new_idx-(sring_rsp_eventt.sring))<(new_idx-old_idx)letcheck_for_requestst=ifhas_unconsumed_requeststthentrueelsebeginsring_set_req_eventt.sring(t.req_cons+1);has_unconsumed_requeststendletnext_res_idt=lets=t.rsp_prod_pvtint.rsp_prod_pvt<-t.rsp_prod_pvt+1;slet_next_slott=slott(next_res_idt)letfinal_check_for_requestst=has_unconsumed_requestst||beginsring_set_req_eventt.sring(t.req_cons+1);has_unconsumed_requeststendletmore_to_dot=ift.rsp_prod_pvt=t.req_consthenfinal_check_for_requeststelsehas_unconsumed_requeststletto_stringt=letreq_prod=sring_req_prodt.sringinletrsp_prod=sring_rsp_prodt.sringinletreq_event=sring_req_eventt.sringinletrsp_event=sring_rsp_eventt.sringinPrintf.sprintf"{ req_prod=%d rsp_prod=%d req_event=%d rsp_event=%d rsp_prod_pvt=%d req_cons=%d }"req_prodrsp_prodreq_eventrsp_eventt.rsp_prod_pvtt.req_consletrecack_requeststfn=letreq_prod=sring_req_prodt.sringinwhilet.req_cons!=req_proddoletslot_id=t.req_consinletslot=slottslot_idint.req_cons<-t.req_cons+1;fnslot;done;ifcheck_for_requeststthenack_requeststfnendendmoduletypeRW=sig(** A bi-directional pipe where 'input' and 'output' are from
the frontend's (i.e. the guest's) point of view *)valget_ring_input:Cstruct.t->Cstruct.tvalget_ring_input_cons:Cstruct.t->int32valget_ring_input_prod:Cstruct.t->int32valset_ring_input_cons:Cstruct.t->int32->unitvalset_ring_input_prod:Cstruct.t->int32->unitvalget_ring_output:Cstruct.t->Cstruct.tvalget_ring_output_cons:Cstruct.t->int32valget_ring_output_prod:Cstruct.t->int32valset_ring_output_cons:Cstruct.t->int32->unitvalset_ring_output_prod:Cstruct.t->int32->unitendmoduleReverse(RW:RW)=structletget_ring_input=RW.get_ring_outputletget_ring_input_cons=RW.get_ring_output_consletget_ring_input_prod=RW.get_ring_output_prodletset_ring_input_cons=RW.set_ring_output_consletset_ring_input_prod=RW.set_ring_output_prodletget_ring_output=RW.get_ring_inputletget_ring_output_cons=RW.get_ring_input_consletget_ring_output_prod=RW.get_ring_input_prodletset_ring_output_cons=RW.set_ring_input_consletset_ring_output_prod=RW.set_ring_input_prodendmoduletypeSTREAM=sigtypestream=Cstruct.ttypeposition=int32valadvance:stream->position->unitendmoduletypeREADABLE=sigincludeSTREAMvalread:stream->(position*Cstruct.t)endmoduletypeWRITABLE=sigincludeSTREAMvalwrite:stream->(position*Cstruct.t)endmoduletypeS=sigmoduleReader:READABLEmoduleWriter:WRITABLEvalwrite:Cstruct.t->bytes->int->int->intvalread:Cstruct.t->bytes->int->int->intvalunsafe_write:Cstruct.t->bytes->int->int->intvalunsafe_read:Cstruct.t->bytes->int->int->intendmodulePipe(RW:RW)=structmoduleWriter=structtypestream=Cstruct.ttypeposition=int32letwritet=letoutput=RW.get_ring_outputtinletoutput_length=lengthoutputin(* Remember: the producer and consumer indices can be >> output_length *)letcons=Int32.to_int(RW.get_ring_output_const)inletprod=Int32.to_int(RW.get_ring_output_prodt)inmemory_barrier();(* 0 <= cons', prod' <= output_length *)letcons'=letx=consmodoutput_lengthinifx<0thenx+output_lengthelsexandprod'=letx=prodmodoutput_lengthinifx<0thenx+output_lengthelsexinletfree_space=ifprod-cons>=output_lengththen0elseifprod'>=cons'thenoutput_length-prod'(* in this write, fill to the end *)elsecons'-prod'inInt32.of_intprod,Cstruct.suboutputprod'free_spaceletadvancetprod'=memory_barrier();letprod=RW.get_ring_output_prodtinRW.set_ring_output_prodt(maxprod'prod)endmoduleReader=structtypestream=Cstruct.ttypeposition=int32letreadt=letinput=RW.get_ring_inputtinletinput_length=lengthinputinletcons=Int32.to_int(RW.get_ring_input_const)inletprod=Int32.to_int(RW.get_ring_input_prodt)inmemory_barrier();letcons'=letx=consmodinput_lengthinifx<0thenx+input_lengthelsexandprod'=letx=prodmodinput_lengthinifx<0thenx+input_lengthelsexinletdata_available=ifprod=consthen0elseifprod'>cons'thenprod'-cons'elseinput_length-cons'in(* read up to the last byte in the ring *)Int32.of_intcons,Cstruct.subinputcons'data_availableletadvancet(cons':int32)=letcons=RW.get_ring_input_constinRW.set_ring_input_const(maxcons'cons)end(* Backwards compatible string interface: *)letreadtbufofslen=letseq,frag=Reader.readtinletdata_available=Cstruct.lengthfraginletcan_read=minlendata_availableinCstruct.blit_to_bytesfrag0bufofscan_read;Reader.advancetInt32.(addseq(of_intcan_read));can_readletwritetbufofslen=letseq,frag=Writer.writetinletfree_space=Cstruct.lengthfraginletcan_write=minlenfree_spaceinCstruct.blit_from_bytesbufofsfrag0can_write;Writer.advancetInt32.(addseq(of_intcan_write));can_writeletrecrepeatffrombufofslen=letn=ffrombufofsleninifn<len&&n>0thenn+(repeatffrombuf(ofs+n)(len-n))elsenletread=repeatreadletwrite=repeatwrite(* These are provided for backwards compat. Note they used to be unsafe
but are now safe (see #10) *)letunsafe_read=readletunsafe_write=writeendmoduletypeBidirectional_byte_stream=sigvalinit:Cstruct.t->unitvalto_debug_map:Cstruct.t->(string*string)listmoduleFront:SmoduleBack:Sendletzerot=fori=0toCstruct.lengtht-1doCstruct.set_charti'\000'done