|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH 5/8] tools/oxenstored: Keep /dev/xen/evtchn open across live update
> On 22 Nov 2022, at 15:20, Andrew Cooper <andrew.cooper3@xxxxxxxxxx> wrote:
>
> From: Edwin Török <edvin.torok@xxxxxxxxxx>
>
> Closing the evtchn handle will unbind and free all local ports. The new
> xenstored would need to rebind all evtchns, which is work that we don't want
> or need to be doing during the critical handover period.
>
> However, it turns out that the Windows PV drivers also rebind their local port
> too across suspend/resume, leaving (o)xenstored with a stale idea of the
> remote port to use. In this case, reusing the established connection is the
> only robust option.
>
> Therefore:
> * Have oxenstored open /dev/xen/evtchn without CLOEXEC at start of day
> * Extend the handover information with the evtchn fd, and the local port
> number for each domain connection.
> * Have (the new) oxenstored recover the open handle using Xeneventchn.fdopen,
> and use the provided local ports rather than trying to rebind them.
>
> When this new information isn't present (i.e. live updating from an oxenstored
> prior to this change), the best-effort status quo will have to do.
>
> Signed-off-by: Edwin Török <edvin.torok@xxxxxxxxxx>
> Signed-off-by: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
> ---
> CC: Christian Lindig <christian.lindig@xxxxxxxxxx>
> CC: David Scott <dave@xxxxxxxxxx>
> CC: Edwin Torok <edvin.torok@xxxxxxxxxx>
> CC: Rob Hoes <Rob.Hoes@xxxxxxxxxx>
>
Acked-by: Christian Lindig <christian.lindig@xxxxxxxxxx>
Nothing stands out for me. But this is obviously delicate in support of a
delicate feature, which is live update. I commented before that the commend
line gets increasingly crowded.
— C
> Merge two patches to retain bisectability. Drop changes to the evtchn
> bindings.
> ---
> tools/ocaml/xenstored/domain.ml | 6 ++-
> tools/ocaml/xenstored/domains.ml | 14 +++++--
> tools/ocaml/xenstored/event.ml | 8 +++-
> tools/ocaml/xenstored/xenstored.ml | 82 ++++++++++++++++++++++++++------------
> 4 files changed, 78 insertions(+), 32 deletions(-)
>
> diff --git a/tools/ocaml/xenstored/domain.ml b/tools/ocaml/xenstored/domain.ml
> index 81cb59b8f1a2..527035ffdd32 100644
> --- a/tools/ocaml/xenstored/domain.ml
> +++ b/tools/ocaml/xenstored/domain.ml
> @@ -61,7 +61,7 @@ let string_of_port = function
> | Some x -> string_of_int (Xeneventchn.to_int x)
>
> let dump d chan =
> - fprintf chan "dom,%d,%nd,%d\n" d.id d.mfn d.remote_port
> + fprintf chan "dom,%d,%nd,%d,%s\n" d.id d.mfn d.remote_port
> (string_of_port d.port)
>
> let notify dom = match dom.port with
> | None ->
> @@ -77,6 +77,10 @@ let bind_interdomain dom =
> dom.port <- Some (Event.bind_interdomain dom.eventchn dom.id
> dom.remote_port);
> debug "bound domain %d remote port %d to local port %s" dom.id
> dom.remote_port (string_of_port dom.port)
>
> +let restore_interdomain dom localport =
> + assert (dom.port = None);
> + dom.port <- Some (Xeneventchn.of_int localport);
> + debug "restored interdomain %d remote port %d to local port %s" dom.id
> dom.remote_port (string_of_port dom.port)
>
> let close dom =
> debug "domain %d unbound port %s" dom.id (string_of_port dom.port);
> diff --git a/tools/ocaml/xenstored/domains.ml
> b/tools/ocaml/xenstored/domains.ml
> index 17fe2fa25772..a91d2afd2a82 100644
> --- a/tools/ocaml/xenstored/domains.ml
> +++ b/tools/ocaml/xenstored/domains.ml
> @@ -56,6 +56,7 @@ let exist doms id = Hashtbl.mem doms.table id
> let find doms id = Hashtbl.find doms.table id
> let number doms = Hashtbl.length doms.table
> let iter doms fct = Hashtbl.iter (fun _ b -> fct b) doms.table
> +let eventchn doms = doms.eventchn
>
> let rec is_empty_queue q =
> Queue.is_empty q ||
> @@ -122,17 +123,22 @@ let cleanup doms =
> let resume _doms _domid =
> ()
>
> -let create doms domid mfn port =
> +let maybe_bind_interdomain restore_localport dom =
> + match restore_localport with
> + | None -> Domain.bind_interdomain dom
> + | Some p -> Domain.restore_interdomain dom p
> +
> +let create doms domid mfn ?restore_localport port =
> let interface = Xenctrl.map_foreign_range xc domid
> (Xenmmap.getpagesize()) mfn in
> let dom = Domain.make domid mfn port interface doms.eventchn in
> Hashtbl.add doms.table domid dom;
> - Domain.bind_interdomain dom;
> + maybe_bind_interdomain restore_localport dom;
> dom
>
> let xenstored_kva = ref ""
> let xenstored_port = ref ""
>
> -let create0 doms =
> +let create0 ?restore_localport doms =
> let port, interface =
> (
> let port = Utils.read_file_single_integer
> !xenstored_port
> @@ -146,7 +152,7 @@ let create0 doms =
> in
> let dom = Domain.make 0 Nativeint.zero port interface doms.eventchn in
> Hashtbl.add doms.table 0 dom;
> - Domain.bind_interdomain dom;
> + maybe_bind_interdomain restore_localport dom;
> Domain.notify dom;
> dom
>
> diff --git a/tools/ocaml/xenstored/event.ml b/tools/ocaml/xenstored/event.ml
> index ccca90b6fc4f..0159daac91f4 100644
> --- a/tools/ocaml/xenstored/event.ml
> +++ b/tools/ocaml/xenstored/event.ml
> @@ -20,7 +20,13 @@ type t = {
> mutable virq_port: Xeneventchn.t option;
> }
>
> -let init () = { handle = Xeneventchn.init (); virq_port = None; }
> +let init ?fd () =
> + let handle = match fd with
> + | None -> Xeneventchn.init ~cloexec:false ()
> + | Some fd -> Xeneventchn.fdopen fd
> + in
> + { handle; virq_port = None }
> +
> let fd eventchn = Xeneventchn.fd eventchn.handle
> let bind_dom_exc_virq eventchn = eventchn.virq_port <- Some
> (Xeneventchn.bind_dom_exc_virq eventchn.handle)
> let bind_interdomain eventchn domid port = Xeneventchn.bind_interdomain
> eventchn.handle domid port
> diff --git a/tools/ocaml/xenstored/xenstored.ml
> b/tools/ocaml/xenstored/xenstored.ml
> index c5dc7a28d082..6ceab02dee1e 100644
> --- a/tools/ocaml/xenstored/xenstored.ml
> +++ b/tools/ocaml/xenstored/xenstored.ml
> @@ -144,7 +144,7 @@ exception Bad_format of string
>
> let dump_format_header = "$xenstored-dump-format"
>
> -let from_channel_f chan global_f socket_f domain_f watch_f store_f =
> +let from_channel_f chan global_f event_f socket_f domain_f watch_f store_f =
> let unhexify s = Utils.unhexify s in
> let getpath s =
> let u = Utils.unhexify s in
> @@ -165,12 +165,17 @@ let from_channel_f chan global_f socket_f domain_f
> watch_f store_f =
> (* there might be more parameters here,
> e.g. a RO socket from a previous
> version: ignore it *)
> global_f ~rw
> + | "eventfd" :: eventfd :: [] ->
> + event_f ~eventfd
> | "socket" :: fd :: [] ->
> socket_f ~fd:(int_of_string fd)
> - | "dom" :: domid :: mfn :: port :: []->
> + | "dom" :: domid :: mfn :: port :: rest ->
> domain_f (int_of_string domid)
> (Nativeint.of_string mfn)
> (int_of_string port)
> + (match rest with
> + | [] -> None (* backward
> compat: old version didn't have it *)
> + | localport :: _ -> Some
> (int_of_string localport))
> | "watch" :: domid :: path :: token :: [] ->
> watch_f (int_of_string domid)
> (unhexify path) (unhexify token)
> @@ -189,10 +194,27 @@ let from_channel_f chan global_f socket_f domain_f
> watch_f store_f =
> done;
> info "Completed loading xenstore dump"
>
> -let from_channel store cons doms chan =
> +let from_channel store cons createdoms chan =
> (* don't let the permission get on our way, full perm ! *)
> let op = Store.get_ops store Perms.Connection.full_rights in
> let rwro = ref (None) in
> + let eventchnfd = ref (None) in
No parenthesis required: "ref None” would be enough. But don’t bother - once we
use OCamlformat instances like these will be picked up.
> + let doms = ref (None) in
> +
> + let require_doms () =
> + match !doms with
> + | None ->
Alternative could be:
| None when !eventchnfd = None ->
...
| None ->
...
| Some d -> d
> + let missing_eventchnfd = !eventchnfd = None in
> + if missing_eventchnfd then
> + warn "No event channel file descriptor
> available in dump!";
> + let eventchn = Event.init ?fd:!eventchnfd () in
> + let domains = createdoms eventchn in
> + if missing_eventchnfd then
> + Event.bind_dom_exc_virq eventchn;
> + doms := Some domains;
> + domains
> + | Some d -> d
> + in
> let global_f ~rw =
> let get_listen_sock sockfd =
> let fd = sockfd |> int_of_string |> Utils.FD.of_int in
> @@ -201,6 +223,10 @@ let from_channel store cons doms chan =
> in
> rwro := get_listen_sock rw
> in
> + let event_f ~eventfd =
> + let fd = eventfd |> int_of_string |> Utils.FD.of_int in
> + eventchnfd := Some fd
> + in
> let socket_f ~fd =
> let ufd = Utils.FD.of_int fd in
> let is_valid = try (Unix.fstat ufd).Unix.st_kind = Unix.S_SOCK
> with _ -> false in
> @@ -209,12 +235,13 @@ let from_channel store cons doms chan =
> else
> warn "Ignoring invalid socket FD %d" fd
> in
> - let domain_f domid mfn port =
> + let domain_f domid mfn port restore_localport =
> + let doms = require_doms () in
> let ndom =
> if domid > 0 then
> - Domains.create doms domid mfn port
> + Domains.create doms domid mfn
> ?restore_localport port
> else
> - Domains.create0 doms
> + Domains.create0 ?restore_localport doms
> in
> Connections.add_domain cons ndom;
> in
> @@ -229,8 +256,8 @@ let from_channel store cons doms chan =
> op.Store.write path value;
> op.Store.setperms path perms
> in
> - from_channel_f chan global_f socket_f domain_f watch_f store_f;
> - !rwro
> + from_channel_f chan global_f event_f socket_f domain_f watch_f store_f;
> + !rwro, require_doms ()
>
> let from_file store cons doms file =
> info "Loading xenstore dump from %s" file;
> @@ -238,7 +265,7 @@ let from_file store cons doms file =
> finally (fun () -> from_channel store doms cons channel)
> (fun () -> close_in channel)
>
> -let to_channel store cons rw chan =
> +let to_channel store cons (rw, eventchn) chan =
> let hexify s = Utils.hexify s in
>
> fprintf chan "%s\n" dump_format_header;
> @@ -247,6 +274,7 @@ let to_channel store cons rw chan =
> Unix.clear_close_on_exec fd;
> Utils.FD.to_int fd in
> fprintf chan "global,%d\n" (fdopt rw);
> + fprintf chan "eventchnfd,%d\n" (Utils.FD.to_int @@ Event.fd eventchn);
>
> (* dump connections related to domains: domid, mfn, eventchn port/
> sockets, and watches *)
> Connections.iter cons (fun con -> Connection.dump con chan);
> @@ -367,7 +395,6 @@ let _ =
> | None -> () end;
>
> let store = Store.create () in
> - let eventchn = Event.init () in
> let next_frequent_ops = ref 0. in
> let advance_next_frequent_ops () =
> next_frequent_ops := (Unix.gettimeofday () +.
> !Define.conflict_max_history_seconds)
> @@ -375,16 +402,8 @@ let _ =
> let delay_next_frequent_ops_by duration =
> next_frequent_ops := !next_frequent_ops +. duration
> in
> - let domains = Domains.init eventchn advance_next_frequent_ops in
> + let domains eventchn = Domains.init eventchn advance_next_frequent_ops
> in
>
> - (* For things that need to be done periodically but more often
> - * than the periodic_ops function *)
> - let frequent_ops () =
> - if Unix.gettimeofday () > !next_frequent_ops then (
> - History.trim ();
> - Domains.incr_conflict_credit domains;
> - advance_next_frequent_ops ()
> - ) in
> let cons = Connections.create () in
>
> let quit = ref false in
> @@ -393,15 +412,15 @@ let _ =
> List.iter (fun path ->
> Store.write store Perms.Connection.full_rights path "")
> Store.Path.specials;
>
> - let rw_sock =
> + let rw_sock, domains =
> if cf.restart && Sys.file_exists Disk.xs_daemon_database then (
> - let rwro = DB.from_file store domains cons
> Disk.xs_daemon_database in
> + let rw, domains = DB.from_file store domains cons
> Disk.xs_daemon_database in
> info "Live reload: database loaded";
> - Event.bind_dom_exc_virq eventchn;
> Process.LiveUpdate.completed ();
> - rwro
> + rw, domains
> ) else (
> info "No live reload: regular startup";
> + let domains = domains @@ Event.init () in
> if !Disk.enable then (
> info "reading store from disk";
> Disk.read store
> @@ -411,13 +430,23 @@ let _ =
> if not (Store.path_exists store localpath) then
> Store.mkdir store (Perms.Connection.create 0) localpath;
>
> + let eventchn = Event.init () in
> if cf.domain_init then (
> Connections.add_domain cons (Domains.create0 domains);
> Event.bind_dom_exc_virq eventchn
> );
> - rw_sock
> + rw_sock, domains
> ) in
>
> + (* For things that need to be done periodically but more often
> + * than the periodic_ops function *)
> + let frequent_ops () =
> + if Unix.gettimeofday () > !next_frequent_ops then (
> + History.trim ();
> + Domains.incr_conflict_credit domains;
> + advance_next_frequent_ops ()
> + ) in
> +
> (* required for xenstore-control to detect availability of live-update
> *)
> let tool_path = Store.Path.of_string "/tool" in
> if not (Store.path_exists store tool_path) then
> @@ -433,10 +462,11 @@ let _ =
> Sys.set_signal Sys.sigpipe Sys.Signal_ignore;
>
> if cf.activate_access_log then begin
> - let post_rotate () = DB.to_file store cons (None)
> Disk.xs_daemon_database in
> + let post_rotate () = DB.to_file store cons (None,
> Domains.eventchn domains) Disk.xs_daemon_database in
> Logging.init_access_log post_rotate
> end;
>
> + let eventchn = Domains.eventchn domains in
> let spec_fds =
> (match rw_sock with None -> [] | Some x -> [ x ]) @
> (if cf.domain_init then [ Event.fd eventchn ] else [])
> @@ -595,7 +625,7 @@ let _ =
> live_update := Process.LiveUpdate.should_run cons;
> if !live_update || !quit then begin
> (* don't initiate live update if saving state
> fails *)
> - DB.to_file store cons (rw_sock)
> Disk.xs_daemon_database;
> + DB.to_file store cons (rw_sock, eventchn)
> Disk.xs_daemon_database;
> quit := true;
> end
> with exc ->
> --
> 2.11.0
>
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |