1(* 2 * Copyright (C) 2006-2007 XenSource Ltd. 3 * Copyright (C) 2008 Citrix Ltd. 4 * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU Lesser General Public License as published 8 * by the Free Software Foundation; version 2.1 only. with the special 9 * exception on linking described in file LICENSE. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU Lesser General Public License for more details. 15 *) 16 17let debug fmt = Logging.debug "domains" fmt 18let error fmt = Logging.error "domains" fmt 19let warn fmt = Logging.warn "domains" fmt 20 21type domains = { 22 eventchn: Event.t; 23 table: (Xenctrl.domid, Domain.t) Hashtbl.t; 24 25 (* N.B. the Queue module is not thread-safe but oxenstored is single-threaded. *) 26 (* Domains queue up to regain conflict-credit; we have a queue for 27 domains that are carrying some penalty and so are below the 28 maximum credit, and another queue for domains that have run out of 29 credit and so have had their access paused. *) 30 doms_conflict_paused: (Domain.t option ref) Queue.t; 31 doms_with_conflict_penalty: (Domain.t option ref) Queue.t; 32 33 (* A callback function to be called when we go from zero to one paused domain. 34 This will be to reset the countdown until the next unit of credit is issued. *) 35 on_first_conflict_pause: unit -> unit; 36 37 (* If config is set to use individual instead of aggregate conflict-rate-limiting, 38 we use these counts instead of the queues. The second one includes the first. *) 39 mutable n_paused: int; (* Number of domains with zero or negative credit *) 40 mutable n_penalised: int; (* Number of domains with less than maximum credit *) 41} 42 43let init eventchn on_first_conflict_pause = { 44 eventchn = eventchn; 45 table = Hashtbl.create 10; 46 doms_conflict_paused = Queue.create (); 47 doms_with_conflict_penalty = Queue.create (); 48 on_first_conflict_pause = on_first_conflict_pause; 49 n_paused = 0; 50 n_penalised = 0; 51} 52let del doms id = Hashtbl.remove doms.table id 53let exist doms id = Hashtbl.mem doms.table id 54let find doms id = Hashtbl.find doms.table id 55let number doms = Hashtbl.length doms.table 56let iter doms fct = Hashtbl.iter (fun _ b -> fct b) doms.table 57 58let rec is_empty_queue q = 59 Queue.is_empty q || 60 if !(Queue.peek q) = None 61 then ( 62 ignore (Queue.pop q); 63 is_empty_queue q 64 ) else false 65 66let all_at_max_credit doms = 67 if !Define.conflict_rate_limit_is_aggregate 68 then 69 (* Check both becuase if burst limit is 1.0 then a domain can go straight 70 * from max-credit to paused without getting into the penalty queue. *) 71 is_empty_queue doms.doms_with_conflict_penalty 72 && is_empty_queue doms.doms_conflict_paused 73 else doms.n_penalised = 0 74 75(* Functions to handle queues of domains given that the domain might be deleted while in a queue. *) 76let push dom queue = 77 Queue.push (ref (Some dom)) queue 78 79let rec pop queue = 80 match !(Queue.pop queue) with 81 | None -> pop queue 82 | Some x -> x 83 84let remove_from_queue dom queue = 85 Queue.iter (fun d -> match !d with 86 | None -> () 87 | Some x -> if x=dom then d := None) queue 88 89let cleanup xc doms = 90 let notify = ref false in 91 let dead_dom = ref [] in 92 93 Hashtbl.iter (fun id _ -> if id <> 0 then 94 try 95 let info = Xenctrl.domain_getinfo xc id in 96 if info.Xenctrl.shutdown || info.Xenctrl.dying then ( 97 debug "Domain %u died (dying=%b, shutdown %b -- code %d)" 98 id info.Xenctrl.dying info.Xenctrl.shutdown info.Xenctrl.shutdown_code; 99 if info.Xenctrl.dying then 100 dead_dom := id :: !dead_dom 101 else 102 notify := true; 103 ) 104 with Xenctrl.Error _ -> 105 debug "Domain %u died -- no domain info" id; 106 dead_dom := id :: !dead_dom; 107 ) doms.table; 108 List.iter (fun id -> 109 let dom = Hashtbl.find doms.table id in 110 Domain.close dom; 111 Hashtbl.remove doms.table id; 112 if dom.Domain.conflict_credit <= !Define.conflict_burst_limit 113 then ( 114 remove_from_queue dom doms.doms_with_conflict_penalty; 115 if (dom.Domain.conflict_credit <= 0.) then remove_from_queue dom doms.doms_conflict_paused 116 ) 117 ) !dead_dom; 118 !notify, !dead_dom 119 120let resume doms domid = 121 () 122 123let create xc doms domid mfn port = 124 let interface = Xenctrl.map_foreign_range xc domid (Xenmmap.getpagesize()) mfn in 125 let dom = Domain.make domid mfn port interface doms.eventchn in 126 Hashtbl.add doms.table domid dom; 127 Domain.bind_interdomain dom; 128 dom 129 130let xenstored_kva = ref "" 131let xenstored_port = ref "" 132 133let create0 doms = 134 let port, interface = 135 ( 136 let port = Utils.read_file_single_integer !xenstored_port 137 and fd = Unix.openfile !xenstored_kva 138 [ Unix.O_RDWR ] 0o600 in 139 let interface = Xenmmap.mmap fd Xenmmap.RDWR Xenmmap.SHARED 140 (Xenmmap.getpagesize()) 0 in 141 Unix.close fd; 142 port, interface 143 ) 144 in 145 let dom = Domain.make 0 Nativeint.zero port interface doms.eventchn in 146 Hashtbl.add doms.table 0 dom; 147 Domain.bind_interdomain dom; 148 Domain.notify dom; 149 dom 150 151let decr_conflict_credit doms dom = 152 dom.Domain.caused_conflicts <- Int64.add 1L dom.Domain.caused_conflicts; 153 let before = dom.Domain.conflict_credit in 154 let after = max (-1.0) (before -. 1.0) in 155 debug "decr_conflict_credit dom%d %F -> %F" (Domain.get_id dom) before after; 156 dom.Domain.conflict_credit <- after; 157 let newly_penalised = 158 before >= !Define.conflict_burst_limit 159 && after < !Define.conflict_burst_limit in 160 let newly_paused = before > 0.0 && after <= 0.0 in 161 if !Define.conflict_rate_limit_is_aggregate then ( 162 if newly_penalised 163 && after > 0.0 164 then ( 165 push dom doms.doms_with_conflict_penalty 166 ) else if newly_paused 167 then ( 168 let first_pause = Queue.is_empty doms.doms_conflict_paused in 169 push dom doms.doms_conflict_paused; 170 if first_pause then doms.on_first_conflict_pause () 171 ) else ( 172 (* The queues are correct already: no further action needed. *) 173 ) 174 ) else ( 175 if newly_penalised then doms.n_penalised <- doms.n_penalised + 1; 176 if newly_paused then ( 177 doms.n_paused <- doms.n_paused + 1; 178 if doms.n_paused = 1 then doms.on_first_conflict_pause () 179 ) 180 ) 181 182(* Give one point of credit to one domain, and update the queues appropriately. *) 183let incr_conflict_credit_from_queue doms = 184 let process_queue q requeue_test = 185 let d = pop q in 186 let before = d.Domain.conflict_credit in (* just for debug-logging *) 187 d.Domain.conflict_credit <- min (d.Domain.conflict_credit +. 1.0) !Define.conflict_burst_limit; 188 debug "incr_conflict_credit_from_queue: dom%d: %F -> %F" (Domain.get_id d) before d.Domain.conflict_credit; 189 if requeue_test d.Domain.conflict_credit then ( 190 push d q (* Make it queue up again for its next point of credit. *) 191 ) 192 in 193 let paused_queue_test cred = cred <= 0.0 in 194 let penalty_queue_test cred = cred < !Define.conflict_burst_limit in 195 try process_queue doms.doms_conflict_paused paused_queue_test 196 with Queue.Empty -> ( 197 try process_queue doms.doms_with_conflict_penalty penalty_queue_test 198 with Queue.Empty -> () (* Both queues are empty: nothing to do here. *) 199 ) 200 201let incr_conflict_credit doms = 202 if !Define.conflict_rate_limit_is_aggregate 203 then incr_conflict_credit_from_queue doms 204 else ( 205 (* Give a point of credit to every domain, subject only to the cap. *) 206 let inc dom = 207 let before = dom.Domain.conflict_credit in 208 let after = min (before +. 1.0) !Define.conflict_burst_limit in 209 dom.Domain.conflict_credit <- after; 210 debug "incr_conflict_credit dom%d: %F -> %F" (Domain.get_id dom) before after; 211 212 if before <= 0.0 && after > 0.0 213 then doms.n_paused <- doms.n_paused - 1; 214 215 if before < !Define.conflict_burst_limit 216 && after >= !Define.conflict_burst_limit 217 then doms.n_penalised <- doms.n_penalised - 1 218 in 219 if doms.n_penalised > 0 then iter doms inc 220 ) 221