mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
Also run 'make format' on the file to reformat an adjacent block of code correctly. Signed-off-by: Andrii Sultanov <andrii.sultanov@cloud.com> Acked-by: Christian Lindig <christian.lindig@cloud.com> Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
247 lines
9.0 KiB
OCaml
247 lines
9.0 KiB
OCaml
(*
|
|
* Copyright (C) 2006-2007 XenSource Ltd.
|
|
* Copyright (C) 2008 Citrix Ltd.
|
|
* Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published
|
|
* by the Free Software Foundation; version 2.1 only. with the special
|
|
* exception on linking described in file LICENSE.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*)
|
|
|
|
let debug fmt = Logging.debug "domains" fmt
|
|
let error fmt = Logging.error "domains" fmt
|
|
let warn fmt = Logging.warn "domains" fmt
|
|
|
|
let xc = Xenctrl.interface_open ()
|
|
|
|
let load_plug fname =
|
|
let fail_with fmt = Printf.ksprintf failwith fmt in
|
|
let fname = Dynlink.adapt_filename fname in
|
|
if Sys.file_exists fname then
|
|
try Dynlink.loadfile fname with
|
|
| Dynlink.Error err as e ->
|
|
error "ERROR loading plugin '%s': %s\n%!" fname
|
|
(Dynlink.error_message err);
|
|
raise e
|
|
| _ -> fail_with "Unknown error while loading plugin"
|
|
else fail_with "Plugin file '%s' does not exist" fname
|
|
|
|
let () =
|
|
let plugins_dir = Paths.libexec ^ "/ocaml/xsd_glue/xenctrl_plugin/" in
|
|
let filepath = plugins_dir ^ "domain_getinfo_v1.cmxs" in
|
|
debug "Trying to load plugin '%s'\n%!" filepath;
|
|
let list_files = Sys.readdir plugins_dir in
|
|
debug "Directory listing of '%s'\n%!" plugins_dir ;
|
|
Array.iter (fun x -> debug "\t%s\n%!" x) list_files;
|
|
Dynlink.allow_only [ "Plugin_interface_v1" ];
|
|
load_plug filepath
|
|
|
|
module Plugin =
|
|
(val Plugin_interface_v1.get_plugin_v1 ()
|
|
: Plugin_interface_v1.Domain_getinfo_V1)
|
|
|
|
let handle = Plugin.interface_open ()
|
|
|
|
type domains = {
|
|
eventchn : Event.t;
|
|
table : (Plugin.domid, Domain.t) Hashtbl.t;
|
|
|
|
(* N.B. the Queue module is not thread-safe but oxenstored is single-threaded. *)
|
|
(* Domains queue up to regain conflict-credit; we have a queue for
|
|
domains that are carrying some penalty and so are below the
|
|
maximum credit, and another queue for domains that have run out of
|
|
credit and so have had their access paused. *)
|
|
doms_conflict_paused: (Domain.t option ref) Queue.t;
|
|
doms_with_conflict_penalty: (Domain.t option ref) Queue.t;
|
|
|
|
(* A callback function to be called when we go from zero to one paused domain.
|
|
This will be to reset the countdown until the next unit of credit is issued. *)
|
|
on_first_conflict_pause: unit -> unit;
|
|
|
|
(* If config is set to use individual instead of aggregate conflict-rate-limiting,
|
|
we use these counts instead of the queues. The second one includes the first. *)
|
|
mutable n_paused: int; (* Number of domains with zero or negative credit *)
|
|
mutable n_penalised: int; (* Number of domains with less than maximum credit *)
|
|
}
|
|
|
|
let init eventchn on_first_conflict_pause = {
|
|
eventchn = eventchn;
|
|
table = Hashtbl.create 10;
|
|
doms_conflict_paused = Queue.create ();
|
|
doms_with_conflict_penalty = Queue.create ();
|
|
on_first_conflict_pause = on_first_conflict_pause;
|
|
n_paused = 0;
|
|
n_penalised = 0;
|
|
}
|
|
let del doms id = Hashtbl.remove doms.table id
|
|
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 ||
|
|
if !(Queue.peek q) = None
|
|
then (
|
|
ignore (Queue.pop q);
|
|
is_empty_queue q
|
|
) else false
|
|
|
|
let all_at_max_credit doms =
|
|
if !Define.conflict_rate_limit_is_aggregate
|
|
then
|
|
(* Check both becuase if burst limit is 1.0 then a domain can go straight
|
|
* from max-credit to paused without getting into the penalty queue. *)
|
|
is_empty_queue doms.doms_with_conflict_penalty
|
|
&& is_empty_queue doms.doms_conflict_paused
|
|
else doms.n_penalised = 0
|
|
|
|
(* Functions to handle queues of domains given that the domain might be deleted while in a queue. *)
|
|
let push dom queue =
|
|
Queue.push (ref (Some dom)) queue
|
|
|
|
let rec pop queue =
|
|
match !(Queue.pop queue) with
|
|
| None -> pop queue
|
|
| Some x -> x
|
|
|
|
let remove_from_queue dom queue =
|
|
Queue.iter (fun d -> match !d with
|
|
| None -> ()
|
|
| Some x -> if x=dom then d := None) queue
|
|
|
|
let cleanup doms =
|
|
let notify = ref false in
|
|
let dead_dom = ref [] in
|
|
|
|
Hashtbl.iter
|
|
(fun id _ ->
|
|
if id <> 0 then (
|
|
try
|
|
let info = Plugin.domain_getinfo handle id in
|
|
if info.Plugin.shutdown || info.Plugin.dying then (
|
|
debug "Domain %u died (dying=%b, shutdown %b -- code %d)" id
|
|
info.Plugin.dying info.Plugin.shutdown info.Plugin.shutdown_code;
|
|
if info.Plugin.dying then dead_dom := id :: !dead_dom else notify := true)
|
|
with Plugin.Error _ ->
|
|
debug "Domain %u died -- no domain info" id;
|
|
dead_dom := id :: !dead_dom))
|
|
doms.table;
|
|
List.iter
|
|
(fun id ->
|
|
let dom = Hashtbl.find doms.table id in
|
|
Domain.close dom;
|
|
Hashtbl.remove doms.table id;
|
|
if dom.Domain.conflict_credit <= !Define.conflict_burst_limit
|
|
then (
|
|
remove_from_queue dom doms.doms_with_conflict_penalty;
|
|
if (dom.Domain.conflict_credit <= 0.) then remove_from_queue dom doms.doms_conflict_paused
|
|
)
|
|
) !dead_dom;
|
|
!notify, !dead_dom
|
|
|
|
let resume _doms _domid =
|
|
()
|
|
|
|
let create doms ?local_port ~remote_port domid mfn =
|
|
let interface = Xenctrl.map_foreign_range xc domid (Xenmmap.getpagesize()) mfn in
|
|
let dom = Domain.make ?local_port ~remote_port domid mfn interface doms.eventchn in
|
|
Hashtbl.add doms.table domid dom;
|
|
dom
|
|
|
|
let xenstored_kva = ref ""
|
|
let xenstored_port = ref ""
|
|
|
|
let create0 ?local_port doms =
|
|
let remote_port = Utils.read_file_single_integer !xenstored_port in
|
|
|
|
let interface =
|
|
let fd = Unix.openfile !xenstored_kva [ Unix.O_RDWR ] 0o600 in
|
|
let interface = Xenmmap.mmap fd Xenmmap.RDWR Xenmmap.SHARED (Xenmmap.getpagesize()) 0 in
|
|
Unix.close fd;
|
|
interface
|
|
in
|
|
|
|
let dom = Domain.make ?local_port ~remote_port 0 Nativeint.zero interface doms.eventchn in
|
|
Hashtbl.add doms.table 0 dom;
|
|
Domain.notify dom;
|
|
dom
|
|
|
|
let decr_conflict_credit doms dom =
|
|
dom.Domain.caused_conflicts <- Int64.add 1L dom.Domain.caused_conflicts;
|
|
let before = dom.Domain.conflict_credit in
|
|
let after = max (-1.0) (before -. 1.0) in
|
|
debug "decr_conflict_credit dom%d %F -> %F" (Domain.get_id dom) before after;
|
|
dom.Domain.conflict_credit <- after;
|
|
let newly_penalised =
|
|
before >= !Define.conflict_burst_limit
|
|
&& after < !Define.conflict_burst_limit in
|
|
let newly_paused = before > 0.0 && after <= 0.0 in
|
|
if !Define.conflict_rate_limit_is_aggregate then (
|
|
if newly_penalised
|
|
&& after > 0.0
|
|
then (
|
|
push dom doms.doms_with_conflict_penalty
|
|
) else if newly_paused
|
|
then (
|
|
let first_pause = Queue.is_empty doms.doms_conflict_paused in
|
|
push dom doms.doms_conflict_paused;
|
|
if first_pause then doms.on_first_conflict_pause ()
|
|
) else (
|
|
(* The queues are correct already: no further action needed. *)
|
|
)
|
|
) else (
|
|
if newly_penalised then doms.n_penalised <- doms.n_penalised + 1;
|
|
if newly_paused then (
|
|
doms.n_paused <- doms.n_paused + 1;
|
|
if doms.n_paused = 1 then doms.on_first_conflict_pause ()
|
|
)
|
|
)
|
|
|
|
(* Give one point of credit to one domain, and update the queues appropriately. *)
|
|
let incr_conflict_credit_from_queue doms =
|
|
let process_queue q requeue_test =
|
|
let d = pop q in
|
|
let before = d.Domain.conflict_credit in (* just for debug-logging *)
|
|
d.Domain.conflict_credit <- min (d.Domain.conflict_credit +. 1.0) !Define.conflict_burst_limit;
|
|
debug "incr_conflict_credit_from_queue: dom%d: %F -> %F" (Domain.get_id d) before d.Domain.conflict_credit;
|
|
if requeue_test d.Domain.conflict_credit then (
|
|
push d q (* Make it queue up again for its next point of credit. *)
|
|
)
|
|
in
|
|
let paused_queue_test cred = cred <= 0.0 in
|
|
let penalty_queue_test cred = cred < !Define.conflict_burst_limit in
|
|
try process_queue doms.doms_conflict_paused paused_queue_test
|
|
with Queue.Empty -> (
|
|
try process_queue doms.doms_with_conflict_penalty penalty_queue_test
|
|
with Queue.Empty -> () (* Both queues are empty: nothing to do here. *)
|
|
)
|
|
|
|
let incr_conflict_credit doms =
|
|
if !Define.conflict_rate_limit_is_aggregate
|
|
then incr_conflict_credit_from_queue doms
|
|
else (
|
|
(* Give a point of credit to every domain, subject only to the cap. *)
|
|
let inc dom =
|
|
let before = dom.Domain.conflict_credit in
|
|
let after = min (before +. 1.0) !Define.conflict_burst_limit in
|
|
dom.Domain.conflict_credit <- after;
|
|
debug "incr_conflict_credit dom%d: %F -> %F" (Domain.get_id dom) before after;
|
|
|
|
if before <= 0.0 && after > 0.0
|
|
then doms.n_paused <- doms.n_paused - 1;
|
|
|
|
if before < !Define.conflict_burst_limit
|
|
&& after >= !Define.conflict_burst_limit
|
|
then doms.n_penalised <- doms.n_penalised - 1
|
|
in
|
|
if doms.n_penalised > 0 then iter doms inc
|
|
)
|