Skip to main content

virtio_accel_device/
engine.rs

1//! Transport-neutral baseline command processing.
2//!
3//! `CommandProcessor` owns one backend and one device-state graph. Processing requires exclusive
4//! access, so the portable layer contains no locks, atomics, reference-counted handles, or hidden
5//! executor. Transport integrations may schedule distinct processors or backend work concurrently,
6//! but state admission and response commitment remain explicit serialization boundaries.
7
8use alloc::vec::Vec;
9
10use virtio_accel_core::{
11    Accelerator, ArtifactRef, BackendError, BindingRef, BufferRange, BufferUsage, ByteSink,
12    ByteSource, Capabilities, DeviceInfo, DeviceInfoError, EventState, ReleaseFailure,
13    SubmitFailure, Timeout, validate_bindings,
14};
15use virtio_accel_proto::{
16    KnownEventState, Le16, Le32, Le64, ObjectPayload, StatusCode, SubmitResponse, WireConfig,
17    WireDeviceInfo, WireEventState,
18};
19use zerocopy::IntoBytes;
20
21use crate::{
22    BufferCreateOutcome, BufferRecord, ChainRegion, CreateError, DecodedBinding, DecodedRequest,
23    DecodedRequestBody, DecoderLimits, DecoderLimitsError, DeviceState, DeviceStateConfigError,
24    DeviceStateError, FrameDecoder, FramePreflight, FramePreflightError, ObjectId, ObjectNamespace,
25    ResourceCounts, ResourcePolicy, ResponseWriteError, ResponseWriter, RetainedBytes,
26    UnusableFrame, preflight_command_frame, status_from_backend_error,
27    status_from_device_state_error,
28};
29
30pub type AcceleratorState<A> = DeviceState<
31    <A as Accelerator>::Context,
32    <A as Accelerator>::Buffer,
33    <A as Accelerator>::Program,
34    <A as Accelerator>::Queue,
35    <A as Accelerator>::Event,
36>;
37
38#[derive(Clone, Copy, Debug, PartialEq, Eq)]
39pub enum DeviceHealth {
40    Running,
41    NeedsReset,
42    BackendDiscardRequired,
43}
44
45#[derive(Clone, Copy, Debug, PartialEq, Eq)]
46pub enum ResetDisposition {
47    BackendReusable,
48    BackendDiscardRequired,
49}
50
51#[derive(Clone, Copy, Debug, PartialEq, Eq)]
52pub struct ResetReport {
53    pub disposition: ResetDisposition,
54    pub released: ResourceCounts,
55    pub quarantined: ResourceCounts,
56    pub released_bytes: RetainedBytes,
57    pub quarantined_bytes: RetainedBytes,
58}
59
60#[derive(Clone, Copy, Debug, PartialEq, Eq)]
61pub enum ResetError {
62    NamespaceReuse,
63    State(DeviceStateConfigError),
64}
65
66#[derive(Clone, Copy, Debug, PartialEq, Eq)]
67pub enum CommandProcessorInitError {
68    Backend(BackendError),
69    DeviceInfo(DeviceInfoError),
70    Decoder(DecoderLimitsError),
71    State(DeviceStateConfigError),
72}
73
74#[derive(Clone, Copy, Debug, PartialEq, Eq)]
75pub enum CommandProcessError {
76    Preflight(FramePreflightError),
77    ResponseWrite(ResponseWriteError),
78}
79
80#[derive(Clone, Copy, Debug, PartialEq, Eq)]
81pub enum CommandOutcome {
82    Response {
83        request_id: u64,
84        status: StatusCode,
85        used: u32,
86    },
87    Unusable(UnusableFrame),
88}
89
90#[derive(Clone, Copy, Debug, PartialEq, Eq)]
91enum SubmitCreateError {
92    State(DeviceStateError),
93    OutOfMemory,
94    Rejected(BackendError),
95    Validation(StatusCode),
96}
97
98/// One synchronization-free command engine for a backend instance.
99///
100/// The backend's immutable device information is fetched once during construction and then used
101/// for decoding, validation, and discovery responses. This prevents limits from changing beneath
102/// live object state and removes a backend call from the discovery path.
103pub struct CommandProcessor<A: Accelerator> {
104    state: AcceleratorState<A>,
105    accelerator: A,
106    info: DeviceInfo,
107    policy: ResourcePolicy,
108    decoder: FrameDecoder,
109    health: DeviceHealth,
110    quarantined: ResourceCounts,
111    quarantined_bytes: RetainedBytes,
112    last_reset: Option<ResetReport>,
113}
114
115impl<A: Accelerator> core::fmt::Debug for CommandProcessor<A> {
116    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
117        formatter
118            .debug_struct("CommandProcessor")
119            .field("info", &self.info)
120            .field("policy", &self.policy)
121            .field("decoder", &self.decoder)
122            .field("health", &self.health)
123            .finish_non_exhaustive()
124    }
125}
126
127impl<A: Accelerator> CommandProcessor<A> {
128    pub fn new(
129        accelerator: A,
130        config: &WireConfig,
131        namespace: ObjectNamespace,
132        policy: ResourcePolicy,
133    ) -> Result<Self, CommandProcessorInitError> {
134        config.validate().map_err(|error| {
135            CommandProcessorInitError::Decoder(DecoderLimitsError::Config(error))
136        })?;
137        let info = accelerator
138            .device_info()
139            .map_err(CommandProcessorInitError::Backend)?;
140        info.validate()
141            .map_err(CommandProcessorInitError::DeviceInfo)?;
142        let limits =
143            DecoderLimits::new(config, info).map_err(CommandProcessorInitError::Decoder)?;
144        let state = DeviceState::new(namespace, info.limits, policy)
145            .map_err(CommandProcessorInitError::State)?;
146        Ok(Self {
147            state,
148            accelerator,
149            info,
150            policy,
151            decoder: FrameDecoder::new(limits),
152            health: DeviceHealth::Running,
153            quarantined: ResourceCounts::default(),
154            quarantined_bytes: RetainedBytes::default(),
155            last_reset: None,
156        })
157    }
158
159    pub const fn device_info(&self) -> DeviceInfo {
160        self.info
161    }
162
163    pub const fn health(&self) -> DeviceHealth {
164        self.health
165    }
166
167    pub const fn resource_policy(&self) -> ResourcePolicy {
168        self.policy
169    }
170
171    /// Provider-retained bulk bytes still live or quarantined in this backend instance.
172    pub const fn retained_bytes(&self) -> RetainedBytes {
173        self.state
174            .retained_bytes()
175            .saturating_add(self.quarantined_bytes)
176    }
177
178    pub const fn decoder(&self) -> &FrameDecoder {
179        &self.decoder
180    }
181
182    pub const fn accelerator(&self) -> &A {
183        &self.accelerator
184    }
185
186    pub const fn state(&self) -> &AcceleratorState<A> {
187        &self.state
188    }
189
190    /// Perform one bounded, child-before-parent reset pass.
191    ///
192    /// The transport must stop fetching command chains and stop publishing completions before
193    /// calling this method. A reusable result renews every object table with `namespace`; every
194    /// prior ID is then stale. A discard-required result is sticky and makes no backend calls on
195    /// later reset attempts. The caller must discard the complete processor/backend instance.
196    pub fn reset(&mut self, namespace: ObjectNamespace) -> Result<ResetReport, ResetError> {
197        if self.health == DeviceHealth::BackendDiscardRequired {
198            let report = match self.last_reset {
199                Some(report) => report,
200                None => {
201                    let report =
202                        self.discard_report(ResourceCounts::default(), RetainedBytes::default());
203                    self.last_reset = Some(report);
204                    report
205                }
206            };
207            return Ok(report);
208        }
209        if namespace == self.state.namespace() {
210            return Err(ResetError::NamespaceReuse);
211        }
212
213        self.health = DeviceHealth::NeedsReset;
214        self.last_reset = None;
215        let mut released = ResourceCounts::default();
216        let mut released_bytes = RetainedBytes::default();
217        let mut progress = ResetProgress::default();
218
219        self.reset_events(&mut released, &mut progress);
220        if progress.backend_callable {
221            self.reset_queues(&mut released, &mut progress);
222        }
223        if progress.backend_callable {
224            self.reset_programs(&mut released, &mut released_bytes, &mut progress);
225        }
226        if progress.backend_callable {
227            self.reset_buffers(&mut released, &mut released_bytes, &mut progress);
228        }
229        if progress.backend_callable {
230            self.reset_contexts(&mut released, &mut progress);
231        }
232
233        let remaining = self.state.resource_counts();
234        let quarantined = self.quarantined.saturating_add(remaining);
235        let quarantined_bytes = self
236            .quarantined_bytes
237            .saturating_add(self.state.retained_bytes());
238        if progress.backend_reusable
239            && self.state.is_empty()
240            && quarantined.is_empty()
241            && quarantined_bytes.is_empty()
242        {
243            self.state = DeviceState::new(namespace, self.info.limits, self.policy)
244                .map_err(ResetError::State)?;
245            self.health = DeviceHealth::Running;
246            let report = ResetReport {
247                disposition: ResetDisposition::BackendReusable,
248                released,
249                quarantined,
250                released_bytes,
251                quarantined_bytes,
252            };
253            self.last_reset = None;
254            return Ok(report);
255        }
256
257        self.health = DeviceHealth::BackendDiscardRequired;
258        let report = ResetReport {
259            disposition: ResetDisposition::BackendDiscardRequired,
260            released,
261            quarantined,
262            released_bytes,
263            quarantined_bytes,
264        };
265        self.last_reset = Some(report);
266        Ok(report)
267    }
268
269    /// Process one complete flattened command chain.
270    ///
271    /// A successful preflight guarantees response capacity for the command's largest success
272    /// payload before semantic state or the backend is touched. Once recovery is required, later
273    /// valid frames receive `DEVICE_LOST` without reaching semantic dispatch.
274    pub fn process(
275        &mut self,
276        regions: &[ChainRegion],
277        request: &dyn ByteSource,
278        response: &mut dyn ByteSink,
279    ) -> Result<CommandOutcome, CommandProcessError> {
280        match preflight_command_frame(&self.decoder, regions, request, response)
281            .map_err(CommandProcessError::Preflight)?
282        {
283            FramePreflight::Rejected {
284                request_id,
285                status,
286                used,
287            } => Ok(CommandOutcome::Response {
288                request_id,
289                status,
290                used,
291            }),
292            FramePreflight::Unusable(error) => Ok(CommandOutcome::Unusable(error)),
293            FramePreflight::Ready(request) => {
294                if self.health != DeviceHealth::Running {
295                    return self.respond_empty(
296                        response,
297                        StatusCode::DEVICE_LOST,
298                        request.request_id(),
299                        false,
300                    );
301                }
302                self.last_reset = None;
303                self.dispatch(request, response)
304            }
305        }
306    }
307
308    fn dispatch(
309        &mut self,
310        request: DecodedRequest<'_>,
311        response: &mut dyn ByteSink,
312    ) -> Result<CommandOutcome, CommandProcessError> {
313        let request_id = request.request_id();
314        match request.into_body() {
315            DecodedRequestBody::GetDeviceInfo => {
316                let payload = wire_device_info(self.info);
317                self.respond_bytes(
318                    response,
319                    StatusCode::OK,
320                    request_id,
321                    payload.as_bytes(),
322                    false,
323                )
324            }
325            DecodedRequestBody::CreateContext(desc) => {
326                if let Err(error) = self.info.validate_context_desc(desc) {
327                    return self.respond_backend_error(response, request_id, error, false);
328                }
329                let accelerator = &self.accelerator;
330                match self
331                    .state
332                    .create_context_with(|| accelerator.create_context(desc))
333                {
334                    Ok(id) => self.respond_object(response, request_id, id, true),
335                    Err(error) => self.respond_create_error(response, request_id, error),
336                }
337            }
338            DecodedRequestBody::DestroyContext { context_id } => {
339                self.destroy_context(response, request_id, context_id)
340            }
341            DecodedRequestBody::AllocateBuffer { context_id, desc } => {
342                if let Err(error) = self.info.validate_buffer_desc(desc) {
343                    return self.respond_backend_error(response, request_id, error, false);
344                }
345                let accelerator = &self.accelerator;
346                match self
347                    .state
348                    .create_buffer_with(context_id, desc, |context, requested| {
349                        let allocated = accelerator.allocate_buffer(context, requested)?;
350                        Ok(allocated.into_parts())
351                    }) {
352                    Ok(BufferCreateOutcome::Admitted(id)) => {
353                        self.respond_object(response, request_id, id, true)
354                    }
355                    Ok(BufferCreateOutcome::CleanupRequired { id, error }) => {
356                        self.release_unpublished_buffer(response, request_id, id, error)
357                    }
358                    Err(error) => self.respond_create_error(response, request_id, error),
359                }
360            }
361            DecodedRequestBody::FreeBuffer { buffer_id } => {
362                self.free_buffer(response, request_id, buffer_id)
363            }
364            DecodedRequestBody::WriteBuffer {
365                buffer_id,
366                range,
367                data,
368            } => self.write_buffer(response, request_id, buffer_id, range, &data),
369            DecodedRequestBody::ReadBuffer { buffer_id, range } => {
370                self.read_buffer(response, request_id, buffer_id, range)
371            }
372            DecodedRequestBody::LoadProgram {
373                context_id,
374                format,
375                target,
376                payload,
377                resident_bytes,
378            } => {
379                let accelerator = &self.accelerator;
380                let artifact_bytes = payload.len();
381                match self.state.create_program_with(
382                    context_id,
383                    artifact_bytes,
384                    resident_bytes,
385                    |context| {
386                        accelerator.load_program(
387                            context,
388                            ArtifactRef {
389                                format,
390                                target,
391                                payload: &payload,
392                                resident_bytes,
393                            },
394                        )
395                    },
396                ) {
397                    Ok(id) => self.respond_object(response, request_id, id, true),
398                    Err(error) => self.respond_create_error(response, request_id, error),
399                }
400            }
401            DecodedRequestBody::UnloadProgram { program_id } => {
402                self.unload_program(response, request_id, program_id)
403            }
404            DecodedRequestBody::CreateQueue { context_id, desc } => {
405                if let Err(error) = self.info.validate_queue_desc(desc) {
406                    return self.respond_backend_error(response, request_id, error, false);
407                }
408                let accelerator = &self.accelerator;
409                match self.state.create_queue_with(context_id, |context| {
410                    accelerator.create_queue(context, desc)
411                }) {
412                    Ok(id) => self.respond_object(response, request_id, id, true),
413                    Err(error) => self.respond_create_error(response, request_id, error),
414                }
415            }
416            DecodedRequestBody::DestroyQueue { queue_id } => {
417                self.destroy_queue(response, request_id, queue_id)
418            }
419            DecodedRequestBody::Submit {
420                queue_id,
421                program_id,
422                bindings,
423                timeout,
424            } => self.submit(
425                response, request_id, queue_id, program_id, bindings, timeout,
426            ),
427            DecodedRequestBody::PollEvent { event_id } => {
428                self.poll_event(response, request_id, event_id)
429            }
430            DecodedRequestBody::CancelEvent { event_id } => {
431                self.cancel_event(response, request_id, event_id)
432            }
433            DecodedRequestBody::DestroyEvent { event_id } => {
434                self.destroy_event(response, request_id, event_id)
435            }
436        }
437    }
438
439    fn submit(
440        &mut self,
441        response: &mut dyn ByteSink,
442        request_id: u64,
443        queue_id: ObjectId,
444        program_id: ObjectId,
445        bindings: Vec<DecodedBinding>,
446        timeout: Timeout,
447    ) -> Result<CommandOutcome, CommandProcessError> {
448        let mut buffer_ids = Vec::new();
449        if buffer_ids.try_reserve_exact(bindings.len()).is_err() {
450            return self.respond_empty(response, StatusCode::OUT_OF_MEMORY, request_id, false);
451        }
452        buffer_ids.extend(bindings.iter().map(|binding| binding.buffer_id));
453
454        let accelerator = &self.accelerator;
455        let max_bindings = self.info.limits.max_bindings_per_submission;
456        let mut admission_status = StatusCode::OK;
457        let mut admission_requires_discard = false;
458        let result = self
459            .state
460            .create_event_with(queue_id, program_id, buffer_ids, |resources| {
461                let mut native_bindings = Vec::new();
462                native_bindings
463                    .try_reserve_exact(bindings.len())
464                    .map_err(|_| SubmitCreateError::OutOfMemory)?;
465                for binding in &bindings {
466                    let (buffer, info) = resources
467                        .buffer_with_info_by_id(binding.buffer_id)
468                        .map_err(SubmitCreateError::State)?;
469                    let desc = info.desc();
470                    if binding.range.end() > desc.bytes() {
471                        return Err(SubmitCreateError::Validation(StatusCode::OUT_OF_BOUNDS));
472                    }
473                    if !desc.allows_access(binding.access) {
474                        return Err(SubmitCreateError::Validation(StatusCode::PERMISSION_DENIED));
475                    }
476                    native_bindings.push(BindingRef {
477                        slot: binding.slot,
478                        buffer,
479                        range: binding.range,
480                        access: binding.access,
481                    });
482                }
483                validate_bindings(&native_bindings, max_bindings).map_err(|error| {
484                    SubmitCreateError::Validation(status_from_backend_error(error))
485                })?;
486                match accelerator.submit(
487                    resources.queue(),
488                    resources.program(),
489                    &native_bindings,
490                    timeout,
491                ) {
492                    Ok(event) => Ok(event),
493                    Err(SubmitFailure::Rejected(error)) => Err(SubmitCreateError::Rejected(error)),
494                    Err(SubmitFailure::Indeterminate { error, event }) => {
495                        admission_status = status_from_backend_error(error);
496                        admission_requires_discard = error == BackendError::DeviceLost;
497                        Ok(event)
498                    }
499                }
500            });
501
502        match result {
503            Ok(event_id) => {
504                if admission_requires_discard {
505                    self.require_backend_discard();
506                }
507                self.respond_event_id(response, admission_status, request_id, event_id, true)
508            }
509            Err(CreateError::State(error)) => self.respond_state_error(response, request_id, error),
510            Err(CreateError::Provider(SubmitCreateError::Rejected(error))) => {
511                self.respond_backend_error(response, request_id, error, false)
512            }
513            Err(CreateError::Provider(SubmitCreateError::OutOfMemory)) => {
514                self.respond_empty(response, StatusCode::OUT_OF_MEMORY, request_id, false)
515            }
516            Err(CreateError::Provider(SubmitCreateError::Validation(status))) => {
517                self.respond_empty(response, status, request_id, false)
518            }
519            Err(CreateError::Provider(SubmitCreateError::State(_))) => {
520                self.discard_response(response, request_id)
521            }
522        }
523    }
524
525    fn reset_events(&mut self, released: &mut ResourceCounts, progress: &mut ResetProgress) {
526        let mut cursor = 0;
527        while let Some((next, id)) = self.state.next_event_id(cursor) {
528            cursor = next;
529            if !progress.backend_callable {
530                break;
531            }
532            self.reset_event(id, released, progress);
533        }
534    }
535
536    fn reset_event(
537        &mut self,
538        id: ObjectId,
539        released: &mut ResourceCounts,
540        progress: &mut ResetProgress,
541    ) {
542        let mut event_state = {
543            let event = match self
544                .state
545                .event_record(id)
546                .and_then(|record| record.resource())
547            {
548                Ok(event) => event,
549                Err(_) => {
550                    progress.backend_reusable = false;
551                    return;
552                }
553            };
554            match self.accelerator.poll_event(event) {
555                Ok(state) => state,
556                Err(error) => {
557                    progress.backend_reusable = false;
558                    if error == BackendError::DeviceLost {
559                        progress.backend_callable = false;
560                    }
561                    return;
562                }
563            }
564        };
565
566        if event_state == EventState::Pending {
567            if !self
568                .info
569                .capabilities
570                .contains(Capabilities::EVENT_CANCELLATION)
571            {
572                progress.backend_reusable = false;
573                return;
574            }
575            let cancel_result = {
576                let event = match self
577                    .state
578                    .event_record(id)
579                    .and_then(|record| record.resource())
580                {
581                    Ok(event) => event,
582                    Err(_) => {
583                        progress.backend_reusable = false;
584                        return;
585                    }
586                };
587                self.accelerator.cancel_event(event)
588            };
589            match cancel_result {
590                Ok(()) => event_state = EventState::Cancelled,
591                Err(BackendError::Busy) => {
592                    let event = match self
593                        .state
594                        .event_record(id)
595                        .and_then(|record| record.resource())
596                    {
597                        Ok(event) => event,
598                        Err(_) => {
599                            progress.backend_reusable = false;
600                            return;
601                        }
602                    };
603                    match self.accelerator.poll_event(event) {
604                        Ok(state) => event_state = state,
605                        Err(error) => {
606                            progress.backend_reusable = false;
607                            if error == BackendError::DeviceLost {
608                                progress.backend_callable = false;
609                            }
610                            return;
611                        }
612                    }
613                }
614                Err(error) => {
615                    progress.backend_reusable = false;
616                    if error == BackendError::DeviceLost {
617                        progress.backend_callable = false;
618                    }
619                    return;
620                }
621            }
622        }
623
624        match event_state {
625            EventState::Pending => {
626                progress.backend_reusable = false;
627                return;
628            }
629            EventState::Failed(BackendError::DeviceLost) => {
630                progress.backend_reusable = false;
631                progress.backend_callable = false;
632                return;
633            }
634            EventState::Complete | EventState::Failed(_) | EventState::Cancelled => {}
635        }
636
637        let event = match self.state.begin_event_release(id) {
638            Ok(event) => event,
639            Err(_) => {
640                progress.backend_reusable = false;
641                return;
642            }
643        };
644        match self.accelerator.destroy_event(event) {
645            Ok(()) => match self.state.commit_event_release(id) {
646                Ok(()) => released.events += 1,
647                Err(_) => {
648                    progress.backend_reusable = false;
649                    progress.backend_callable = false;
650                }
651            },
652            Err(ReleaseFailure::Rejected { error, resource }) => {
653                progress.backend_reusable = false;
654                if error == BackendError::DeviceLost {
655                    progress.backend_callable = false;
656                }
657                if self.state.restore_event_release(id, resource).is_err() {
658                    progress.backend_callable = false;
659                }
660            }
661            Err(ReleaseFailure::Indeterminate { .. }) => {
662                progress.backend_reusable = false;
663                progress.backend_callable = false;
664            }
665        }
666    }
667
668    fn reset_queues(&mut self, released: &mut ResourceCounts, progress: &mut ResetProgress) {
669        let mut cursor = 0;
670        while let Some((next, id)) = self.state.next_queue_id(cursor) {
671            cursor = next;
672            let queue = match self.state.begin_queue_release(id) {
673                Ok(queue) => queue,
674                Err(_) => {
675                    progress.backend_reusable = false;
676                    continue;
677                }
678            };
679            match self.accelerator.destroy_queue(queue) {
680                Ok(()) => match self.state.commit_queue_release(id) {
681                    Ok(()) => released.queues += 1,
682                    Err(_) => {
683                        progress.backend_reusable = false;
684                        progress.backend_callable = false;
685                        break;
686                    }
687                },
688                Err(ReleaseFailure::Rejected { error, resource }) => {
689                    progress.backend_reusable = false;
690                    if error == BackendError::DeviceLost {
691                        progress.backend_callable = false;
692                    }
693                    if self.state.restore_queue_release(id, resource).is_err() {
694                        progress.backend_callable = false;
695                        break;
696                    }
697                }
698                Err(ReleaseFailure::Indeterminate { .. }) => {
699                    progress.backend_reusable = false;
700                    progress.backend_callable = false;
701                    break;
702                }
703            }
704        }
705    }
706
707    fn reset_programs(
708        &mut self,
709        released: &mut ResourceCounts,
710        released_bytes: &mut RetainedBytes,
711        progress: &mut ResetProgress,
712    ) {
713        let mut cursor = 0;
714        while let Some((next, id)) = self.state.next_program_id(cursor) {
715            cursor = next;
716            let resident_bytes = match self.state.program_record(id) {
717                Ok(record) => record.resident_bytes(),
718                Err(_) => {
719                    progress.backend_reusable = false;
720                    continue;
721                }
722            };
723            let program = match self.state.begin_program_release(id) {
724                Ok(program) => program,
725                Err(_) => {
726                    progress.backend_reusable = false;
727                    continue;
728                }
729            };
730            match self.accelerator.unload_program(program) {
731                Ok(()) => match self.state.commit_program_release(id) {
732                    Ok(()) => {
733                        released.programs += 1;
734                        released_bytes.program_resident += u128::from(resident_bytes);
735                    }
736                    Err(_) => {
737                        progress.backend_reusable = false;
738                        progress.backend_callable = false;
739                        break;
740                    }
741                },
742                Err(ReleaseFailure::Rejected { error, resource }) => {
743                    progress.backend_reusable = false;
744                    if error == BackendError::DeviceLost {
745                        progress.backend_callable = false;
746                    }
747                    if self.state.restore_program_release(id, resource).is_err() {
748                        progress.backend_callable = false;
749                        break;
750                    }
751                }
752                Err(ReleaseFailure::Indeterminate { .. }) => {
753                    progress.backend_reusable = false;
754                    progress.backend_callable = false;
755                    break;
756                }
757            }
758        }
759    }
760
761    fn reset_buffers(
762        &mut self,
763        released: &mut ResourceCounts,
764        released_bytes: &mut RetainedBytes,
765        progress: &mut ResetProgress,
766    ) {
767        let mut cursor = 0;
768        while let Some((next, id)) = self.state.next_buffer_id(cursor) {
769            cursor = next;
770            let allocation_bytes = match self.state.buffer_record(id) {
771                Ok(record) => record.info().allocation_bytes(),
772                Err(_) => {
773                    progress.backend_reusable = false;
774                    continue;
775                }
776            };
777            let buffer = match self.state.begin_buffer_release(id) {
778                Ok(buffer) => buffer,
779                Err(_) => {
780                    progress.backend_reusable = false;
781                    continue;
782                }
783            };
784            match self.accelerator.free_buffer(buffer) {
785                Ok(()) => match self.state.commit_buffer_release(id) {
786                    Ok(()) => {
787                        released.buffers += 1;
788                        released_bytes.buffer_backing += u128::from(allocation_bytes);
789                    }
790                    Err(_) => {
791                        progress.backend_reusable = false;
792                        progress.backend_callable = false;
793                        break;
794                    }
795                },
796                Err(ReleaseFailure::Rejected { error, resource }) => {
797                    progress.backend_reusable = false;
798                    if error == BackendError::DeviceLost {
799                        progress.backend_callable = false;
800                    }
801                    if self.state.restore_buffer_release(id, resource).is_err() {
802                        progress.backend_callable = false;
803                        break;
804                    }
805                }
806                Err(ReleaseFailure::Indeterminate { .. }) => {
807                    progress.backend_reusable = false;
808                    progress.backend_callable = false;
809                    break;
810                }
811            }
812        }
813    }
814
815    fn reset_contexts(&mut self, released: &mut ResourceCounts, progress: &mut ResetProgress) {
816        let mut cursor = 0;
817        while let Some((next, id)) = self.state.next_context_id(cursor) {
818            cursor = next;
819            let context = match self.state.begin_context_release(id) {
820                Ok(context) => context,
821                Err(_) => {
822                    progress.backend_reusable = false;
823                    continue;
824                }
825            };
826            match self.accelerator.destroy_context(context) {
827                Ok(()) => match self.state.commit_context_release(id) {
828                    Ok(()) => released.contexts += 1,
829                    Err(_) => {
830                        progress.backend_reusable = false;
831                        progress.backend_callable = false;
832                        break;
833                    }
834                },
835                Err(ReleaseFailure::Rejected { error, resource }) => {
836                    progress.backend_reusable = false;
837                    if error == BackendError::DeviceLost {
838                        progress.backend_callable = false;
839                    }
840                    if self.state.restore_context_release(id, resource).is_err() {
841                        progress.backend_callable = false;
842                        break;
843                    }
844                }
845                Err(ReleaseFailure::Indeterminate { .. }) => {
846                    progress.backend_reusable = false;
847                    progress.backend_callable = false;
848                    break;
849                }
850            }
851        }
852    }
853
854    fn poll_event(
855        &mut self,
856        response: &mut dyn ByteSink,
857        request_id: u64,
858        event_id: ObjectId,
859    ) -> Result<CommandOutcome, CommandProcessError> {
860        let event = match self.state.event_record(event_id) {
861            Ok(record) => match record.resource() {
862                Ok(event) => event,
863                Err(error) => return self.respond_state_error(response, request_id, error),
864            },
865            Err(error) => return self.respond_state_error(response, request_id, error),
866        };
867        match self.accelerator.poll_event(event) {
868            Ok(state) => {
869                if state == EventState::Failed(BackendError::DeviceLost) {
870                    self.require_backend_discard();
871                }
872                let payload = wire_event_state(state);
873                self.respond_bytes(
874                    response,
875                    StatusCode::OK,
876                    request_id,
877                    payload.as_bytes(),
878                    false,
879                )
880            }
881            Err(error) => self.respond_backend_error(response, request_id, error, false),
882        }
883    }
884
885    fn cancel_event(
886        &mut self,
887        response: &mut dyn ByteSink,
888        request_id: u64,
889        event_id: ObjectId,
890    ) -> Result<CommandOutcome, CommandProcessError> {
891        if self.info.validate_event_cancellation().is_err() {
892            return self.respond_empty(response, StatusCode::UNSUPPORTED, request_id, false);
893        }
894        let event = match self.state.event_record(event_id) {
895            Ok(record) => match record.resource() {
896                Ok(event) => event,
897                Err(error) => return self.respond_state_error(response, request_id, error),
898            },
899            Err(error) => return self.respond_state_error(response, request_id, error),
900        };
901        match self.accelerator.cancel_event(event) {
902            Ok(()) => self.respond_empty(response, StatusCode::OK, request_id, true),
903            Err(error) => self.respond_backend_error(response, request_id, error, false),
904        }
905    }
906
907    fn destroy_event(
908        &mut self,
909        response: &mut dyn ByteSink,
910        request_id: u64,
911        event_id: ObjectId,
912    ) -> Result<CommandOutcome, CommandProcessError> {
913        let state = {
914            let event = match self.state.event_record(event_id) {
915                Ok(record) => match record.resource() {
916                    Ok(event) => event,
917                    Err(error) => return self.respond_state_error(response, request_id, error),
918                },
919                Err(error) => return self.respond_state_error(response, request_id, error),
920            };
921            self.accelerator.poll_event(event)
922        };
923        match state {
924            Ok(EventState::Pending) => {
925                return self.respond_empty(response, StatusCode::BUSY, request_id, false);
926            }
927            Ok(EventState::Failed(BackendError::DeviceLost)) => {
928                self.require_backend_discard();
929                return self.respond_empty(response, StatusCode::DEVICE_LOST, request_id, false);
930            }
931            Ok(EventState::Complete | EventState::Failed(_) | EventState::Cancelled) => {}
932            Err(error) => {
933                return self.respond_backend_error(response, request_id, error, false);
934            }
935        }
936
937        let event = match self.state.begin_event_release(event_id) {
938            Ok(event) => event,
939            Err(error) => return self.respond_state_error(response, request_id, error),
940        };
941        match self.accelerator.destroy_event(event) {
942            Ok(()) => match self.state.commit_event_release(event_id) {
943                Ok(()) => self.respond_empty(response, StatusCode::OK, request_id, true),
944                Err(_) => self.discard_response(response, request_id),
945            },
946            Err(ReleaseFailure::Rejected { error, resource }) => {
947                match self.state.restore_event_release(event_id, resource) {
948                    Ok(()) => self.respond_backend_error(response, request_id, error, false),
949                    Err(_) => self.discard_response(response, request_id),
950                }
951            }
952            Err(ReleaseFailure::Indeterminate { .. }) => {
953                if self.state.commit_event_release(event_id).is_ok() {
954                    self.quarantined.events += 1;
955                }
956                self.discard_response(response, request_id)
957            }
958        }
959    }
960
961    fn release_unpublished_buffer(
962        &mut self,
963        response: &mut dyn ByteSink,
964        request_id: u64,
965        buffer_id: ObjectId,
966        admission_error: BackendError,
967    ) -> Result<CommandOutcome, CommandProcessError> {
968        let allocation_bytes = match self.state.buffer_record(buffer_id) {
969            Ok(record) => record.info().allocation_bytes(),
970            Err(_) => return self.discard_response(response, request_id),
971        };
972        let buffer = match self.state.begin_buffer_release(buffer_id) {
973            Ok(buffer) => buffer,
974            Err(_) => return self.discard_response(response, request_id),
975        };
976        match self.accelerator.free_buffer(buffer) {
977            Ok(()) => match self.state.commit_buffer_release(buffer_id) {
978                Ok(()) => self.respond_backend_error(response, request_id, admission_error, false),
979                Err(_) => self.discard_response(response, request_id),
980            },
981            Err(ReleaseFailure::Rejected { resource, .. }) => {
982                let _ = self.state.restore_buffer_release(buffer_id, resource);
983                self.discard_response(response, request_id)
984            }
985            Err(ReleaseFailure::Indeterminate { .. }) => {
986                if self.state.commit_buffer_release(buffer_id).is_ok() {
987                    self.quarantined.buffers += 1;
988                    self.quarantined_bytes.buffer_backing += u128::from(allocation_bytes);
989                }
990                self.discard_response(response, request_id)
991            }
992        }
993    }
994
995    fn write_buffer(
996        &mut self,
997        response: &mut dyn ByteSink,
998        request_id: u64,
999        buffer_id: ObjectId,
1000        range: BufferRange,
1001        data: &dyn ByteSource,
1002    ) -> Result<CommandOutcome, CommandProcessError> {
1003        let result = {
1004            let record = match self.state.buffer_record_mut(buffer_id) {
1005                Ok(record) => record,
1006                Err(error) => {
1007                    return self.respond_state_error(response, request_id, error);
1008                }
1009            };
1010            if let Err(status) = validate_transfer(record, range, BufferUsage::TRANSFER_DESTINATION)
1011            {
1012                return self.respond_empty(response, status, request_id, false);
1013            }
1014            let buffer = match record.resource_mut() {
1015                Ok(buffer) => buffer,
1016                Err(error) => {
1017                    return self.respond_state_error(response, request_id, error);
1018                }
1019            };
1020            self.accelerator.write_buffer(buffer, range.offset, data)
1021        };
1022
1023        match result {
1024            Ok(()) => self.respond_empty(response, StatusCode::OK, request_id, true),
1025            Err(error) => self.respond_backend_error(response, request_id, error, true),
1026        }
1027    }
1028
1029    fn read_buffer(
1030        &mut self,
1031        response: &mut dyn ByteSink,
1032        request_id: u64,
1033        buffer_id: ObjectId,
1034        range: BufferRange,
1035    ) -> Result<CommandOutcome, CommandProcessError> {
1036        let record = match self.state.buffer_record(buffer_id) {
1037            Ok(record) => record,
1038            Err(error) => {
1039                return self.respond_state_error(response, request_id, error);
1040            }
1041        };
1042        if let Err(status) = validate_transfer(record, range, BufferUsage::TRANSFER_SOURCE) {
1043            return self.respond_empty(response, status, request_id, false);
1044        }
1045        let buffer = match record.resource() {
1046            Ok(buffer) => buffer,
1047            Err(error) => {
1048                return self.respond_state_error(response, request_id, error);
1049            }
1050        };
1051
1052        let mut writer = ResponseWriter::new(response, self.decoder.limits().max_response_bytes());
1053        let error = {
1054            let mut payload = writer
1055                .payload(range.bytes())
1056                .map_err(CommandProcessError::ResponseWrite)?;
1057            match self
1058                .accelerator
1059                .read_buffer(buffer, range.offset, &mut payload)
1060            {
1061                Ok(()) => {
1062                    let result = payload.commit(StatusCode::OK, request_id);
1063                    return self.complete_response(result, StatusCode::OK, request_id, false);
1064                }
1065                Err(error) => error,
1066            }
1067        };
1068        let status = self.backend_status(error);
1069        let result = writer.write_empty(status, request_id);
1070        self.complete_response(result, status, request_id, false)
1071    }
1072
1073    fn destroy_context(
1074        &mut self,
1075        response: &mut dyn ByteSink,
1076        request_id: u64,
1077        id: ObjectId,
1078    ) -> Result<CommandOutcome, CommandProcessError> {
1079        let resource = match self.state.begin_context_release(id) {
1080            Ok(resource) => resource,
1081            Err(error) => return self.respond_state_error(response, request_id, error),
1082        };
1083        match self.accelerator.destroy_context(resource) {
1084            Ok(()) => match self.state.commit_context_release(id) {
1085                Ok(()) => self.respond_empty(response, StatusCode::OK, request_id, true),
1086                Err(_) => self.discard_response(response, request_id),
1087            },
1088            Err(ReleaseFailure::Rejected { error, resource }) => {
1089                match self.state.restore_context_release(id, resource) {
1090                    Ok(()) => self.respond_backend_error(response, request_id, error, false),
1091                    Err(_) => self.discard_response(response, request_id),
1092                }
1093            }
1094            Err(ReleaseFailure::Indeterminate { .. }) => {
1095                if self.state.commit_context_release(id).is_ok() {
1096                    self.quarantined.contexts += 1;
1097                }
1098                self.discard_response(response, request_id)
1099            }
1100        }
1101    }
1102
1103    fn free_buffer(
1104        &mut self,
1105        response: &mut dyn ByteSink,
1106        request_id: u64,
1107        id: ObjectId,
1108    ) -> Result<CommandOutcome, CommandProcessError> {
1109        let allocation_bytes = match self.state.buffer_record(id) {
1110            Ok(record) => record.info().allocation_bytes(),
1111            Err(error) => return self.respond_state_error(response, request_id, error),
1112        };
1113        let resource = match self.state.begin_buffer_release(id) {
1114            Ok(resource) => resource,
1115            Err(error) => return self.respond_state_error(response, request_id, error),
1116        };
1117        match self.accelerator.free_buffer(resource) {
1118            Ok(()) => match self.state.commit_buffer_release(id) {
1119                Ok(()) => self.respond_empty(response, StatusCode::OK, request_id, true),
1120                Err(_) => self.discard_response(response, request_id),
1121            },
1122            Err(ReleaseFailure::Rejected { error, resource }) => {
1123                match self.state.restore_buffer_release(id, resource) {
1124                    Ok(()) => self.respond_backend_error(response, request_id, error, false),
1125                    Err(_) => self.discard_response(response, request_id),
1126                }
1127            }
1128            Err(ReleaseFailure::Indeterminate { .. }) => {
1129                if self.state.commit_buffer_release(id).is_ok() {
1130                    self.quarantined.buffers += 1;
1131                    self.quarantined_bytes.buffer_backing += u128::from(allocation_bytes);
1132                }
1133                self.discard_response(response, request_id)
1134            }
1135        }
1136    }
1137
1138    fn unload_program(
1139        &mut self,
1140        response: &mut dyn ByteSink,
1141        request_id: u64,
1142        id: ObjectId,
1143    ) -> Result<CommandOutcome, CommandProcessError> {
1144        let resident_bytes = match self.state.program_record(id) {
1145            Ok(record) => record.resident_bytes(),
1146            Err(error) => return self.respond_state_error(response, request_id, error),
1147        };
1148        let resource = match self.state.begin_program_release(id) {
1149            Ok(resource) => resource,
1150            Err(error) => return self.respond_state_error(response, request_id, error),
1151        };
1152        match self.accelerator.unload_program(resource) {
1153            Ok(()) => match self.state.commit_program_release(id) {
1154                Ok(()) => self.respond_empty(response, StatusCode::OK, request_id, true),
1155                Err(_) => self.discard_response(response, request_id),
1156            },
1157            Err(ReleaseFailure::Rejected { error, resource }) => {
1158                match self.state.restore_program_release(id, resource) {
1159                    Ok(()) => self.respond_backend_error(response, request_id, error, false),
1160                    Err(_) => self.discard_response(response, request_id),
1161                }
1162            }
1163            Err(ReleaseFailure::Indeterminate { .. }) => {
1164                if self.state.commit_program_release(id).is_ok() {
1165                    self.quarantined.programs += 1;
1166                    self.quarantined_bytes.program_resident += u128::from(resident_bytes);
1167                }
1168                self.discard_response(response, request_id)
1169            }
1170        }
1171    }
1172
1173    fn destroy_queue(
1174        &mut self,
1175        response: &mut dyn ByteSink,
1176        request_id: u64,
1177        id: ObjectId,
1178    ) -> Result<CommandOutcome, CommandProcessError> {
1179        let resource = match self.state.begin_queue_release(id) {
1180            Ok(resource) => resource,
1181            Err(error) => return self.respond_state_error(response, request_id, error),
1182        };
1183        match self.accelerator.destroy_queue(resource) {
1184            Ok(()) => match self.state.commit_queue_release(id) {
1185                Ok(()) => self.respond_empty(response, StatusCode::OK, request_id, true),
1186                Err(_) => self.discard_response(response, request_id),
1187            },
1188            Err(ReleaseFailure::Rejected { error, resource }) => {
1189                match self.state.restore_queue_release(id, resource) {
1190                    Ok(()) => self.respond_backend_error(response, request_id, error, false),
1191                    Err(_) => self.discard_response(response, request_id),
1192                }
1193            }
1194            Err(ReleaseFailure::Indeterminate { .. }) => {
1195                if self.state.commit_queue_release(id).is_ok() {
1196                    self.quarantined.queues += 1;
1197                }
1198                self.discard_response(response, request_id)
1199            }
1200        }
1201    }
1202
1203    fn respond_create_error(
1204        &mut self,
1205        response: &mut dyn ByteSink,
1206        request_id: u64,
1207        error: CreateError<BackendError>,
1208    ) -> Result<CommandOutcome, CommandProcessError> {
1209        match error {
1210            CreateError::State(error) => self.respond_state_error(response, request_id, error),
1211            CreateError::Provider(error) => {
1212                self.respond_backend_error(response, request_id, error, false)
1213            }
1214        }
1215    }
1216
1217    fn respond_state_error(
1218        &mut self,
1219        response: &mut dyn ByteSink,
1220        request_id: u64,
1221        error: DeviceStateError,
1222    ) -> Result<CommandOutcome, CommandProcessError> {
1223        self.respond_empty(
1224            response,
1225            status_from_device_state_error(error),
1226            request_id,
1227            false,
1228        )
1229    }
1230
1231    fn respond_backend_error(
1232        &mut self,
1233        response: &mut dyn ByteSink,
1234        request_id: u64,
1235        error: BackendError,
1236        mutated: bool,
1237    ) -> Result<CommandOutcome, CommandProcessError> {
1238        let status = self.backend_status(error);
1239        self.respond_empty(response, status, request_id, mutated)
1240    }
1241
1242    fn backend_status(&mut self, error: BackendError) -> StatusCode {
1243        if error == BackendError::DeviceLost {
1244            self.require_backend_discard();
1245        }
1246        status_from_backend_error(error)
1247    }
1248
1249    fn require_backend_discard(&mut self) {
1250        self.health = DeviceHealth::BackendDiscardRequired;
1251        self.last_reset = None;
1252    }
1253
1254    fn discard_response(
1255        &mut self,
1256        response: &mut dyn ByteSink,
1257        request_id: u64,
1258    ) -> Result<CommandOutcome, CommandProcessError> {
1259        self.require_backend_discard();
1260        self.respond_empty(response, StatusCode::DEVICE_LOST, request_id, true)
1261    }
1262
1263    fn discard_report(
1264        &self,
1265        released: ResourceCounts,
1266        released_bytes: RetainedBytes,
1267    ) -> ResetReport {
1268        ResetReport {
1269            disposition: ResetDisposition::BackendDiscardRequired,
1270            released,
1271            quarantined: self
1272                .quarantined
1273                .saturating_add(self.state.resource_counts()),
1274            released_bytes,
1275            quarantined_bytes: self
1276                .quarantined_bytes
1277                .saturating_add(self.state.retained_bytes()),
1278        }
1279    }
1280
1281    fn respond_object(
1282        &mut self,
1283        response: &mut dyn ByteSink,
1284        request_id: u64,
1285        id: ObjectId,
1286        mutated: bool,
1287    ) -> Result<CommandOutcome, CommandProcessError> {
1288        let payload = ObjectPayload {
1289            object_id: Le64::new(id.get()),
1290        };
1291        self.respond_bytes(
1292            response,
1293            StatusCode::OK,
1294            request_id,
1295            payload.as_bytes(),
1296            mutated,
1297        )
1298    }
1299
1300    fn respond_event_id(
1301        &mut self,
1302        response: &mut dyn ByteSink,
1303        status: StatusCode,
1304        request_id: u64,
1305        id: ObjectId,
1306        mutated: bool,
1307    ) -> Result<CommandOutcome, CommandProcessError> {
1308        let payload = SubmitResponse {
1309            event_id: Le64::new(id.get()),
1310        };
1311        self.respond_bytes(response, status, request_id, payload.as_bytes(), mutated)
1312    }
1313
1314    fn respond_bytes(
1315        &mut self,
1316        response: &mut dyn ByteSink,
1317        status: StatusCode,
1318        request_id: u64,
1319        payload: &[u8],
1320        mutated: bool,
1321    ) -> Result<CommandOutcome, CommandProcessError> {
1322        let result = (|| {
1323            let mut writer =
1324                ResponseWriter::new(response, self.decoder.limits().max_response_bytes());
1325            let payload_bytes =
1326                u64::try_from(payload.len()).map_err(|_| ResponseWriteError::FrameTooLarge)?;
1327            let mut destination = writer.payload(payload_bytes)?;
1328            destination
1329                .write_at(0, payload)
1330                .map_err(|_| ResponseWriteError::SinkAccess)?;
1331            destination.commit(status, request_id)
1332        })();
1333        self.complete_response(result, status, request_id, mutated)
1334    }
1335
1336    fn respond_empty(
1337        &mut self,
1338        response: &mut dyn ByteSink,
1339        status: StatusCode,
1340        request_id: u64,
1341        mutated: bool,
1342    ) -> Result<CommandOutcome, CommandProcessError> {
1343        let result = ResponseWriter::new(response, self.decoder.limits().max_response_bytes())
1344            .write_empty(status, request_id);
1345        self.complete_response(result, status, request_id, mutated)
1346    }
1347
1348    fn complete_response(
1349        &mut self,
1350        result: Result<u32, ResponseWriteError>,
1351        status: StatusCode,
1352        request_id: u64,
1353        mutated: bool,
1354    ) -> Result<CommandOutcome, CommandProcessError> {
1355        match result {
1356            Ok(used) => Ok(CommandOutcome::Response {
1357                request_id,
1358                status,
1359                used,
1360            }),
1361            Err(error) => {
1362                if mutated && self.health == DeviceHealth::Running {
1363                    self.health = DeviceHealth::NeedsReset;
1364                    self.last_reset = None;
1365                }
1366                Err(CommandProcessError::ResponseWrite(error))
1367            }
1368        }
1369    }
1370}
1371
1372#[derive(Clone, Copy, Debug)]
1373struct ResetProgress {
1374    backend_reusable: bool,
1375    backend_callable: bool,
1376}
1377
1378impl Default for ResetProgress {
1379    fn default() -> Self {
1380        Self {
1381            backend_reusable: true,
1382            backend_callable: true,
1383        }
1384    }
1385}
1386
1387fn validate_transfer<B>(
1388    record: &BufferRecord<B>,
1389    range: BufferRange,
1390    required_usage: BufferUsage,
1391) -> Result<(), StatusCode> {
1392    if record.in_flight() != 0 {
1393        return Err(StatusCode::BUSY);
1394    }
1395    let desc = record.info().desc();
1396    if range.end() > desc.bytes() {
1397        return Err(StatusCode::OUT_OF_BOUNDS);
1398    }
1399    if !desc.usage.contains(required_usage) {
1400        return Err(StatusCode::PERMISSION_DENIED);
1401    }
1402    Ok(())
1403}
1404
1405fn wire_device_info(info: DeviceInfo) -> WireDeviceInfo {
1406    WireDeviceInfo {
1407        uuid: info.identity.uuid,
1408        class: Le16::new(info.identity.class.get()),
1409        reserved: Le16::new(0),
1410        vendor_id: Le32::new(info.identity.vendor_id),
1411        device_id: Le32::new(info.identity.device_id),
1412        capabilities: Le64::new(info.capabilities.bits()),
1413        max_contexts: Le32::new(info.limits.max_contexts),
1414        max_buffers_per_context: Le32::new(info.limits.max_buffers_per_context),
1415        max_programs_per_context: Le32::new(info.limits.max_programs_per_context),
1416        max_queues_per_context: Le32::new(info.limits.max_queues_per_context),
1417        max_events_per_context: Le32::new(info.limits.max_events_per_context),
1418        max_bindings_per_submission: Le32::new(info.limits.max_bindings_per_submission),
1419        max_buffer_bytes: Le64::new(info.limits.max_buffer_bytes),
1420        max_artifact_bytes: Le64::new(info.limits.max_artifact_bytes),
1421    }
1422}
1423
1424fn wire_event_state(state: EventState) -> WireEventState {
1425    let (state, error) = match state {
1426        EventState::Pending => (KnownEventState::Pending, StatusCode::OK),
1427        EventState::Complete => (KnownEventState::Complete, StatusCode::OK),
1428        EventState::Failed(error) => (KnownEventState::Failed, status_from_backend_error(error)),
1429        EventState::Cancelled => (KnownEventState::Cancelled, StatusCode::OK),
1430    };
1431    WireEventState {
1432        state: Le16::new(state as u16),
1433        error: Le16::new(error.0),
1434        reserved: Le32::new(0),
1435    }
1436}