Skip to content

Commit c7e0f7c

Browse files
authored
CP-54473 Configure max-cstate by XAPI (#6681)
C-states are power management states for CPUs where higher numbered states represent deeper sleep modes with lower power consumption but higher wake-up latency. The max_cstate parameter controls the deepest C-state that CPUs are allowed to enter. Common C-state values: - C0: CPU is active (not a sleep state) - C1: CPU is halted but can wake up almost instantly - C2: CPU caches are flushed, slightly longer wake-up time - C3+: Deeper sleep states with progressively longer wake-up times To set max_cstate on dom0 host, two commands are used: `xenpm` to set at runtime and `xen-cmdline` to set it in grub conf file to take effect after reboot. xenpm examples: ``` # xenpm set-max-cstate 0 0 max C-state set to C0 max C-substate set to 0 succeeded # xenpm set-max-cstate 0 max C-state set to C0 max C-substate set to unlimited succeeded # xenpm set-max-cstate unlimited max C-state set to unlimited # xenpm set-max-cstate -1 Missing, excess, or invalid argument(s) ``` xen-command-line examples: ``` /opt/xensource/libexec/xen-cmdline --get-xen max_cstate "" -> unlimited "max_cstate=N" -> max cstate N "max_cstate=N,M" -> max cstate N, max c-sub-state M *) /opt/xensource/libexec/xen-cmdline --set-xen max_cstate=1 /opt/xensource/libexec/xen-cmdline --set-xen max_cstate=1,0 /opt/xensource/libexec/xen-cmdline --delete-xen max_cstate ``` [xen-command-line.max_cstate](https://xenbits.xen.org/docs/unstable/misc/xen-command-line.html#max_cstate-x86). This PR adds a new field `host.max_cstate` to manage host's max_cstate. `host.set_max_cstate` use the two commands mentioned above to configure. While dbsync on xapi start, the filed will be synced by `xen-cmdline --get-xen max_cstate`
2 parents 5bafbbc + 3fcb233 commit c7e0f7c

File tree

11 files changed

+360
-3
lines changed

11 files changed

+360
-3
lines changed

ocaml/idl/datamodel_host.ml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2505,6 +2505,21 @@ let latest_synced_updates_applied_state =
25052505
]
25062506
)
25072507

2508+
let set_max_cstate =
2509+
call ~name:"set_max_cstate" ~lifecycle:[]
2510+
~doc:
2511+
"Sets xen's max-cstate on a host. See: \
2512+
https://xenbits.xen.org/docs/unstable/misc/xen-command-line.html#max_cstate-x86. \
2513+
\"\" means unlimited; \"N\" means limit to CN; \"N,M\" means limit to \
2514+
CN with max sub cstate M. Note: Only C0, C1, unlimited are supported \
2515+
currently."
2516+
~params:
2517+
[
2518+
(Ref _host, "self", "The host")
2519+
; (String, "value", "The max_cstate to apply to a host")
2520+
]
2521+
~allowed_roles:_R_POOL_OP ()
2522+
25082523
(** Hosts *)
25092524
let t =
25102525
create_obj ~in_db:true
@@ -2649,6 +2664,7 @@ let t =
26492664
; set_ssh_enabled_timeout
26502665
; set_console_idle_timeout
26512666
; set_ssh_auto_mode
2667+
; set_max_cstate
26522668
]
26532669
~contents:
26542670
([
@@ -3108,6 +3124,11 @@ let t =
31083124
~default_value:(Some (VBool Constants.default_ssh_auto_mode))
31093125
"ssh_auto_mode"
31103126
"Reflects whether SSH auto mode is enabled for the host"
3127+
; field ~qualifier:DynamicRO ~lifecycle:[] ~ty:String
3128+
~default_value:(Some (VString "")) "max_cstate"
3129+
"The maximum C-state that the host is allowed to enter, \"\" means \
3130+
unlimited; \"N\" means limit to CN; \"N,M\" means limit to CN \
3131+
with max sub cstate M."
31113132
]
31123133
)
31133134
()

ocaml/idl/schematest.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ let hash x = Digest.string x |> Digest.to_hex
33
(* BEWARE: if this changes, check that schema has been bumped accordingly in
44
ocaml/idl/datamodel_common.ml, usually schema_minor_vsn *)
55

6-
let last_known_schema_hash = "7586cb039918e573594fc358e90b0f04"
6+
let last_known_schema_hash = "79dc1d985749f6ec31d4460ec49b9029"
77

88
let current_schema_hash : string =
99
let open Datamodel_types in

