@@ -9,23 +9,26 @@ use log::debug;
99use mctp:: { AsyncRespChannel , MsgIC } ;
1010
1111use crate :: {
12- CommandEffect , CommandEffectError , Discriminant , MAX_NAMESPACES ,
12+ CommandEffect , CommandEffectError , Discriminant , MAX_CONTROLLERS , MAX_NAMESPACES ,
1313 nvme:: {
1414 AdminGetLogPageLidRequestType , AdminGetLogPageSupportedLogPagesResponse ,
1515 AdminIdentifyActiveNamespaceIdListResponse , AdminIdentifyAllocatedNamespaceIdListResponse ,
1616 AdminIdentifyCnsRequestType , AdminIdentifyControllerResponse ,
1717 AdminIdentifyNamespaceIdentificationDescriptorListResponse ,
1818 AdminIdentifyNvmIdentifyNamespaceResponse , AdminIoCqeGenericCommandStatus ,
19- AdminIoCqeStatus , AdminIoCqeStatusType , ControllerListResponse , CriticalWarningFlags ,
19+ AdminIoCqeStatus , AdminIoCqeStatusType , ControllerListResponse ,
2020 LidSupportedAndEffectsDataStructure , LidSupportedAndEffectsFlags , LogPageAttributes ,
2121 NamespaceIdentifierType , SmartHealthInformationLogPageResponse ,
2222 mi:: {
2323 AdminCommandRequestHeader , AdminCommandResponseHeader ,
24- CompositeControllerStatusDataStructureResponse , ControllerInformationResponse ,
25- MessageType , NvmSubsystemHealthDataStructureResponse , NvmSubsystemInformationResponse ,
26- NvmeManagementResponse , NvmeMiCommandRequestHeader , NvmeMiCommandRequestType ,
27- NvmeMiDataStructureManagementResponse , NvmeMiDataStructureRequestType ,
28- PciePortDataResponse , PortInformationResponse , TwoWirePortDataResponse ,
24+ CompositeControllerStatusDataStructureResponse , CompositeControllerStatusFlagSet ,
25+ ControllerFunctionAndReportingFlags , ControllerHealthDataStructure ,
26+ ControllerHealthStatusPollResponse , ControllerInformationResponse ,
27+ ControllerPropertyFlags , MessageType , NvmSubsystemHealthDataStructureResponse ,
28+ NvmSubsystemInformationResponse , NvmeManagementResponse , NvmeMiCommandRequestHeader ,
29+ NvmeMiCommandRequestType , NvmeMiDataStructureManagementResponse ,
30+ NvmeMiDataStructureRequestType , PciePortDataResponse , PortInformationResponse ,
31+ TwoWirePortDataResponse ,
2932 } ,
3033 } ,
3134 wire:: { WireString , WireVec } ,
@@ -232,6 +235,109 @@ impl RequestHandler for NvmeMiCommandRequestHeader {
232235 send_response ( resp, & [ & mh. 0 , & mr. 0 , & nvmshds. 0 , & ccs. 0 ] ) . await ;
233236 Ok ( ( ) )
234237 }
238+ NvmeMiCommandRequestType :: ControllerHealthStatusPoll ( req) => {
239+ // MI v2.0, 5.3
240+ if !rest. is_empty ( ) {
241+ debug ! ( "Lost coherence decoding {:?}" , ctx. opcode) ;
242+ return Err ( ResponseStatus :: InvalidCommandSize ) ;
243+ }
244+
245+ if !req
246+ . functions
247+ . 0
248+ . contains ( ControllerFunctionAndReportingFlags :: All )
249+ {
250+ debug ! ( "TODO: Implement support for property-based selectors" ) ;
251+ return Err ( ResponseStatus :: InternalError ) ;
252+ }
253+
254+ if req. functions . 0 . contains (
255+ ControllerFunctionAndReportingFlags :: Incf
256+ | ControllerFunctionAndReportingFlags :: Incpf
257+ | ControllerFunctionAndReportingFlags :: Incvf ,
258+ ) {
259+ debug ! ( "TODO: Implement support for function-base selectors" ) ;
260+ return Err ( ResponseStatus :: InternalError ) ;
261+ }
262+
263+ assert ! ( MAX_CONTROLLERS <= u8 :: MAX as usize ) ;
264+ if req. maxrent < MAX_CONTROLLERS as u8 {
265+ debug ! ( "TODO: Implement response entry constraint" ) ;
266+ return Err ( ResponseStatus :: InternalError ) ;
267+ }
268+
269+ if req. sctlid > 0 {
270+ debug ! ( "TODO: Implement starting controller ID constraint" ) ;
271+ return Err ( ResponseStatus :: InternalError ) ;
272+ }
273+
274+ let mh = MessageHeader :: respond ( MessageType :: NvmeMiCommand ) . encode ( ) ?;
275+
276+ let mut chspr = ControllerHealthStatusPollResponse {
277+ status : ResponseStatus :: Success ,
278+ rent : 0 ,
279+ body : WireVec :: new ( ) ,
280+ } ;
281+
282+ for ctlr in & subsys. ctlrs {
283+ chspr
284+ . body
285+ . push ( ControllerHealthDataStructure {
286+ ctlid : ctlr. id . 0 ,
287+ csts : ctlr. csts . into ( ) ,
288+ ctemp : ctlr. temp ,
289+ pdlu : core:: cmp:: min ( 255 , 100 * ctlr. write_age / ctlr. write_lifespan )
290+ as u8 ,
291+ spare : <u8 >:: try_from ( 100 * ctlr. spare / ctlr. capacity )
292+ . map_err ( |_| ResponseStatus :: InternalError ) ?
293+ . clamp ( 0 , 100 ) ,
294+ cwarn : {
295+ let mut fs = FlagSet :: empty ( ) ;
296+
297+ if ctlr. spare < ctlr. spare_range . lower {
298+ fs |= crate :: nvme:: mi:: CriticalWarningFlags :: St ;
299+ }
300+
301+ if ctlr. temp < ctlr. temp_range . lower
302+ || ctlr. temp > ctlr. temp_range . upper
303+ {
304+ fs |= crate :: nvme:: mi:: CriticalWarningFlags :: Taut ;
305+ }
306+
307+ // TODO: RD
308+
309+ if ctlr. ro {
310+ fs |= crate :: nvme:: mi:: CriticalWarningFlags :: Ro ;
311+ }
312+
313+ // TODO: VMBF
314+ // TODO: PMRRO
315+
316+ fs. into ( )
317+ } ,
318+ chsc : {
319+ let mecs = & mut mep. mecss [ ctlr. id . 0 as usize ] ;
320+ let fs = mecs. chscf ;
321+
322+ if req. properties . 0 . contains ( ControllerPropertyFlags :: Ccf ) {
323+ mecs. chscf . clear ( ) ;
324+ // TODO: Clear NAC, FA, TCIDA in controller health
325+ }
326+
327+ fs. into ( )
328+ } ,
329+ } )
330+ . map_err ( |_| {
331+ debug ! ( "Failed to push ControllerHealthDataStructure" ) ;
332+ ResponseStatus :: InternalError
333+ } ) ?;
334+ }
335+ chspr. update ( ) ?;
336+ let chspr = chspr. encode ( ) ?;
337+
338+ send_response ( resp, & [ & mh. 0 , & chspr. 0 [ ..chspr. 1 ] ] ) . await ;
339+ Ok ( ( ) )
340+ }
235341 NvmeMiCommandRequestType :: ConfigurationSet ( cid) => {
236342 cid. handle ( ctx, mep, subsys, rest, resp, app) . await
237343 }
@@ -970,17 +1076,17 @@ impl RequestHandler for AdminGetLogPageRequest {
9701076 let mut fs = FlagSet :: empty ( ) ;
9711077
9721078 if ctlr. spare < ctlr. spare_range . lower {
973- fs |= CriticalWarningFlags :: Ascbt ;
1079+ fs |= crate :: nvme :: CriticalWarningFlags :: Ascbt ;
9741080 }
9751081
9761082 if ctlr. temp < ctlr. temp_range . lower || ctlr. temp > ctlr. temp_range . upper {
977- fs |= CriticalWarningFlags :: Ttc ;
1083+ fs |= crate :: nvme :: CriticalWarningFlags :: Ttc ;
9781084 }
9791085
9801086 // TODO: NDR
9811087
9821088 if ctlr. ro {
983- fs |= CriticalWarningFlags :: Amro ;
1089+ fs |= crate :: nvme :: CriticalWarningFlags :: Amro ;
9841090 }
9851091
9861092 // TODO: VMBF
@@ -1350,16 +1456,35 @@ impl crate::ManagementEndpoint {
13501456 for c in & subsys. ctlrs {
13511457 let mecs = & mut self . mecss [ c. id . 0 as usize ] ;
13521458
1459+ // It might seem tempting to compose self.ccsf with an
1460+ // assignment-union over each controller's mecs.chscf. However, this
1461+ // doesn't work in practice due to the requirements of NVMe MI /
1462+ // Configuration Set / Health Status Change on the behaviour of
1463+ // clearing the composite controller flags, against the requirements
1464+ // of NVMe MI / Controller Health Status Poll on the behaviour of
1465+ // clearing the controller health status flags.
1466+ //
1467+ // Instead, update each independently by first gathering the change
1468+ // flags for the current update cycle, then using union-assignment
1469+ // into both mecs.chscf and self.ccsf (in the case of the latter,
1470+ // via the conversion to the composite controller flag set).
1471+ let mut update = FlagSet :: empty ( ) ;
1472+
13531473 if mecs. cc . en != c. cc . en {
1354- self . ccsf . 0 |= crate :: nvme:: mi:: CompositeControllerStatusFlags :: Ceco ;
1474+ update |= crate :: nvme:: mi:: ControllerHealthStatusChangedFlags :: Ceco ;
13551475 }
13561476
13571477 if mecs. csts . contains ( crate :: nvme:: ControllerStatusFlags :: Rdy )
13581478 != c. csts . contains ( crate :: nvme:: ControllerStatusFlags :: Rdy )
13591479 {
1360- self . ccsf . 0 |= crate :: nvme:: mi:: CompositeControllerStatusFlags :: Rdy ;
1480+ update |= crate :: nvme:: mi:: ControllerHealthStatusChangedFlags :: Rdy ;
13611481 }
13621482
1483+ mecs. chscf |= update;
1484+
1485+ let update: CompositeControllerStatusFlagSet = update. into ( ) ;
1486+ self . ccsf . 0 |= update. 0 ;
1487+
13631488 mecs. cc = c. cc ;
13641489 mecs. csts = c. csts ;
13651490 }
0 commit comments