···44type t = iovec * buf array
55external alloc_iovec : buf array -> iovec = "ocaml_uring_alloc_iovecs"
66external free_iovec : iovec -> unit = "ocaml_uring_free_iovecs"
77+external adjust_iovec : iovec -> int -> int -> unit = "ocaml_iovec_advance_offset"
7889let alloc_buf len =
910 Bigarray.(Array1.create char c_layout len)
···1112let alloc bufs : t =
1213 let v = alloc_iovec bufs in
1314 v, bufs
1515+1616+let advance (iovec,_) ~idx ~adj =
1717+ adjust_iovec iovec idx adj
14181519let free (iov,_) = free_iovec iov
1620
+1
iovec.mli
···88val nr_vecs : t -> int
99val bufs : t -> buf array
1010val empty : t
1111+val advance : t -> idx:int -> adj:int -> unit
+1-1
urcat.ml
···3232 let blocks = if file_sz mod block_size <> 0 then (file_sz / block_size)+1 else file_sz/block_size in
3333 let bufs = Array.init blocks (fun _ -> Uring.Iovec.alloc_buf block_size) in
3434 let iov = Uring.Iovec.alloc bufs in
3535- Uring.submit_readv uring fd iov (iov :> Uring.Iovec.t);
3535+ Uring.readv uring fd iov (iov :> Uring.Iovec.t);
3636 let numreq = Uring.submit uring in
3737 assert(numreq=1);
3838 ()
+103
urcp.ml
···11+(* cp(1) built with liburing. Queues up as many reads as the queue
22+ * depth allows and then queues up corresponding writes.
33+ OCaml version of https://unixism.net/loti/tutorial/cp_liburing.html *)
44+55+let queue_depth = 64
66+let block_size = 32*1024
77+88+let get_file_size fd =
99+ Unix.handle_unix_error Unix.fstat fd |>
1010+ fun {Unix.st_size; _} -> st_size
1111+(* TODO make this work with ST_ISBLK *)
1212+1313+type t = {
1414+ mutable insize: int;
1515+ mutable offset: int;
1616+ mutable reads: int;
1717+ mutable writes: int;
1818+ mutable write_left: int;
1919+ mutable read_left: int;
2020+ infd: Unix.file_descr;
2121+ outfd: Unix.file_descr;
2222+}
2323+2424+type req = {
2525+ op: [`R | `W ];
2626+ iov: Uring.Iovec.t;
2727+ len: int;
2828+ mutable off: int;
2929+ t : t;
3030+}
3131+3232+let empty_req t = { op=`R; iov=Uring.Iovec.empty; len=0; off=0; t}
3333+3434+(* Perform a complete read into bufs. *)
3535+let queue_read uring t len =
3636+ let ba = Uring.Iovec.alloc_buf block_size in
3737+ let iov = Uring.Iovec.alloc [|ba|] in
3838+ let req = { op=`R; iov; len; off=t.offset; t } in
3939+ Uring.readv uring t.infd iov req;
4040+ t.offset <- t.offset + len;
4141+ t.read_left <- t.read_left + len;
4242+ t.reads <- t.reads + 1
4343+4444+(* TODO compile time check *)
4545+let eagain = -11
4646+let eintr = -4
4747+4848+(* Check that a read has completely finished, and if not
4949+ * queue it up for completing the remaining amount *)
5050+let handle_read_completion uring req res =
5151+ let bytes_to_read = req.len - req.off in
5252+ match res with
5353+ | 0 -> raise End_of_file
5454+ | n when n = eagain || n = eintr ->
5555+ (* requeue the request *)
5656+ Uring.readv ~offset:req.off uring req.t.infd req.iov req
5757+ | n when n < 0 ->
5858+ raise (Failure ("unix errorno " ^ (string_of_int n)))
5959+ | n when n < bytes_to_read ->
6060+ (* handle short read so new iovec and resubmit *)
6161+ Uring.Iovec.advance req.iov ~idx:0 ~adj:n;
6262+ req.off <-req.off + n;
6363+ Uring.readv ~offset:req.off uring req.t.infd req.iov req
6464+ | n when n = bytes_to_read ->
6565+ (* Read is complete, all bytes are read, turn it into a write *)
6666+ req.t.reads <- req.t.reads - 1;
6767+ req.t.writes <- req.t.writes + 1;
6868+ (* reset the iovec *)
6969+ Uring.Iovec.advance req.iov ~idx:0 ~adj:(req.off * -1);
7070+ let req = { req with op=`W; off=0 } in
7171+ Uring.writev ~offset:0 uring req.t.outfd req.iov req
7272+ | n -> raise (Failure ("unexpected readv result > len " ^ (string_of_int n)))
7373+7474+let copy_file uring t =
7575+ (* Create a set of read requests that we will turn into write requests
7676+ * up until the queue depth *)
7777+ while t.write_left > 0 || t.read_left > 0 do
7878+ let need_submit = ref false in
7979+ let submit () =
8080+ if t.read_left > 0 then begin
8181+ if t.reads + t.writes < queue_depth then begin
8282+ let size = min block_size t.read_left in
8383+ queue_read uring t size;
8484+ need_submit := true;
8585+ end
8686+ end;
8787+ if !need_submit then
8888+ let _ = Uring.submit uring in ()
8989+9090+ in
9191+ submit ()
9292+ done
9393+9494+let () =
9595+ let infile = Sys.argv.(1) in
9696+ let outfile = Sys.argv.(2) in
9797+ let infd = Unix.(handle_unix_error (openfile infile [O_RDONLY]) 0) in
9898+ let outfd = Unix.(handle_unix_error (openfile outfile [O_WRONLY; O_CREAT; O_TRUNC]) 0o644) in
9999+ let insize = get_file_size infd in
100100+ let t = { insize; offset=0; reads=0; writes=0; write_left=insize; read_left=insize; infd; outfd } in
101101+ let uring = Uring.create ~queue_depth ~default:(empty_req t) () in
102102+ copy_file uring t
103103+ (* TOD fd close and iouring exit *)
+6-6
uring.ml
···77external uring_submit : uring -> int = "ocaml_uring_submit"
8899type id = int
1010-external uring_submit_readv : uring -> Unix.file_descr -> id -> Iovec.t -> int64 -> unit = "ocaml_uring_submit_readv"
1111-external uring_submit_writev : uring -> Unix.file_descr -> id -> Iovec.t -> int64 -> unit = "ocaml_uring_submit_writev"
1010+external uring_submit_readv : uring -> Unix.file_descr -> id -> Iovec.t -> int -> unit = "ocaml_uring_submit_readv"
1111+external uring_submit_writev : uring -> Unix.file_descr -> id -> Iovec.t -> int -> unit = "ocaml_uring_submit_writev"
12121313external uring_wait_cqe : uring -> id * int = "ocaml_uring_wait_cqe"
1414···3939let put_id t v =
4040 t.id_freelist <- v :: t.id_freelist
41414242-let submit_readv t fd iovec user_data =
4242+let readv t ?(offset=0) fd iovec user_data =
4343 let id = get_id t in
4444- uring_submit_readv t.uring fd id iovec 0L;
4444+ uring_submit_readv t.uring fd id iovec offset;
4545 t.user_data.(id) <- user_data
46464747-let submit_writev t fd iovec user_data =
4747+let writev t ?(offset=0) fd iovec user_data =
4848 let id = get_id t in
4949- uring_submit_writev t.uring fd id iovec 0L;
4949+ uring_submit_writev t.uring fd id iovec offset;
5050 t.user_data.(id) <- user_data
51515252let submit {uring;_} =
+2-2
uring.mli
···4455val create : queue_depth:int -> default:'a -> unit -> 'a t
6677-val submit_readv : 'a t -> Unix.file_descr -> Iovec.t -> 'a -> unit
88-val submit_writev : 'a t -> Unix.file_descr -> Iovec.t -> 'a -> unit
77+val readv : 'a t -> ?offset:int -> Unix.file_descr -> Iovec.t -> 'a -> unit
88+val writev : 'a t -> ?offset:int -> Unix.file_descr -> Iovec.t -> 'a -> unit
99val submit : 'a t -> int
10101111val wait : 'a t -> 'a * int
+15-4
uring_stubs.c
···9797}
98989999value
100100+ocaml_iovec_advance_offset(value v_iovecs, value v_idx, value v_adj)
101101+{
102102+ struct iovec *iovs = (struct iovec *) (v_iovecs & ~1);
103103+ int idx = Int_val(v_idx);
104104+ int adj = Int_val(v_adj);
105105+ iovs[idx].iov_base += adj;
106106+ iovs[idx].iov_len -= adj;
107107+ return(Val_unit);
108108+}
109109+110110+value
100111ocaml_uring_free_iovecs(value iovecs)
101112{
102113 struct iovec *iovs = (struct iovec *) (iovecs & ~1);
···114125 struct io_uring_sqe *sqe = io_uring_get_sqe(ring);
115126 if (!sqe)
116127 caml_failwith("unable to allocate SQE");
117117- fprintf(stderr, "submit_readv: %d ents off %lu\n", len, Int64_val(v_off));
118118- io_uring_prep_readv(sqe, Int_val(v_fd), iovs, len, Int64_val(v_off)); /* TODO add offset to intf */
128128+ fprintf(stderr, "submit_readv: %d ents off %d\n", len, Int_val(v_off));
129129+ io_uring_prep_readv(sqe, Int_val(v_fd), iovs, len, Int_val(v_off)); /* TODO add offset to intf */
119130 io_uring_sqe_set_data(sqe, (void *)(uintptr_t)Int_val(v_id)); /* TODO sort out cast */
120131 CAMLreturn(Val_unit);
121132}
···129140 struct io_uring_sqe *sqe = io_uring_get_sqe(ring);
130141 if (!sqe)
131142 caml_failwith("unable to allocate SQE");
132132- fprintf(stderr, "submit_writev: %d ents off %lu\n", len, Int64_val(v_off));
133133- io_uring_prep_writev(sqe, Int_val(v_fd), iovs, len, Int64_val(v_off)); /* TODO add offset to intf */
143143+ fprintf(stderr, "submit_writev: %d ents off %d\n", len, Int_val(v_off));
144144+ io_uring_prep_writev(sqe, Int_val(v_fd), iovs, len, Int_val(v_off)); /* TODO add offset to intf */
134145 io_uring_sqe_set_data(sqe, (void *)(uintptr_t)Int_val(v_id)); /* TODO sort out cast */
135146 CAMLreturn(Val_unit);
136147}
+1-1
uring_test.ml
···44 let b1 = Uring.Iovec.alloc_buf 3 in
55 let b2 = Uring.Iovec.alloc_buf 7 in
66 let iov = Uring.Iovec.alloc [|b1;b2|] in
77- Uring.submit_readv t fd iov ();
77+ Uring.readv t fd iov ();
88 let res = Uring.submit t in
99 Printf.eprintf "submitted %d\n%!" res;
1010 let (), res = Uring.wait t in