ocaml/tests/common/test_common.ml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,8 @@ let make_host2 ~__context ?(ref = Ref.make ()) ?(uuid = make_uuid ())
219219
~recommended_guidances:[] ~latest_synced_updates_applied:`unknown
220220
~pending_guidances_recommended:[] ~pending_guidances_full:[]
221221
~last_update_hash:"" ~ssh_enabled:true ~ssh_enabled_timeout:0L
222-
~ssh_expiry:Date.epoch ~console_idle_timeout:0L ~ssh_auto_mode:false ;
222+
~ssh_expiry:Date.epoch ~console_idle_timeout:0L ~ssh_auto_mode:false
223+
~max_cstate:"" ;
223224
ref
224225

225226
let make_pif ~__context ~network ~host ?(device = "eth0")

ocaml/xapi-cli-server/records.ml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3393,6 +3393,12 @@ let host_record rpc session_id host =
33933393
~value:(safe_bool_of_string "ssh-auto-mode" value)
33943394
)
33953395
()
3396+
; make_field ~name:"max-cstate"
3397+
~get:(fun () -> (x ()).API.host_max_cstate)
3398+
~set:(fun value ->
3399+
Client.Host.set_max_cstate ~rpc ~session_id ~self:host ~value
3400+
)
3401+
()
33963402
]
33973403
}
33983404

ocaml/xapi/dbsync_slave.ml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,5 +408,8 @@ let update_env __context sync_keys =
408408
Xapi_host.set_console_idle_timeout ~__context ~self:localhost
409409
~value:console_timeout
410410
) ;
411+
switched_sync Xapi_globs.sync_max_cstate (fun () ->
412+
Xapi_host.sync_max_cstate ~__context ~host:localhost
413+
) ;
411414

412415
remove_pending_guidances ~__context

ocaml/xapi/message_forwarding.ml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4114,6 +4114,14 @@ functor
41144114
let local_fn = Local.Host.set_ssh_auto_mode ~self ~value in
41154115
let remote_fn = Client.Host.set_ssh_auto_mode ~self ~value in
41164116
do_op_on ~local_fn ~__context ~host:self ~remote_fn
4117+
4118+
let set_max_cstate ~__context ~self ~value =
4119+
info "Host.set_max_cstate: host='%s' value='%s'"
4120+
(host_uuid ~__context self)
4121+
value ;
4122+
let local_fn = Local.Host.set_max_cstate ~self ~value in
4123+
let remote_fn = Client.Host.set_max_cstate ~self ~value in
4124+
do_op_on ~local_fn ~__context ~host:self ~remote_fn
41174125
end
41184126

41194127
module Host_crashdump = struct

ocaml/xapi/xapi_globs.ml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,8 @@ let sync_chipset_info = "sync_chipset_info"
374374

375375
let sync_ssh_status = "sync_ssh_status"
376376

377+
let sync_max_cstate = "sync_max_cstate"
378+
377379
let sync_pci_devices = "sync_pci_devices"
378380

379381
let sync_gpus = "sync_gpus"
@@ -920,6 +922,8 @@ let systemctl = ref "/usr/bin/systemctl"
920922

921923
let xen_cmdline_script = ref "/opt/xensource/libexec/xen-cmdline"
922924

925+
let xenpm_bin = ref "/usr/sbin/xenpm"
926+
923927
let alert_certificate_check = ref "alert-certificate-check"
924928

925929
let sr_health_check_task_label = "SR Recovering"

ocaml/xapi/xapi_host.ml

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1092,7 +1092,8 @@ let create ~__context ~uuid ~name_label ~name_description:_ ~hostname ~address
10921092
~tls_verification_enabled ~last_software_update ~last_update_hash
10931093
~recommended_guidances:[] ~latest_synced_updates_applied:`unknown
10941094
~pending_guidances_recommended:[] ~pending_guidances_full:[] ~ssh_enabled
1095-
~ssh_enabled_timeout ~ssh_expiry ~console_idle_timeout ~ssh_auto_mode ;
1095+
~ssh_enabled_timeout ~ssh_expiry ~console_idle_timeout ~ssh_auto_mode
1096+
~max_cstate:"" ;
10961097
(* If the host we're creating is us, make sure its set to live *)
10971098
Db.Host_metrics.set_last_updated ~__context ~self:metrics ~value:(Date.now ()) ;
10981099
Db.Host_metrics.set_live ~__context ~self:metrics ~value:host_is_us ;
@@ -3337,3 +3338,38 @@ let set_console_idle_timeout ~__context ~self ~value =
33373338
error "Failed to configure console timeout: %s" (Printexc.to_string e) ;
33383339
Helpers.internal_error "Failed to set console timeout: %Ld: %s" value
33393340
(Printexc.to_string e)
3341+
3342+
let set_max_cstate ~__context ~self ~value =
3343+
if Helpers.get_localhost ~__context <> self then
3344+
failwith "Forwarded to the wrong host" ;
3345+
let allowed_cstates = [None; Some 0; Some 1] in
3346+
let max_cstate, max_sub_cstate =
3347+
try Xapi_host_max_cstate.of_string value
3348+
with e ->
3349+
error "Invalid max_cstate value: %s" (Printexc.to_string e) ;
3350+
raise Api_errors.(Server_error (invalid_value, ["max_cstate"; value]))
3351+
in
3352+
if not (List.mem max_cstate allowed_cstates) then
3353+
let err_msg = "Only C0, C1 and unlimited are supported for max-cstate" in
3354+
raise
3355+
Api_errors.(
3356+
Server_error (value_not_supported, ["max_cstate"; value; err_msg])
3357+
)
3358+
else
3359+
try
3360+
Xapi_host_max_cstate.xenpm_set max_cstate max_sub_cstate ;
3361+
Xapi_host_max_cstate.xen_cmdline_set max_cstate max_sub_cstate ;
3362+
Db.Host.set_max_cstate ~__context ~self ~value
3363+
with e ->
3364+
let err_msg =
3365+
Printf.sprintf "Failed to update max_cstate: %s" (Printexc.to_string e)
3366+
in
3367+
error "%s" err_msg ;
3368+
Helpers.internal_error "%s" err_msg
3369+
3370+
let sync_max_cstate ~__context ~host =
3371+
try
3372+
let max_cstate, max_sub_cstate = Xapi_host_max_cstate.xen_cmdline_get () in
3373+
let value = Xapi_host_max_cstate.to_string (max_cstate, max_sub_cstate) in
3374+
Db.Host.set_max_cstate ~__context ~self:host ~value
3375+
with e -> error "Failed to sync max_cstate: %s" (Printexc.to_string e)

ocaml/xapi/xapi_host.mli

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,3 +589,8 @@ val schedule_disable_ssh_job :
589589

590590
val set_ssh_auto_mode :
591591
__context:Context.t -> self:API.ref_host -> value:bool -> unit
592+
593+
val set_max_cstate :
594+
__context:Context.t -> self:API.ref_host -> value:string -> unit
595+
596+
val sync_max_cstate : __context:Context.t -> host:API.ref_host -> unit

ocaml/xapi/xapi_host_max_cstate.ml

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
(*
2+
* Copyright (c) Cloud Software Group, Inc.
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU Lesser General Public License as published
6+
* by the Free Software Foundation; version 2.1 only. with the special
7+
* exception on linking described in file LICENSE.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU Lesser General Public License for more details.
13+
*)
14+
15+
exception Invalid_cstate of (int option * int option)
16+
17+
exception Invalid_cstate_string of string
18+
19+
exception Parse_cmd_output_failure of string
20+
21+
exception Command_failure of string
22+
23+
let ( let* ) = Option.bind
24+
25+
let valid cstate = Option.fold ~none:true ~some:(fun v -> v >= 0) cstate
26+
27+
let raise_parse_fail s e =
28+
let msg = Printf.sprintf "output: %s; error: %s" s (Printexc.to_string e) in
29+
raise (Parse_cmd_output_failure msg)
30+
31+
let to_string (cstate, sub_cstate) =
32+
match (cstate, sub_cstate) with
33+
| None, _ ->
34+
""
35+
| Some v, None when v >= 0 ->
36+
Int.to_string v
37+
| Some v, Some sub_v when v >= 0 && sub_v >= 0 ->
38+
Printf.sprintf "%d,%d" v sub_v
39+
| _ ->
40+
raise (Invalid_cstate (cstate, sub_cstate))
41+
42+
let of_string s =
43+
let cstate, sub_cstate =
44+
match String.split_on_char ',' s with
45+
| [""] ->
46+
(None, None)
47+
| [state] -> (
48+
try (Some (int_of_string state), None)
49+
with _ -> raise (Invalid_cstate_string s)
50+
)
51+
| [state; sub_state] -> (
52+
try (Some (int_of_string state), Some (int_of_string sub_state))
53+
with _ -> raise (Invalid_cstate_string s)
54+
)
55+
| _ ->
56+
raise (Invalid_cstate_string s)
57+
in
58+
if valid cstate && valid sub_cstate then
59+
(cstate, sub_cstate)
60+
else
61+
raise (Invalid_cstate_string s)
62+
63+
let parse_xenpm_response resp =
64+
let parse_max_cstate_line line =
65+
let line = String.trim line in
66+
let* cstate_word =
67+
try Scanf.sscanf line "max C-state set to %s" Option.some
68+
with e -> raise_parse_fail resp e
69+
in
70+
match cstate_word with
71+
| "unlimited" ->
72+
None
73+
| s -> (
74+
try Scanf.sscanf s "C%d" Option.some with e -> raise_parse_fail resp e
75+
)
76+
in
77+
let parse_sub_cstate_line line =
78+
let line = String.trim line in
79+
let* sub_cstate_word =
80+
try Scanf.sscanf line "max C-substate set to %s succeeded" Option.some
81+
with e -> raise_parse_fail resp e
82+
in
83+
match sub_cstate_word with
84+
| "unlimited" ->
85+
None
86+
| s -> (
87+
try Some (int_of_string s) with e -> raise_parse_fail resp e
88+
)
89+
in
90+
match String.split_on_char '\n' resp with
91+
| [] | [""] ->
92+
raise (Parse_cmd_output_failure resp)
93+
| cstate_line :: [] | [cstate_line; ""] ->
94+
(parse_max_cstate_line cstate_line, None)
95+
| cstate_line :: sub_cstate_line :: _ ->
96+
(parse_max_cstate_line cstate_line, parse_sub_cstate_line sub_cstate_line)
97+
98+
let parse_xencmdline_response resp =
99+
(* the response may be
100+
"" -> unlimited
101+
"max_cstate=N" -> max cstate N
102+
"max_cstate=N,M" -> max cstate N, max c-sub-state M *)
103+
let resp = String.trim resp in
104+
match Astring.String.fields ~is_sep:(fun c -> c = '=' || c = ',') resp with
105+
| [""] ->
106+
(None, None)
107+
| ["max_cstate"; state] -> (
108+
try (Some (int_of_string state), None) with e -> raise_parse_fail resp e
109+
)
110+
| ["max_cstate"; state; sub_state] -> (
111+
try (Some (int_of_string state), Some (int_of_string sub_state))
112+
with e -> raise_parse_fail resp e
113+
)
114+
| _ ->
115+
raise (Parse_cmd_output_failure resp)
116+
117+
(* xenpm examples:
118+
# xenpm set-max-cstate 0 0
119+
max C-state set to C0
120+
max C-substate set to 0 succeeded
121+
# xenpm set-max-cstate 0
122+
max C-state set to C0
123+
max C-substate set to unlimited succeeded
124+
# xenpm set-max-cstate unlimited
125+
max C-state set to unlimited
126+
# xenpm set-max-cstate -1
127+
Missing, excess, or invalid argument(s)
128+
*)
129+
let xenpm_set cstate sub_cstate =
130+
let args =
131+
match (cstate, sub_cstate) with
132+
| None, _ ->
133+
["set-max-cstate"; "unlimited"]
134+
| Some v, None when v >= 0 ->
135+
["set-max-cstate"; string_of_int v]
136+
| Some v, Some sub_v when v >= 0 && sub_v >= 0 ->
137+
["set-max-cstate"; string_of_int v; string_of_int sub_v]
138+
| _ ->
139+
raise (Invalid_cstate (cstate, sub_cstate))
140+
in
141+
let resp =
142+
try Helpers.call_script !Xapi_globs.xenpm_bin args
143+
with e ->
144+
raise
145+
(Command_failure
146+
(Printf.sprintf "xenpm_set failed: %s" (Printexc.to_string e))
147+
)
148+
in
149+
match parse_xenpm_response resp with
150+
| None, _ ->
151+
if cstate <> None then
152+
raise
153+
(Command_failure
154+
(Printf.sprintf "get unmatched value in xenpm output %s" resp)
155+
)
156+
| cstate_in_resp, sub_cstate_in_resp ->
157+
if (cstate_in_resp, sub_cstate_in_resp) <> (cstate, sub_cstate) then
158+
raise
159+
(Command_failure
160+
(Printf.sprintf "get unmatched value in xenpm output %s" resp)
161+
)
162+
163+
let xen_cmdline_set cstate sub_cstate =
164+
let args =
165+
match (cstate, sub_cstate) with
166+
| None, _ ->
167+
["--delete-xen"; "max_cstate"]
168+
| Some v, None when v >= 0 ->
169+
["--set-xen"; Printf.sprintf "max_cstate=%d" v]
170+
| Some v, Some sub_v when v >= 0 && sub_v >= 0 ->
171+
["--set-xen"; Printf.sprintf "max_cstate=%d,%d" v sub_v]
172+
| _ ->
173+
raise (Invalid_cstate (cstate, sub_cstate))
174+
in
175+
try Helpers.call_script !Xapi_globs.xen_cmdline_script args |> ignore
176+
with e ->
177+
raise
178+
(Command_failure
179+
(Printf.sprintf "xen_cmdline_set failed: %s" (Printexc.to_string e))
180+
)
181+
182+
let xen_cmdline_get () =
183+
let args = ["--get-xen"; "max_cstate"] in
184+
let resp =
185+
try Helpers.call_script !Xapi_globs.xen_cmdline_script args
186+
with e ->
187+
raise
188+
(Command_failure
189+
(Printf.sprintf "xen_cmdline_get failed: %s" (Printexc.to_string e))
190+
)
191+
in
192+
parse_xencmdline_response resp

0 commit comments

Comments
 (0)