@@ -48,12 +48,15 @@ use gateway_messages::{ComponentDetails, Message, MgsError, StartupOptions};
48
48
use gateway_messages:: { DiscoverResponse , IgnitionState , PowerState } ;
49
49
use gateway_messages:: { MessageKind , version} ;
50
50
use gateway_types:: component:: SpState ;
51
+ use omicron_common:: disk:: M2Slot ;
51
52
use slog:: { Logger , debug, error, info, warn} ;
52
53
use std:: cell:: Cell ;
53
54
use std:: collections:: HashMap ;
54
55
use std:: iter;
55
56
use std:: net:: { SocketAddr , SocketAddrV6 } ;
56
57
use std:: pin:: Pin ;
58
+ use std:: sync:: atomic:: AtomicUsize ;
59
+ use std:: sync:: atomic:: Ordering ;
57
60
use std:: sync:: { Arc , Mutex } ;
58
61
use tokio:: io:: { AsyncReadExt , AsyncWriteExt } ;
59
62
use tokio:: net:: { TcpListener , TcpStream , UdpSocket } ;
@@ -86,6 +89,24 @@ pub enum SimSpHandledRequest {
86
89
NotImplemented ,
87
90
}
88
91
92
+ /// Current power state and, if in A0, which M2 slot was active at the time
93
+ /// we transitioned to A0. (This represents what disk the OS would attempt to
94
+ /// boot from, if we were a real SP connected to a real sled.)
95
+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
96
+ pub enum GimletPowerState {
97
+ A2 ,
98
+ A0 ( M2Slot ) ,
99
+ }
100
+
101
+ impl From < GimletPowerState > for PowerState {
102
+ fn from ( value : GimletPowerState ) -> Self {
103
+ match value {
104
+ GimletPowerState :: A2 => Self :: A2 ,
105
+ GimletPowerState :: A0 ( _) => Self :: A0 ,
106
+ }
107
+ }
108
+ }
109
+
89
110
pub struct Gimlet {
90
111
local_addrs : Option < [ SocketAddrV6 ; 2 ] > ,
91
112
ereport_addrs : Option < [ SocketAddrV6 ; 2 ] > ,
@@ -94,7 +115,9 @@ pub struct Gimlet {
94
115
commands : mpsc:: UnboundedSender < Command > ,
95
116
inner_tasks : Vec < JoinHandle < ( ) > > ,
96
117
responses_sent_count : Option < watch:: Receiver < usize > > ,
118
+ power_state_changes : Arc < AtomicUsize > ,
97
119
last_request_handled : Arc < Mutex < Option < SimSpHandledRequest > > > ,
120
+ power_state_rx : Option < watch:: Receiver < GimletPowerState > > ,
98
121
}
99
122
100
123
impl Drop for Gimlet {
@@ -149,13 +172,10 @@ impl SimulatedSp for Gimlet {
149
172
handler. update_state . last_rot_update_data ( )
150
173
}
151
174
152
- async fn last_host_phase1_update_data (
153
- & self ,
154
- slot : u16 ,
155
- ) -> Option < Box < [ u8 ] > > {
175
+ async fn host_phase1_data ( & self , slot : u16 ) -> Option < Vec < u8 > > {
156
176
let handler = self . handler . as_ref ( ) ?;
157
177
let handler = handler. lock ( ) . await ;
158
- handler. update_state . last_host_phase1_update_data ( slot)
178
+ handler. update_state . host_phase1_data ( slot)
159
179
}
160
180
161
181
async fn current_update_status ( & self ) -> gateway_messages:: UpdateStatus {
@@ -166,6 +186,10 @@ impl SimulatedSp for Gimlet {
166
186
handler. lock ( ) . await . update_state . status ( )
167
187
}
168
188
189
+ fn power_state_changes ( & self ) -> usize {
190
+ self . power_state_changes . load ( Ordering :: Relaxed )
191
+ }
192
+
169
193
fn responses_sent_count ( & self ) -> Option < watch:: Receiver < usize > > {
170
194
self . responses_sent_count . clone ( )
171
195
}
@@ -235,6 +259,8 @@ impl Gimlet {
235
259
inner_tasks,
236
260
responses_sent_count : None ,
237
261
last_request_handled,
262
+ power_state_rx : None ,
263
+ power_state_changes : Arc :: new ( AtomicUsize :: new ( 0 ) ) ,
238
264
} ) ;
239
265
} ;
240
266
@@ -374,6 +400,9 @@ impl Gimlet {
374
400
}
375
401
}
376
402
let local_addrs = [ servers[ 0 ] . local_addr ( ) , servers[ 1 ] . local_addr ( ) ] ;
403
+ let ( power_state, power_state_rx) =
404
+ watch:: channel ( GimletPowerState :: A0 ( M2Slot :: A ) ) ;
405
+ let power_state_changes = Arc :: new ( AtomicUsize :: new ( 0 ) ) ;
377
406
let ( inner, handler, responses_sent_count) = UdpTask :: new (
378
407
servers,
379
408
ereport_servers,
@@ -382,11 +411,13 @@ impl Gimlet {
382
411
attached_mgs,
383
412
gimlet. common . serial_number . clone ( ) ,
384
413
incoming_console_tx,
414
+ power_state,
385
415
commands_rx,
386
416
Arc :: clone ( & last_request_handled) ,
387
417
log,
388
418
gimlet. common . old_rot_state ,
389
419
update_state,
420
+ Arc :: clone ( & power_state_changes) ,
390
421
) ;
391
422
inner_tasks
392
423
. push ( task:: spawn ( async move { inner. run ( ) . await . unwrap ( ) } ) ) ;
@@ -400,9 +431,15 @@ impl Gimlet {
400
431
inner_tasks,
401
432
responses_sent_count : Some ( responses_sent_count) ,
402
433
last_request_handled,
434
+ power_state_rx : Some ( power_state_rx) ,
435
+ power_state_changes,
403
436
} )
404
437
}
405
438
439
+ pub fn power_state_rx ( & self ) -> Option < watch:: Receiver < GimletPowerState > > {
440
+ self . power_state_rx . clone ( )
441
+ }
442
+
406
443
pub fn serial_console_addr ( & self , component : & str ) -> Option < SocketAddrV6 > {
407
444
self . serial_console_addrs . get ( component) . copied ( )
408
445
}
@@ -625,21 +662,25 @@ impl UdpTask {
625
662
attached_mgs : AttachedMgsSerialConsole ,
626
663
serial_number : String ,
627
664
incoming_serial_console : HashMap < SpComponent , UnboundedSender < Vec < u8 > > > ,
665
+ power_state : watch:: Sender < GimletPowerState > ,
628
666
commands : mpsc:: UnboundedReceiver < Command > ,
629
667
last_request_handled : Arc < Mutex < Option < SimSpHandledRequest > > > ,
630
668
log : Logger ,
631
669
old_rot_state : bool ,
632
670
update_state : SimSpUpdate ,
671
+ power_state_changes : Arc < AtomicUsize > ,
633
672
) -> ( Self , Arc < TokioMutex < Handler > > , watch:: Receiver < usize > ) {
634
673
let [ udp0, udp1] = servers;
635
674
let handler = Arc :: new ( TokioMutex :: new ( Handler :: new (
636
675
serial_number,
637
676
components,
638
677
attached_mgs,
639
678
incoming_serial_console,
679
+ power_state,
640
680
log. clone ( ) ,
641
681
old_rot_state,
642
682
update_state,
683
+ power_state_changes,
643
684
) ) ) ;
644
685
let responses_sent_count = watch:: Sender :: new ( 0 ) ;
645
686
let responses_sent_count_rx = responses_sent_count. subscribe ( ) ;
@@ -782,7 +823,8 @@ struct Handler {
782
823
783
824
attached_mgs : AttachedMgsSerialConsole ,
784
825
incoming_serial_console : HashMap < SpComponent , UnboundedSender < Vec < u8 > > > ,
785
- power_state : PowerState ,
826
+ power_state : watch:: Sender < GimletPowerState > ,
827
+ power_state_changes : Arc < AtomicUsize > ,
786
828
startup_options : StartupOptions ,
787
829
update_state : SimSpUpdate ,
788
830
reset_pending : Option < SpComponent > ,
@@ -801,14 +843,17 @@ struct Handler {
801
843
}
802
844
803
845
impl Handler {
846
+ #[ allow( clippy:: too_many_arguments) ]
804
847
fn new (
805
848
serial_number : String ,
806
849
components : Vec < SpComponentConfig > ,
807
850
attached_mgs : AttachedMgsSerialConsole ,
808
851
incoming_serial_console : HashMap < SpComponent , UnboundedSender < Vec < u8 > > > ,
852
+ power_state : watch:: Sender < GimletPowerState > ,
809
853
log : Logger ,
810
854
old_rot_state : bool ,
811
855
update_state : SimSpUpdate ,
856
+ power_state_changes : Arc < AtomicUsize > ,
812
857
) -> Self {
813
858
let mut leaked_component_device_strings =
814
859
Vec :: with_capacity ( components. len ( ) ) ;
@@ -835,14 +880,15 @@ impl Handler {
835
880
serial_number,
836
881
attached_mgs,
837
882
incoming_serial_console,
838
- power_state : PowerState :: A2 ,
839
883
startup_options : StartupOptions :: empty ( ) ,
840
884
update_state,
841
885
reset_pending : None ,
886
+ power_state,
842
887
last_request_handled : None ,
843
888
should_fail_to_respond_signal : None ,
844
889
old_rot_state,
845
890
sp_dumps,
891
+ power_state_changes,
846
892
}
847
893
}
848
894
@@ -859,7 +905,7 @@ impl Handler {
859
905
model,
860
906
revision : 0 ,
861
907
base_mac_address : [ 0 ; 6 ] ,
862
- power_state : self . power_state ,
908
+ power_state : ( * self . power_state . borrow ( ) ) . into ( ) ,
863
909
rot : Ok ( rot_state_v2 ( self . update_state . rot_state ( ) ) ) ,
864
910
}
865
911
}
@@ -1195,19 +1241,21 @@ impl SpHandler for Handler {
1195
1241
}
1196
1242
1197
1243
fn power_state ( & mut self ) -> Result < PowerState , SpError > {
1244
+ let power_state = * self . power_state . borrow ( ) ;
1198
1245
debug ! (
1199
1246
& self . log, "received power state" ;
1200
- "power_state" => ?self . power_state,
1247
+ "power_state" => ?power_state,
1201
1248
) ;
1202
- Ok ( self . power_state )
1249
+ Ok ( power_state. into ( ) )
1203
1250
}
1204
1251
1205
1252
fn set_power_state (
1206
1253
& mut self ,
1207
1254
sender : Sender < Self :: VLanId > ,
1208
1255
power_state : PowerState ,
1209
1256
) -> Result < PowerStateTransition , SpError > {
1210
- let transition = if power_state != self . power_state {
1257
+ let prev_power_state = * self . power_state . borrow ( ) ;
1258
+ let transition = if power_state != prev_power_state. into ( ) {
1211
1259
PowerStateTransition :: Changed
1212
1260
} else {
1213
1261
PowerStateTransition :: Unchanged
@@ -1216,11 +1264,38 @@ impl SpHandler for Handler {
1216
1264
debug ! (
1217
1265
& self . log, "received set power state" ;
1218
1266
"sender" => ?sender,
1219
- "prev_power_state" => ?self . power_state,
1267
+ "prev_power_state" => ?power_state,
1220
1268
"power_state" => ?power_state,
1221
1269
"transition" => ?transition,
1222
1270
) ;
1223
- self . power_state = power_state;
1271
+
1272
+ let new_power_state = match power_state {
1273
+ PowerState :: A0 => {
1274
+ let slot = self
1275
+ . update_state
1276
+ . component_get_active_slot ( SpComponent :: HOST_CPU_BOOT_FLASH )
1277
+ . expect ( "can always get active slot for valid component" ) ;
1278
+ let slot = M2Slot :: from_mgs_firmware_slot ( slot)
1279
+ . expect ( "sp-sim ensures host slot is always valid" ) ;
1280
+ GimletPowerState :: A0 ( slot)
1281
+ }
1282
+ // `A1` is a transitory state that we can't even observe on real
1283
+ // devices as of https://github.com/oxidecomputer/hubris/pull/2107.
1284
+ // Our tests really care about "host powered on" (A0) or "host
1285
+ // powered off" (A2), so just squish the transitory state down to
1286
+ // "host powered off".
1287
+ PowerState :: A1 | PowerState :: A2 => GimletPowerState :: A2 ,
1288
+ } ;
1289
+ self . power_state . send_modify ( |s| {
1290
+ * s = new_power_state;
1291
+ } ) ;
1292
+ match transition {
1293
+ PowerStateTransition :: Changed => {
1294
+ self . power_state_changes . fetch_add ( 1 , Ordering :: Relaxed ) ;
1295
+ }
1296
+ PowerStateTransition :: Unchanged => ( ) ,
1297
+ }
1298
+
1224
1299
Ok ( transition)
1225
1300
}
1226
1301
0 commit comments