Skip to content

Commit 715fbd9

Browse files
simonjbeaumontjohnelse
authored andcommitted
CP-5845: Use policy when choosing VM host and pGPU
At the pool-level, hosts are split into groups of equally-optimal hosts (based on GPU_group.allocation_algorithm). The VM placement code is then run sequentially on each of these groups (cf. was run before on all hosts in pool). Only when exhausing this list of lists of hosts does it fail to start the VM. At the host-level, a pGPU is selected according to the same allocation_algorithm. If no capacity can be found then the VM_REQUIRES_VGPU error is raised. Signed-off-by: Si Beaumont <[email protected]>
1 parent 11e8f14 commit 715fbd9

File tree

3 files changed

+96
-2
lines changed

3 files changed

+96
-2
lines changed

ocaml/xapi/helpers.ml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,40 @@ let compare_int_lists : int list -> int list -> int =
489489
let first_non_zero is = List.fold_left (fun a b -> if (a<>0) then a else b) 0 is in
490490
first_non_zero (List.map2 compare a b)
491491

492+
let group_by f list =
493+
let evaluated_list = List.map (fun x -> (x, f x)) list in
494+
let snd_equality (_, x) (_, y) = x = y in
495+
let snd_compare (_, x) (_, y) = compare x y in
496+
let sorted = List.sort snd_compare evaluated_list in
497+
let rec take_while p ac = function
498+
| [] -> (ac, [])
499+
| x :: xs ->
500+
if (p x) then take_while p (x :: ac) xs
501+
else (ac, x :: xs)
502+
in
503+
let rec group ac = function
504+
| [] -> ac
505+
| x :: xs ->
506+
let peers, rest = take_while (snd_equality x) [] (x :: xs) in
507+
group (peers :: ac) rest
508+
in
509+
group [] sorted
510+
511+
(** Groups list elements by equality of result of function application sorted
512+
* in order of that result *)
513+
let group_by ?(descending=false) f list =
514+
match descending with
515+
| true -> group_by f list
516+
| false -> List.rev (group_by f list)
517+
518+
(** Schwarzian transform sort *)
519+
let sort_by_schwarzian ?(descending=false) f list =
520+
let comp x y = if descending then compare y x else compare x y in
521+
let (|>) a f = f a in
522+
List.map (fun x -> (x, f x)) list |>
523+
List.sort (fun (_, x') (_, y') -> comp x' y') |>
524+
List.map (fun (x, _) -> x)
525+
492526
let version_string_of : __context:Context.t -> [`host] api_object -> string =
493527
fun ~__context host ->
494528
try

ocaml/xapi/vgpuops.ml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,18 @@ let create_virtual_vgpu ~__context vm vgpu =
9292
let available_pgpus = Db.Host.get_PGPUs ~__context ~self:host in
9393
let compatible_pgpus = Db.GPU_group.get_PGPUs ~__context ~self:vgpu.gpu_group_ref in
9494
let pgpus = List.intersect compatible_pgpus available_pgpus in
95+
(* Sort the pgpus in lists of equal optimality for vGPU placement based on
96+
* the GPU groups allocation algorithm *)
97+
let sort_desc =
98+
match Db.GPU_group.get_allocation_algorithm ~__context ~self:vgpu.gpu_group_ref with
99+
| `depth_first -> false
100+
| `breadth_first -> true
101+
in
102+
let sorted_pgpus = Helpers.sort_by_schwarzian ~descending:sort_desc
103+
(fun pgpu -> Xapi_pgpu_helpers.get_remaining_capacity ~__context
104+
~self:pgpu ~vgpu_type:vgpu.type_ref)
105+
pgpus
106+
in
95107
let rec allocate_vgpu vgpu_type = function
96108
| [] -> None
97109
| pgpu :: remaining_pgpus ->
@@ -103,7 +115,7 @@ let create_virtual_vgpu ~__context vm vgpu =
103115
with _ -> allocate_vgpu vgpu_type remaining_pgpus
104116
in
105117
Threadext.Mutex.execute m (fun () ->
106-
match allocate_vgpu vgpu.type_ref pgpus with
118+
match allocate_vgpu vgpu.type_ref sorted_pgpus with
107119
| None ->
108120
raise (Api_errors.Server_error (Api_errors.vm_requires_vgpu, [
109121
Ref.string_of vm;

ocaml/xapi/xapi_vm_helpers.ml

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,55 @@ let choose_host_for_vm_no_wlb ~__context ~vm ~snapshot =
602602
let validate_host = vm_can_run_on_host __context vm snapshot true in
603603
let all_hosts = Db.Host.get_all ~__context in
604604
try
605-
Xapi_vm_placement.select_host __context vm validate_host all_hosts
605+
match Db.VM.get_VGPUs ~__context ~self:vm with
606+
| [] -> Xapi_vm_placement.select_host __context vm validate_host all_hosts
607+
| vgpu :: _ -> (* just considering first vgpu *)
608+
let vgpu_type = Db.VGPU.get_type ~__context ~self:vgpu in
609+
let gpu_group = Db.VGPU.get_GPU_group ~__context ~self:vgpu in
610+
if not (Xapi_gpu_group.get_remaining_capacity ~__context ~self:gpu_group ~vgpu_type > 0L)
611+
then raise (Api_errors.Server_error (Api_errors.vm_requires_vgpu,
612+
[Ref.string_of vm; Ref.string_of gpu_group; Ref.string_of vgpu_type]));
613+
let pgpus = Db.GPU_group.get_PGPUs ~__context ~self:gpu_group in
614+
let can_accomodate_vgpu pgpu =
615+
Xapi_pgpu_helpers.get_remaining_capacity ~__context ~self:pgpu
616+
~vgpu_type > 0L
617+
in
618+
let viable_pgpus = List.filter can_accomodate_vgpu pgpus in
619+
let viable_hosts = List.setify
620+
(List.map (fun pgpu -> Db.PGPU.get_host ~__context ~self:pgpu)
621+
viable_pgpus)
622+
in
623+
(* Group the hosts by pgpu-capacity and try to select a host based
624+
* on GPU group allocation algorithm *)
625+
let host_lists = Helpers.group_by
626+
(fun host ->
627+
let group_by_capacity pgpus =
628+
(match Db.GPU_group.get_allocation_algorithm ~__context ~self:gpu_group with
629+
| `depth_first -> Helpers.group_by ~descending:false
630+
| `breadth_first -> Helpers.group_by ~descending:true)
631+
(fun pgpu -> Xapi_pgpu_helpers.get_remaining_capacity ~__context ~self:pgpu ~vgpu_type)
632+
pgpus
633+
in
634+
let viable_resident_pgpus = List.filter
635+
(fun self -> Db.PGPU.get_host ~__context ~self = host)
636+
viable_pgpus
637+
in
638+
snd (List.hd (List.hd (group_by_capacity viable_resident_pgpus)))
639+
) viable_hosts
640+
in
641+
let rec select_host_from = function
642+
| [] -> raise (Api_errors.Server_error (Api_errors.no_hosts_available, []))
643+
| (hosts :: less_optimal_groups_of_hosts) ->
644+
let hosts = List.map (fun (h, c) -> h) hosts in
645+
debug "Attempting to start VM (%s) on one of equally optimal hosts [ %s ]"
646+
(Ref.string_of vm) (String.concat ";" (List.map Ref.string_of hosts));
647+
try Xapi_vm_placement.select_host __context vm validate_host hosts
648+
with _ ->
649+
info "Failed to start VM (%s) on any of [ %s ]"
650+
(Ref.string_of vm) (String.concat ";" (List.map Ref.string_of hosts));
651+
select_host_from less_optimal_groups_of_hosts
652+
in
653+
select_host_from host_lists
606654
with Api_errors.Server_error(x,[]) when x=Api_errors.no_hosts_available ->
607655
debug "No hosts guaranteed to satisfy VM constraints. Trying again ignoring memory checks";
608656
let validate_host = vm_can_run_on_host __context vm snapshot false in

0 commit comments

Comments
 (0)