Skip to content

Commit f51e7e7

Browse files
authored
Merge epoll feature branch to master (xapi-project#6005)
Pending some final testing by QA, these commits have already been reviewed on the feature branch. This switches `select` calls to `Unixext.select` (which is implemented using `epoll`), and implements the few performance sensitive parts using epoll directly. It also enables some more tests with >1024 fds.
2 parents bc10ca0 + 8cf7ab2 commit f51e7e7

File tree

27 files changed

+183
-26
lines changed

27 files changed

+183
-26
lines changed

ezxenstore.opam

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ depends: [
1414
"cmdliner" {with-test & >= "1.1.0"}
1515
"logs"
1616
"uuidm"
17+
"xapi-stdext-unix"
1718
"xenctrl"
1819
"xenstore"
1920
"xenstore_transport"

ezxenstore.opam.template

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ depends: [
1212
"cmdliner" {with-test & >= "1.1.0"}
1313
"logs"
1414
"uuidm"
15+
"xapi-stdext-unix"
1516
"xenctrl"
1617
"xenstore"
1718
"xenstore_transport"

ocaml/database/block_device_io.ml

+1-1
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ let accept_conn s latest_response_time =
328328
let now = Unix.gettimeofday () in
329329
let timeout = latest_response_time -. now in
330330
(* Await an incoming connection... *)
331-
let ready_to_read, _, _ = Unix.select [s] [] [] timeout in
331+
let ready_to_read, _, _ = Xapi_stdext_unix.Unixext.select [s] [] [] timeout in
332332
R.info "Finished selecting" ;
333333
if List.mem s ready_to_read then
334334
(* We've received a connection. Accept it and return the socket. *)

ocaml/database/master_connection.ml

+5-1
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,11 @@ let open_secure_connection () =
171171
~write_to_log:(fun x -> debug "stunnel: %s\n" x)
172172
~verify_cert host port
173173
@@ fun st_proc ->
174-
let fd_closed = Thread.wait_timed_read Unixfd.(!(st_proc.Stunnel.fd)) 5. in
174+
let fd_closed =
175+
Xapi_stdext_threads.Threadext.wait_timed_read
176+
Unixfd.(!(st_proc.Stunnel.fd))
177+
5.
178+
in
175179
let proc_quit =
176180
try
177181
Unix.kill (Stunnel.getpid st_proc.Stunnel.pid) 0 ;

ocaml/forkexecd/src/child.ml

+6-2
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ let handle_comms_sock comms_sock state =
6161

6262
let handle_comms_no_fd_sock2 comms_sock fd_sock state =
6363
debug "Selecting in handle_comms_no_fd_sock2" ;
64-
let ready, _, _ = Unix.select [comms_sock; fd_sock] [] [] (-1.0) in
64+
let ready, _, _ =
65+
Xapi_stdext_unix.Unixext.select [comms_sock; fd_sock] [] [] (-1.0)
66+
in
6567
debug "Done" ;
6668
if List.mem fd_sock ready then (
6769
debug "fd sock" ;
@@ -74,7 +76,9 @@ let handle_comms_no_fd_sock2 comms_sock fd_sock state =
7476

7577
let handle_comms_with_fd_sock2 comms_sock _fd_sock fd_sock2 state =
7678
debug "Selecting in handle_comms_with_fd_sock2" ;
77-
let ready, _, _ = Unix.select [comms_sock; fd_sock2] [] [] (-1.0) in
79+
let ready, _, _ =
80+
Xapi_stdext_unix.Unixext.select [comms_sock; fd_sock2] [] [] (-1.0)
81+
in
7882
debug "Done" ;
7983
if List.mem fd_sock2 ready then (
8084
debug "fd sock2" ;

ocaml/libs/ezxenstore/core/dune

+1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@
99
(re_export xenstore)
1010
(re_export xenstore_transport)
1111
threads.posix
12+
xapi-stdext-unix
1213
(re_export xenstore.unix))
1314
)

ocaml/libs/ezxenstore/core/watch.ml

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ let wait_for ~xs ?(timeout = 300.) (x : 'a t) =
5050
let thread =
5151
Thread.create
5252
(fun () ->
53-
let r, _, _ = Unix.select [p1] [] [] timeout in
53+
let r, _, _ = Xapi_stdext_unix.Unixext.select [p1] [] [] timeout in
5454
if r <> [] then
5555
()
5656
else

ocaml/libs/http-lib/buf_io.ml

+10-6
Original file line numberDiff line numberDiff line change
@@ -74,21 +74,21 @@ let is_full ic = ic.cur = 0 && ic.max = Bytes.length ic.buf
7474
let fill_buf ~buffered ic timeout =
7575
let buf_size = Bytes.length ic.buf in
7676
let fill_no_exc timeout len =
77-
let l, _, _ = Unix.select [ic.fd] [] [] timeout in
78-
if l <> [] then (
77+
Xapi_stdext_unix.Unixext.with_socket_timeout ic.fd timeout @@ fun () ->
78+
try
7979
let n = Unix.read ic.fd ic.buf ic.max len in
8080
ic.max <- n + ic.max ;
8181
if n = 0 && len <> 0 then raise Eof ;
8282
n
83-
) else
84-
-1
83+
with Unix.Unix_error (Unix.(EAGAIN | EWOULDBLOCK), _, _) -> -1
8584
in
8685
(* If there's no space to read, shift *)
8786
if ic.max = buf_size then shift ic ;
8887
let space_left = buf_size - ic.max in
8988
(* Read byte one by one just do make sure we don't buffer too many chars *)
9089
let n =
91-
fill_no_exc timeout (if buffered then space_left else min space_left 1)
90+
fill_no_exc (Some timeout)
91+
(if buffered then space_left else min space_left 1)
9292
in
9393
(* Select returned nothing to read *)
9494
if n = -1 then raise Timeout ;
@@ -97,7 +97,11 @@ let fill_buf ~buffered ic timeout =
9797
let tofillsz =
9898
if buffered then buf_size - ic.max else min (buf_size - ic.max) 1
9999
in
100-
ignore (fill_no_exc 0.0 tofillsz)
100+
(* cannot use 0 here, for select that'd mean timeout immediately, for
101+
setsockopt it would mean no timeout.
102+
So use a very short timeout instead
103+
*)
104+
ignore (fill_no_exc (Some 1e-6) tofillsz)
101105
)
102106

103107
(** Input one line terminated by \n *)

ocaml/libs/http-lib/http.ml

+1-8
Original file line numberDiff line numberDiff line change
@@ -320,14 +320,8 @@ let read_frame_header buf =
320320
let prefix = Bytes.sub_string buf 0 frame_header_length in
321321
try Scanf.sscanf prefix "FRAME %012d" (fun x -> Some x) with _ -> None
322322

323-
let set_socket_timeout fd t =
324-
try Unix.(setsockopt_float fd SO_RCVTIMEO t)
325-
with Unix.Unix_error (Unix.ENOTSOCK, _, _) ->
326-
(* In the unit tests, the fd comes from a pipe... ignore *)
327-
()
328-
329323
let read_http_request_header ~read_timeout ~total_timeout ~max_length fd =
330-
Option.iter (fun t -> set_socket_timeout fd t) read_timeout ;
324+
Unixext.with_socket_timeout fd read_timeout @@ fun () ->
331325
let buf = Bytes.create (Option.value ~default:1024 max_length) in
332326
let deadline =
333327
Option.map
@@ -372,7 +366,6 @@ let read_http_request_header ~read_timeout ~total_timeout ~max_length fd =
372366
check_timeout_and_read 0 length ;
373367
(true, length)
374368
in
375-
set_socket_timeout fd 0. ;
376369
(frame, Bytes.sub_string buf 0 headers_length, proxy)
377370

378371
let read_http_response_header buf fd =

ocaml/libs/xapi-stdext/lib/xapi-stdext-unix/unixext.ml

+15
Original file line numberDiff line numberDiff line change
@@ -1062,3 +1062,18 @@ module Daemon = struct
10621062
true
10631063
with Unix.Unix_error _ -> false
10641064
end
1065+
1066+
let set_socket_timeout fd t =
1067+
try Unix.(setsockopt_float fd SO_RCVTIMEO t)
1068+
with Unix.Unix_error (Unix.ENOTSOCK, _, _) ->
1069+
(* In the unit tests, the fd comes from a pipe... ignore *)
1070+
()
1071+
1072+
let with_socket_timeout fd timeout_opt f =
1073+
match timeout_opt with
1074+
| Some t ->
1075+
if t < 1e-6 then invalid_arg (Printf.sprintf "Timeout too short: %g" t) ;
1076+
let finally () = set_socket_timeout fd 0. in
1077+
set_socket_timeout fd t ; Fun.protect ~finally f
1078+
| None ->
1079+
f ()

ocaml/libs/xapi-stdext/lib/xapi-stdext-unix/unixext.mli

+3-1
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ val try_read_string : ?limit:int -> Unix.file_descr -> string
146146

147147
exception Timeout
148148

149+
val with_socket_timeout : Unix.file_descr -> float option -> (unit -> 'a) -> 'a
150+
149151
val time_limited_write : Unix.file_descr -> int -> bytes -> float -> unit
150152

151153
val time_limited_write_substring :
@@ -257,7 +259,7 @@ val domain_of_addr : string -> Unix.socket_domain option
257259

258260
val test_open : int -> unit
259261
(** [test_open n] opens n file descriptors. This is useful for testing that the application makes no calls
260-
to [Unix.select] that use file descriptors, because such calls will then immediately fail.
262+
to [Xapi_stdext_unix.Unixext.select] that use file descriptors, because such calls will then immediately fail.
261263
262264
This assumes that [ulimit -n] has been suitably increased in the test environment.
263265

ocaml/message-switch/unix/dune

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
rpclib.json
1313
threads.posix
1414
xapi-stdext-threads
15+
xapi-stdext-unix
1516
)
1617
(preprocess (per_module ((pps ppx_deriving_rpc) Protocol_unix_scheduler)))
1718
)

ocaml/networkd/lib/jsonrpc_client.ml

+2-2
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ let timeout_read fd timeout =
4343
in
4444
let rec inner max_time max_bytes =
4545
let ready_to_read, _, _ =
46-
try Unix.select [fd] [] [] (to_s max_time)
46+
try Xapi_stdext_unix.Unixext.select [fd] [] [] (to_s max_time)
4747
with
4848
(* in case the unix.select call fails in situation like interrupt *)
4949
| Unix.Unix_error (Unix.EINTR, _, _) ->
@@ -96,7 +96,7 @@ let timeout_write filedesc total_length data response_time =
9696
in
9797
let rec inner_write offset max_time =
9898
let _, ready_to_write, _ =
99-
try Unix.select [] [filedesc] [] (to_s max_time)
99+
try Xapi_stdext_unix.Unixext.select [] [filedesc] [] (to_s max_time)
100100
with
101101
(* in case the unix.select call fails in situation like interrupt *)
102102
| Unix.Unix_error (Unix.EINTR, _, _) ->

ocaml/tests/common/suite_init.ml

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
let harness_init () =
2+
(* before any calls to XAPI code, to catch early uses of Unix.select *)
3+
Xapi_stdext_unix.Unixext.test_open 1024 ;
24
Xapi_stdext_unix.Unixext.mkdir_safe Test_common.working_area 0o755 ;
35
(* Alcotest hides the standard output of successful tests,
46
so we will probably not exceed the 4MB limit in Travis *)

ocaml/tests/dune

+38
Original file line numberDiff line numberDiff line change
@@ -169,4 +169,42 @@
169169

170170
(env (_ (env-vars (XAPI_TEST 1))))
171171

172+
; disassemble, but without sources
173+
; (source lookup doesn't work for all dependencies, and is very slow on a large binary)
174+
; To make debugging easier the disassembly is saved to a file instead of piping
175+
(rule
176+
(deps ../xapi/xapi_main.exe)
177+
(target xapi.disasm)
178+
(package xapi)
179+
(action
180+
(with-stdout-to %{target}
181+
(run objdump %{deps} --wide -d --no-show-raw-insn)
182+
)
183+
)
184+
)
185+
186+
(rule
187+
(deps ../xenopsd/xc/xenops_xc_main.exe)
188+
(target xenops_xc_main.disasm)
189+
(package xapi-xenopsd-xc)
190+
(action
191+
(with-stdout-to %{target}
192+
(run objdump %{deps} --wide -d --no-show-raw-insn)
193+
)
194+
)
195+
)
196+
197+
(rule
198+
(alias runtest)
199+
(package xapi)
200+
(deps (:script ./unix_select.gawk) (:disasm xapi.disasm))
201+
(action (run gawk -f ./%{script} %{disasm}))
202+
)
203+
(rule
204+
(alias runtest)
205+
(package xapi-xenopsd-xc)
206+
(deps (:script ./unix_select.gawk) (:disasm xenops_xc_main.disasm))
207+
(action (run gawk -f ./%{script} %{disasm}))
208+
)
209+
172210
(data_only_dirs test_data tests)

ocaml/tests/unix_select.gawk

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
BEGIN { n = 0; }
2+
# A function definition and its address
3+
# Remember its address and update current symbol
4+
# 0000000000850330 <unix_select>:
5+
match($0, /^0*([0-9a-fA-F]+) <([^>]+)>/, symdef) {
6+
SYMBOL = symdef[2];
7+
ADDR = symdef[1];
8+
9+
SYMADDR[ADDR] = SYMBOL;
10+
11+
if (ADDR in LOADED) {
12+
for (idx in LOADED[ADDR]) {
13+
caller = LOADED[ADDR][idx]
14+
CALLS[symdef[2]][n++] = caller
15+
}
16+
}
17+
}
18+
19+
# Indirect calls (mostly used for C stubs)
20+
# mov $0x850330,%rax
21+
# call 872834 <caml_c_call>
22+
match($0, /mov.*0x([0-9a-fA-F]*),/, addr) {
23+
# this will have gaps, but the indexes will be unique
24+
LOADED[addr[1]][n++] = SYMBOL
25+
}
26+
27+
match($0, /call.*<([^>]+)>/, call) {
28+
CALLS[call[1]][n++] = SYMBOL
29+
}
30+
31+
END {
32+
SYM = "unix_select"
33+
had_calls = 0
34+
if (SYM in CALLS) {
35+
for (idx in CALLS[SYM]) {
36+
caller = CALLS[SYM][idx];
37+
print "--"
38+
if (caller ~ /caml(Thread|Unix__fun_).*/) {
39+
# direct calls from these functions to unix_select are expected
40+
print caller "[expected]"
41+
} else {
42+
print caller "[bad]"
43+
had_calls++
44+
}
45+
if (caller in CALLS) {
46+
for (idx2 in CALLS[caller]) {
47+
caller2 = CALLS[caller][idx2];
48+
if (caller2 ~ /caml(Thread).*/) {
49+
print caller2 "[expected]"
50+
} else {
51+
print caller2 "[bad]"
52+
had_calls++
53+
}
54+
if (caller2 in CALLS) {
55+
for (idx3 in CALLS[caller2]) {
56+
caller3 = CALLS[caller2][idx3];
57+
# but we don't expect anyone calling these functions from OCaml code,
58+
# reject that
59+
had_calls++
60+
print caller3 "[bad]"
61+
if (caller3 in CALLS) {
62+
for (idx4 in CALLS[caller3]) {
63+
caller4 = CALLS[caller3][idx4];
64+
print caller4 "[bad]"
65+
for (idx5 in CALLS[caller4]) {
66+
caller5 = CALLS[caller4][idx5];
67+
print caller5 "[bad]"
68+
}
69+
}
70+
}
71+
}
72+
}
73+
}
74+
}
75+
}
76+
}
77+
if (had_calls > 0) {
78+
exit 2
79+
}
80+
}

ocaml/xapi-idl/lib/dune

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
xapi-open-uri
3333
xapi-stdext-pervasives
3434
xapi-stdext-threads
35+
xapi-stdext-unix
3536
xapi-inventory
3637
xmlm
3738
)

ocaml/xapi-idl/lib_test/dune

+2
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@
6565
xapi-idl.xen
6666
xapi-idl.xen.interface
6767
xapi-log
68+
xapi-stdext-unix
69+
xapi-stdext-threads
6870
)
6971
(preprocess (per_module ((pps ppx_deriving_rpc) Task_server_test Updates_test))))
7072

ocaml/xapi-idl/lib_test/scheduler_test.ml

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ let timed_wait_callback ~msg ?(time_min = 0.) ?(eps = 0.1) ?(time_max = 60.) f =
3737
()
3838
in
3939
f callback ;
40-
let ready = Thread.wait_timed_read rd time_max in
40+
let ready = Xapi_stdext_threads.Threadext.wait_timed_read rd time_max in
4141
match (ready, !after) with
4242
| true, None ->
4343
Alcotest.fail "pipe ready to read, but after is not set"

ocaml/xe-cli/newcli.ml

+2-1
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,8 @@ let main_loop ifd ofd permitted_filenames =
594594
finished := true
595595
else
596596
let r, _, _ =
597-
Unix.select [Unix.stdin; fd] [] [] heartbeat_interval
597+
Xapi_stdext_unix.Unixext.select [Unix.stdin; fd] [] []
598+
heartbeat_interval
598599
in
599600
let now = Unix.time () in
600601
if now -. !last_heartbeat >= heartbeat_interval then (

ocaml/xenopsd/cli/dune

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
xapi-idl.xen.interface
2323
xapi-idl.xen.interface.types
2424
xapi-stdext-pervasives
25+
xapi-stdext-unix
2526
yojson
2627
)
2728
(preprocess (per_module ((pps ppx_deriving_rpc) Common Xn_cfg_types)))

ocaml/xenopsd/cli/xn.ml

+3-1
Original file line numberDiff line numberDiff line change
@@ -959,7 +959,9 @@ let raw_console_proxy sockaddr =
959959
) else if !final then
960960
finished := true
961961
else
962-
let r, _, _ = Unix.select [Unix.stdin; fd] [] [] (-1.) in
962+
let r, _, _ =
963+
Xapi_stdext_unix.Unixext.select [Unix.stdin; fd] [] [] (-1.)
964+
in
963965
if List.mem Unix.stdin r then (
964966
let b =
965967
Unix.read Unix.stdin buf_remote !buf_remote_end

ocaml/xsh/dune

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
safe-resources
1010
xapi-consts
1111
xapi-log
12+
xapi-stdext-unix
1213
)
1314
)
1415

0 commit comments

Comments
 (0)