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, BufferDesc, BufferRange, BufferUsage,
12    ByteSink, ByteSource, Capabilities, DeviceInfo, DeviceInfoError, EventState, ReleaseFailure,
13    SubmitFailure, Timeout,
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                let mut descs: Vec<BufferDesc> = Vec::new();
463                native_bindings
464                    .try_reserve_exact(bindings.len())
465                    .map_err(|_| SubmitCreateError::OutOfMemory)?;
466                descs
467                    .try_reserve_exact(bindings.len())
468                    .map_err(|_| SubmitCreateError::OutOfMemory)?;
469                for binding in &bindings {
470                    let (buffer, info) = resources
471                        .buffer_with_info_by_id(binding.buffer_id)
472                        .map_err(SubmitCreateError::State)?;
473                    let desc = info.desc();
474                    if binding.range.end() > desc.bytes() {
475                        return Err(SubmitCreateError::Validation(StatusCode::OUT_OF_BOUNDS));
476                    }
477                    descs.push(desc);
478                    native_bindings.push(BindingRef {
479                        slot: binding.slot,
480                        buffer,
481                        range: binding.range,
482                        access: binding.access,
483                    });
484                }
485                BindingRef::validate_for_submit(&native_bindings, &descs, max_bindings).map_err(
486                    |error| SubmitCreateError::Validation(status_from_backend_error(error)),
487                )?;
488                match accelerator.submit(
489                    resources.queue(),
490                    resources.program(),
491                    &native_bindings,
492                    timeout,
493                ) {
494                    Ok(event) => Ok(event),
495                    Err(SubmitFailure::Rejected(error)) => Err(SubmitCreateError::Rejected(error)),
496                    Err(SubmitFailure::Indeterminate { error, event }) => {
497                        admission_status = status_from_backend_error(error);
498                        admission_requires_discard = error == BackendError::DeviceLost;
499                        Ok(event)
500                    }
501                }
502            });
503
504        match result {
505            Ok(event_id) => {
506                if admission_requires_discard {
507                    self.require_backend_discard();
508                }
509                self.respond_event_id(response, admission_status, request_id, event_id, true)
510            }
511            Err(CreateError::State(error)) => self.respond_state_error(response, request_id, error),
512            Err(CreateError::Provider(SubmitCreateError::Rejected(error))) => {
513                self.respond_backend_error(response, request_id, error, false)
514            }
515            Err(CreateError::Provider(SubmitCreateError::OutOfMemory)) => {
516                self.respond_empty(response, StatusCode::OUT_OF_MEMORY, request_id, false)
517            }
518            Err(CreateError::Provider(SubmitCreateError::Validation(status))) => {
519                self.respond_empty(response, status, request_id, false)
520            }
521            Err(CreateError::Provider(SubmitCreateError::State(_))) => {
522                self.discard_response(response, request_id)
523            }
524        }
525    }
526
527    fn reset_events(&mut self, released: &mut ResourceCounts, progress: &mut ResetProgress) {
528        let mut cursor = 0;
529        while let Some((next, id)) = self.state.next_event_id(cursor) {
530            cursor = next;
531            if !progress.backend_callable {
532                break;
533            }
534            self.reset_event(id, released, progress);
535        }
536    }
537
538    fn reset_event(
539        &mut self,
540        id: ObjectId,
541        released: &mut ResourceCounts,
542        progress: &mut ResetProgress,
543    ) {
544        let mut event_state = {
545            let event = match self
546                .state
547                .event_record(id)
548                .and_then(|record| record.resource())
549            {
550                Ok(event) => event,
551                Err(_) => {
552                    progress.backend_reusable = false;
553                    return;
554                }
555            };
556            match self.accelerator.poll_event(event) {
557                Ok(state) => state,
558                Err(error) => {
559                    progress.backend_reusable = false;
560                    if error == BackendError::DeviceLost {
561                        progress.backend_callable = false;
562                    }
563                    return;
564                }
565            }
566        };
567
568        if event_state == EventState::Pending {
569            if !self
570                .info
571                .capabilities
572                .contains(Capabilities::EVENT_CANCELLATION)
573            {
574                progress.backend_reusable = false;
575                return;
576            }
577            let cancel_result = {
578                let event = match self
579                    .state
580                    .event_record(id)
581                    .and_then(|record| record.resource())
582                {
583                    Ok(event) => event,
584                    Err(_) => {
585                        progress.backend_reusable = false;
586                        return;
587                    }
588                };
589                self.accelerator.cancel_event(event)
590            };
591            match cancel_result {
592                Ok(()) => event_state = EventState::Cancelled,
593                Err(BackendError::Busy) => {
594                    let event = match self
595                        .state
596                        .event_record(id)
597                        .and_then(|record| record.resource())
598                    {
599                        Ok(event) => event,
600                        Err(_) => {
601                            progress.backend_reusable = false;
602                            return;
603                        }
604                    };
605                    match self.accelerator.poll_event(event) {
606                        Ok(state) => event_state = state,
607                        Err(error) => {
608                            progress.backend_reusable = false;
609                            if error == BackendError::DeviceLost {
610                                progress.backend_callable = false;
611                            }
612                            return;
613                        }
614                    }
615                }
616                Err(error) => {
617                    progress.backend_reusable = false;
618                    if error == BackendError::DeviceLost {
619                        progress.backend_callable = false;
620                    }
621                    return;
622                }
623            }
624        }
625
626        match event_state {
627            EventState::Pending => {
628                progress.backend_reusable = false;
629                return;
630            }
631            EventState::Failed(BackendError::DeviceLost) => {
632                progress.backend_reusable = false;
633                progress.backend_callable = false;
634                return;
635            }
636            EventState::Complete | EventState::Failed(_) | EventState::Cancelled => {}
637        }
638
639        let event = match self.state.begin_event_release(id) {
640            Ok(event) => event,
641            Err(_) => {
642                progress.backend_reusable = false;
643                return;
644            }
645        };
646        match self.accelerator.destroy_event(event) {
647            Ok(()) => match self.state.commit_event_release(id) {
648                Ok(()) => released.events += 1,
649                Err(_) => {
650                    progress.backend_reusable = false;
651                    progress.backend_callable = false;
652                }
653            },
654            Err(ReleaseFailure::Rejected { error, resource }) => {
655                progress.backend_reusable = false;
656                if error == BackendError::DeviceLost {
657                    progress.backend_callable = false;
658                }
659                if self.state.restore_event_release(id, resource).is_err() {
660                    progress.backend_callable = false;
661                }
662            }
663            Err(ReleaseFailure::Indeterminate { .. }) => {
664                progress.backend_reusable = false;
665                progress.backend_callable = false;
666            }
667        }
668    }
669
670    fn reset_queues(&mut self, released: &mut ResourceCounts, progress: &mut ResetProgress) {
671        let mut cursor = 0;
672        while let Some((next, id)) = self.state.next_queue_id(cursor) {
673            cursor = next;
674            let queue = match self.state.begin_queue_release(id) {
675                Ok(queue) => queue,
676                Err(_) => {
677                    progress.backend_reusable = false;
678                    continue;
679                }
680            };
681            match self.accelerator.destroy_queue(queue) {
682                Ok(()) => match self.state.commit_queue_release(id) {
683                    Ok(()) => released.queues += 1,
684                    Err(_) => {
685                        progress.backend_reusable = false;
686                        progress.backend_callable = false;
687                        break;
688                    }
689                },
690                Err(ReleaseFailure::Rejected { error, resource }) => {
691                    progress.backend_reusable = false;
692                    if error == BackendError::DeviceLost {
693                        progress.backend_callable = false;
694                    }
695                    if self.state.restore_queue_release(id, resource).is_err() {
696                        progress.backend_callable = false;
697                        break;
698                    }
699                }
700                Err(ReleaseFailure::Indeterminate { .. }) => {
701                    progress.backend_reusable = false;
702                    progress.backend_callable = false;
703                    break;
704                }
705            }
706        }
707    }
708
709    fn reset_programs(
710        &mut self,
711        released: &mut ResourceCounts,
712        released_bytes: &mut RetainedBytes,
713        progress: &mut ResetProgress,
714    ) {
715        let mut cursor = 0;
716        while let Some((next, id)) = self.state.next_program_id(cursor) {
717            cursor = next;
718            let resident_bytes = match self.state.program_record(id) {
719                Ok(record) => record.resident_bytes(),
720                Err(_) => {
721                    progress.backend_reusable = false;
722                    continue;
723                }
724            };
725            let program = match self.state.begin_program_release(id) {
726                Ok(program) => program,
727                Err(_) => {
728                    progress.backend_reusable = false;
729                    continue;
730                }
731            };
732            match self.accelerator.unload_program(program) {
733                Ok(()) => match self.state.commit_program_release(id) {
734                    Ok(()) => {
735                        released.programs += 1;
736                        released_bytes.program_resident += u128::from(resident_bytes);
737                    }
738                    Err(_) => {
739                        progress.backend_reusable = false;
740                        progress.backend_callable = false;
741                        break;
742                    }
743                },
744                Err(ReleaseFailure::Rejected { error, resource }) => {
745                    progress.backend_reusable = false;
746                    if error == BackendError::DeviceLost {
747                        progress.backend_callable = false;
748                    }
749                    if self.state.restore_program_release(id, resource).is_err() {
750                        progress.backend_callable = false;
751                        break;
752                    }
753                }
754                Err(ReleaseFailure::Indeterminate { .. }) => {
755                    progress.backend_reusable = false;
756                    progress.backend_callable = false;
757                    break;
758                }
759            }
760        }
761    }
762
763    fn reset_buffers(
764        &mut self,
765        released: &mut ResourceCounts,
766        released_bytes: &mut RetainedBytes,
767        progress: &mut ResetProgress,
768    ) {
769        let mut cursor = 0;
770        while let Some((next, id)) = self.state.next_buffer_id(cursor) {
771            cursor = next;
772            let allocation_bytes = match self.state.buffer_record(id) {
773                Ok(record) => record.info().allocation_bytes(),
774                Err(_) => {
775                    progress.backend_reusable = false;
776                    continue;
777                }
778            };
779            let buffer = match self.state.begin_buffer_release(id) {
780                Ok(buffer) => buffer,
781                Err(_) => {
782                    progress.backend_reusable = false;
783                    continue;
784                }
785            };
786            match self.accelerator.free_buffer(buffer) {
787                Ok(()) => match self.state.commit_buffer_release(id) {
788                    Ok(()) => {
789                        released.buffers += 1;
790                        released_bytes.buffer_backing += u128::from(allocation_bytes);
791                    }
792                    Err(_) => {
793                        progress.backend_reusable = false;
794                        progress.backend_callable = false;
795                        break;
796                    }
797                },
798                Err(ReleaseFailure::Rejected { error, resource }) => {
799                    progress.backend_reusable = false;
800                    if error == BackendError::DeviceLost {
801                        progress.backend_callable = false;
802                    }
803                    if self.state.restore_buffer_release(id, resource).is_err() {
804                        progress.backend_callable = false;
805                        break;
806                    }
807                }
808                Err(ReleaseFailure::Indeterminate { .. }) => {
809                    progress.backend_reusable = false;
810                    progress.backend_callable = false;
811                    break;
812                }
813            }
814        }
815    }
816
817    fn reset_contexts(&mut self, released: &mut ResourceCounts, progress: &mut ResetProgress) {
818        let mut cursor = 0;
819        while let Some((next, id)) = self.state.next_context_id(cursor) {
820            cursor = next;
821            let context = match self.state.begin_context_release(id) {
822                Ok(context) => context,
823                Err(_) => {
824                    progress.backend_reusable = false;
825                    continue;
826                }
827            };
828            match self.accelerator.destroy_context(context) {
829                Ok(()) => match self.state.commit_context_release(id) {
830                    Ok(()) => released.contexts += 1,
831                    Err(_) => {
832                        progress.backend_reusable = false;
833                        progress.backend_callable = false;
834                        break;
835                    }
836                },
837                Err(ReleaseFailure::Rejected { error, resource }) => {
838                    progress.backend_reusable = false;
839                    if error == BackendError::DeviceLost {
840                        progress.backend_callable = false;
841                    }
842                    if self.state.restore_context_release(id, resource).is_err() {
843                        progress.backend_callable = false;
844                        break;
845                    }
846                }
847                Err(ReleaseFailure::Indeterminate { .. }) => {
848                    progress.backend_reusable = false;
849                    progress.backend_callable = false;
850                    break;
851                }
852            }
853        }
854    }
855
856    fn poll_event(
857        &mut self,
858        response: &mut dyn ByteSink,
859        request_id: u64,
860        event_id: ObjectId,
861    ) -> Result<CommandOutcome, CommandProcessError> {
862        let event = match self.state.event_record(event_id) {
863            Ok(record) => match record.resource() {
864                Ok(event) => event,
865                Err(error) => return self.respond_state_error(response, request_id, error),
866            },
867            Err(error) => return self.respond_state_error(response, request_id, error),
868        };
869        match self.accelerator.poll_event(event) {
870            Ok(state) => {
871                if state == EventState::Failed(BackendError::DeviceLost) {
872                    self.require_backend_discard();
873                }
874                let payload = wire_event_state(state);
875                self.respond_bytes(
876                    response,
877                    StatusCode::OK,
878                    request_id,
879                    payload.as_bytes(),
880                    false,
881                )
882            }
883            Err(error) => self.respond_backend_error(response, request_id, error, false),
884        }
885    }
886
887    fn cancel_event(
888        &mut self,
889        response: &mut dyn ByteSink,
890        request_id: u64,
891        event_id: ObjectId,
892    ) -> Result<CommandOutcome, CommandProcessError> {
893        if self.info.validate_event_cancellation().is_err() {
894            return self.respond_empty(response, StatusCode::UNSUPPORTED, request_id, false);
895        }
896        let event = match self.state.event_record(event_id) {
897            Ok(record) => match record.resource() {
898                Ok(event) => event,
899                Err(error) => return self.respond_state_error(response, request_id, error),
900            },
901            Err(error) => return self.respond_state_error(response, request_id, error),
902        };
903        match self.accelerator.cancel_event(event) {
904            Ok(()) => self.respond_empty(response, StatusCode::OK, request_id, true),
905            Err(error) => self.respond_backend_error(response, request_id, error, false),
906        }
907    }
908
909    fn destroy_event(
910        &mut self,
911        response: &mut dyn ByteSink,
912        request_id: u64,
913        event_id: ObjectId,
914    ) -> Result<CommandOutcome, CommandProcessError> {
915        let state = {
916            let event = match self.state.event_record(event_id) {
917                Ok(record) => match record.resource() {
918                    Ok(event) => event,
919                    Err(error) => return self.respond_state_error(response, request_id, error),
920                },
921                Err(error) => return self.respond_state_error(response, request_id, error),
922            };
923            self.accelerator.poll_event(event)
924        };
925        match state {
926            Ok(EventState::Pending) => {
927                return self.respond_empty(response, StatusCode::BUSY, request_id, false);
928            }
929            Ok(EventState::Failed(BackendError::DeviceLost)) => {
930                self.require_backend_discard();
931                return self.respond_empty(response, StatusCode::DEVICE_LOST, request_id, false);
932            }
933            Ok(EventState::Complete | EventState::Failed(_) | EventState::Cancelled) => {}
934            Err(error) => {
935                return self.respond_backend_error(response, request_id, error, false);
936            }
937        }
938
939        let event = match self.state.begin_event_release(event_id) {
940            Ok(event) => event,
941            Err(error) => return self.respond_state_error(response, request_id, error),
942        };
943        match self.accelerator.destroy_event(event) {
944            Ok(()) => match self.state.commit_event_release(event_id) {
945                Ok(()) => self.respond_empty(response, StatusCode::OK, request_id, true),
946                Err(_) => self.discard_response(response, request_id),
947            },
948            Err(ReleaseFailure::Rejected { error, resource }) => {
949                match self.state.restore_event_release(event_id, resource) {
950                    Ok(()) => self.respond_backend_error(response, request_id, error, false),
951                    Err(_) => self.discard_response(response, request_id),
952                }
953            }
954            Err(ReleaseFailure::Indeterminate { .. }) => {
955                if self.state.commit_event_release(event_id).is_ok() {
956                    self.quarantined.events += 1;
957                }
958                self.discard_response(response, request_id)
959            }
960        }
961    }
962
963    fn release_unpublished_buffer(
964        &mut self,
965        response: &mut dyn ByteSink,
966        request_id: u64,
967        buffer_id: ObjectId,
968        admission_error: BackendError,
969    ) -> Result<CommandOutcome, CommandProcessError> {
970        let allocation_bytes = match self.state.buffer_record(buffer_id) {
971            Ok(record) => record.info().allocation_bytes(),
972            Err(_) => return self.discard_response(response, request_id),
973        };
974        let buffer = match self.state.begin_buffer_release(buffer_id) {
975            Ok(buffer) => buffer,
976            Err(_) => return self.discard_response(response, request_id),
977        };
978        match self.accelerator.free_buffer(buffer) {
979            Ok(()) => match self.state.commit_buffer_release(buffer_id) {
980                Ok(()) => self.respond_backend_error(response, request_id, admission_error, false),
981                Err(_) => self.discard_response(response, request_id),
982            },
983            Err(ReleaseFailure::Rejected { resource, .. }) => {
984                let _ = self.state.restore_buffer_release(buffer_id, resource);
985                self.discard_response(response, request_id)
986            }
987            Err(ReleaseFailure::Indeterminate { .. }) => {
988                if self.state.commit_buffer_release(buffer_id).is_ok() {
989                    self.quarantined.buffers += 1;
990                    self.quarantined_bytes.buffer_backing += u128::from(allocation_bytes);
991                }
992                self.discard_response(response, request_id)
993            }
994        }
995    }
996
997    fn write_buffer(
998        &mut self,
999        response: &mut dyn ByteSink,
1000        request_id: u64,
1001        buffer_id: ObjectId,
1002        range: BufferRange,
1003        data: &dyn ByteSource,
1004    ) -> Result<CommandOutcome, CommandProcessError> {
1005        let result = {
1006            let record = match self.state.buffer_record_mut(buffer_id) {
1007                Ok(record) => record,
1008                Err(error) => {
1009                    return self.respond_state_error(response, request_id, error);
1010                }
1011            };
1012            if let Err(status) = validate_transfer(record, range, BufferUsage::TRANSFER_DESTINATION)
1013            {
1014                return self.respond_empty(response, status, request_id, false);
1015            }
1016            let buffer = match record.resource_mut() {
1017                Ok(buffer) => buffer,
1018                Err(error) => {
1019                    return self.respond_state_error(response, request_id, error);
1020                }
1021            };
1022            self.accelerator.write_buffer(buffer, range.offset, data)
1023        };
1024
1025        match result {
1026            Ok(()) => self.respond_empty(response, StatusCode::OK, request_id, true),
1027            Err(error) => self.respond_backend_error(response, request_id, error, true),
1028        }
1029    }
1030
1031    fn read_buffer(
1032        &mut self,
1033        response: &mut dyn ByteSink,
1034        request_id: u64,
1035        buffer_id: ObjectId,
1036        range: BufferRange,
1037    ) -> Result<CommandOutcome, CommandProcessError> {
1038        let record = match self.state.buffer_record(buffer_id) {
1039            Ok(record) => record,
1040            Err(error) => {
1041                return self.respond_state_error(response, request_id, error);
1042            }
1043        };
1044        if let Err(status) = validate_transfer(record, range, BufferUsage::TRANSFER_SOURCE) {
1045            return self.respond_empty(response, status, request_id, false);
1046        }
1047        let buffer = match record.resource() {
1048            Ok(buffer) => buffer,
1049            Err(error) => {
1050                return self.respond_state_error(response, request_id, error);
1051            }
1052        };
1053
1054        let mut writer = ResponseWriter::new(response, self.decoder.limits().max_response_bytes());
1055        let error = {
1056            let mut payload = writer
1057                .payload(range.bytes())
1058                .map_err(CommandProcessError::ResponseWrite)?;
1059            match self
1060                .accelerator
1061                .read_buffer(buffer, range.offset, &mut payload)
1062            {
1063                Ok(()) => {
1064                    let result = payload.commit(StatusCode::OK, request_id);
1065                    return self.complete_response(result, StatusCode::OK, request_id, false);
1066                }
1067                Err(error) => error,
1068            }
1069        };
1070        let status = self.backend_status(error);
1071        let result = writer.write_empty(status, request_id);
1072        self.complete_response(result, status, request_id, false)
1073    }
1074
1075    fn destroy_context(
1076        &mut self,
1077        response: &mut dyn ByteSink,
1078        request_id: u64,
1079        id: ObjectId,
1080    ) -> Result<CommandOutcome, CommandProcessError> {
1081        let resource = match self.state.begin_context_release(id) {
1082            Ok(resource) => resource,
1083            Err(error) => return self.respond_state_error(response, request_id, error),
1084        };
1085        match self.accelerator.destroy_context(resource) {
1086            Ok(()) => match self.state.commit_context_release(id) {
1087                Ok(()) => self.respond_empty(response, StatusCode::OK, request_id, true),
1088                Err(_) => self.discard_response(response, request_id),
1089            },
1090            Err(ReleaseFailure::Rejected { error, resource }) => {
1091                match self.state.restore_context_release(id, resource) {
1092                    Ok(()) => self.respond_backend_error(response, request_id, error, false),
1093                    Err(_) => self.discard_response(response, request_id),
1094                }
1095            }
1096            Err(ReleaseFailure::Indeterminate { .. }) => {
1097                if self.state.commit_context_release(id).is_ok() {
1098                    self.quarantined.contexts += 1;
1099                }
1100                self.discard_response(response, request_id)
1101            }
1102        }
1103    }
1104
1105    fn free_buffer(
1106        &mut self,
1107        response: &mut dyn ByteSink,
1108        request_id: u64,
1109        id: ObjectId,
1110    ) -> Result<CommandOutcome, CommandProcessError> {
1111        let allocation_bytes = match self.state.buffer_record(id) {
1112            Ok(record) => record.info().allocation_bytes(),
1113            Err(error) => return self.respond_state_error(response, request_id, error),
1114        };
1115        let resource = match self.state.begin_buffer_release(id) {
1116            Ok(resource) => resource,
1117            Err(error) => return self.respond_state_error(response, request_id, error),
1118        };
1119        match self.accelerator.free_buffer(resource) {
1120            Ok(()) => match self.state.commit_buffer_release(id) {
1121                Ok(()) => self.respond_empty(response, StatusCode::OK, request_id, true),
1122                Err(_) => self.discard_response(response, request_id),
1123            },
1124            Err(ReleaseFailure::Rejected { error, resource }) => {
1125                match self.state.restore_buffer_release(id, resource) {
1126                    Ok(()) => self.respond_backend_error(response, request_id, error, false),
1127                    Err(_) => self.discard_response(response, request_id),
1128                }
1129            }
1130            Err(ReleaseFailure::Indeterminate { .. }) => {
1131                if self.state.commit_buffer_release(id).is_ok() {
1132                    self.quarantined.buffers += 1;
1133                    self.quarantined_bytes.buffer_backing += u128::from(allocation_bytes);
1134                }
1135                self.discard_response(response, request_id)
1136            }
1137        }
1138    }
1139
1140    fn unload_program(
1141        &mut self,
1142        response: &mut dyn ByteSink,
1143        request_id: u64,
1144        id: ObjectId,
1145    ) -> Result<CommandOutcome, CommandProcessError> {
1146        let resident_bytes = match self.state.program_record(id) {
1147            Ok(record) => record.resident_bytes(),
1148            Err(error) => return self.respond_state_error(response, request_id, error),
1149        };
1150        let resource = match self.state.begin_program_release(id) {
1151            Ok(resource) => resource,
1152            Err(error) => return self.respond_state_error(response, request_id, error),
1153        };
1154        match self.accelerator.unload_program(resource) {
1155            Ok(()) => match self.state.commit_program_release(id) {
1156                Ok(()) => self.respond_empty(response, StatusCode::OK, request_id, true),
1157                Err(_) => self.discard_response(response, request_id),
1158            },
1159            Err(ReleaseFailure::Rejected { error, resource }) => {
1160                match self.state.restore_program_release(id, resource) {
1161                    Ok(()) => self.respond_backend_error(response, request_id, error, false),
1162                    Err(_) => self.discard_response(response, request_id),
1163                }
1164            }
1165            Err(ReleaseFailure::Indeterminate { .. }) => {
1166                if self.state.commit_program_release(id).is_ok() {
1167                    self.quarantined.programs += 1;
1168                    self.quarantined_bytes.program_resident += u128::from(resident_bytes);
1169                }
1170                self.discard_response(response, request_id)
1171            }
1172        }
1173    }
1174
1175    fn destroy_queue(
1176        &mut self,
1177        response: &mut dyn ByteSink,
1178        request_id: u64,
1179        id: ObjectId,
1180    ) -> Result<CommandOutcome, CommandProcessError> {
1181        let resource = match self.state.begin_queue_release(id) {
1182            Ok(resource) => resource,
1183            Err(error) => return self.respond_state_error(response, request_id, error),
1184        };
1185        match self.accelerator.destroy_queue(resource) {
1186            Ok(()) => match self.state.commit_queue_release(id) {
1187                Ok(()) => self.respond_empty(response, StatusCode::OK, request_id, true),
1188                Err(_) => self.discard_response(response, request_id),
1189            },
1190            Err(ReleaseFailure::Rejected { error, resource }) => {
1191                match self.state.restore_queue_release(id, resource) {
1192                    Ok(()) => self.respond_backend_error(response, request_id, error, false),
1193                    Err(_) => self.discard_response(response, request_id),
1194                }
1195            }
1196            Err(ReleaseFailure::Indeterminate { .. }) => {
1197                if self.state.commit_queue_release(id).is_ok() {
1198                    self.quarantined.queues += 1;
1199                }
1200                self.discard_response(response, request_id)
1201            }
1202        }
1203    }
1204
1205    fn respond_create_error(
1206        &mut self,
1207        response: &mut dyn ByteSink,
1208        request_id: u64,
1209        error: CreateError<BackendError>,
1210    ) -> Result<CommandOutcome, CommandProcessError> {
1211        match error {
1212            CreateError::State(error) => self.respond_state_error(response, request_id, error),
1213            CreateError::Provider(error) => {
1214                self.respond_backend_error(response, request_id, error, false)
1215            }
1216        }
1217    }
1218
1219    fn respond_state_error(
1220        &mut self,
1221        response: &mut dyn ByteSink,
1222        request_id: u64,
1223        error: DeviceStateError,
1224    ) -> Result<CommandOutcome, CommandProcessError> {
1225        self.respond_empty(
1226            response,
1227            status_from_device_state_error(error),
1228            request_id,
1229            false,
1230        )
1231    }
1232
1233    fn respond_backend_error(
1234        &mut self,
1235        response: &mut dyn ByteSink,
1236        request_id: u64,
1237        error: BackendError,
1238        mutated: bool,
1239    ) -> Result<CommandOutcome, CommandProcessError> {
1240        let status = self.backend_status(error);
1241        self.respond_empty(response, status, request_id, mutated)
1242    }
1243
1244    fn backend_status(&mut self, error: BackendError) -> StatusCode {
1245        if error == BackendError::DeviceLost {
1246            self.require_backend_discard();
1247        }
1248        status_from_backend_error(error)
1249    }
1250
1251    fn require_backend_discard(&mut self) {
1252        self.health = DeviceHealth::BackendDiscardRequired;
1253        self.last_reset = None;
1254    }
1255
1256    fn discard_response(
1257        &mut self,
1258        response: &mut dyn ByteSink,
1259        request_id: u64,
1260    ) -> Result<CommandOutcome, CommandProcessError> {
1261        self.require_backend_discard();
1262        self.respond_empty(response, StatusCode::DEVICE_LOST, request_id, true)
1263    }
1264
1265    fn discard_report(
1266        &self,
1267        released: ResourceCounts,
1268        released_bytes: RetainedBytes,
1269    ) -> ResetReport {
1270        ResetReport {
1271            disposition: ResetDisposition::BackendDiscardRequired,
1272            released,
1273            quarantined: self
1274                .quarantined
1275                .saturating_add(self.state.resource_counts()),
1276            released_bytes,
1277            quarantined_bytes: self
1278                .quarantined_bytes
1279                .saturating_add(self.state.retained_bytes()),
1280        }
1281    }
1282
1283    fn respond_object(
1284        &mut self,
1285        response: &mut dyn ByteSink,
1286        request_id: u64,
1287        id: ObjectId,
1288        mutated: bool,
1289    ) -> Result<CommandOutcome, CommandProcessError> {
1290        let payload = ObjectPayload {
1291            object_id: Le64::new(id.get()),
1292        };
1293        self.respond_bytes(
1294            response,
1295            StatusCode::OK,
1296            request_id,
1297            payload.as_bytes(),
1298            mutated,
1299        )
1300    }
1301
1302    fn respond_event_id(
1303        &mut self,
1304        response: &mut dyn ByteSink,
1305        status: StatusCode,
1306        request_id: u64,
1307        id: ObjectId,
1308        mutated: bool,
1309    ) -> Result<CommandOutcome, CommandProcessError> {
1310        let payload = SubmitResponse {
1311            event_id: Le64::new(id.get()),
1312        };
1313        self.respond_bytes(response, status, request_id, payload.as_bytes(), mutated)
1314    }
1315
1316    fn respond_bytes(
1317        &mut self,
1318        response: &mut dyn ByteSink,
1319        status: StatusCode,
1320        request_id: u64,
1321        payload: &[u8],
1322        mutated: bool,
1323    ) -> Result<CommandOutcome, CommandProcessError> {
1324        let result = (|| {
1325            let mut writer =
1326                ResponseWriter::new(response, self.decoder.limits().max_response_bytes());
1327            let payload_bytes =
1328                u64::try_from(payload.len()).map_err(|_| ResponseWriteError::FrameTooLarge)?;
1329            let mut destination = writer.payload(payload_bytes)?;
1330            destination
1331                .write_at(0, payload)
1332                .map_err(|_| ResponseWriteError::SinkAccess)?;
1333            destination.commit(status, request_id)
1334        })();
1335        self.complete_response(result, status, request_id, mutated)
1336    }
1337
1338    fn respond_empty(
1339        &mut self,
1340        response: &mut dyn ByteSink,
1341        status: StatusCode,
1342        request_id: u64,
1343        mutated: bool,
1344    ) -> Result<CommandOutcome, CommandProcessError> {
1345        let result = ResponseWriter::new(response, self.decoder.limits().max_response_bytes())
1346            .write_empty(status, request_id);
1347        self.complete_response(result, status, request_id, mutated)
1348    }
1349
1350    fn complete_response(
1351        &mut self,
1352        result: Result<u32, ResponseWriteError>,
1353        status: StatusCode,
1354        request_id: u64,
1355        mutated: bool,
1356    ) -> Result<CommandOutcome, CommandProcessError> {
1357        match result {
1358            Ok(used) => Ok(CommandOutcome::Response {
1359                request_id,
1360                status,
1361                used,
1362            }),
1363            Err(error) => {
1364                if mutated && self.health == DeviceHealth::Running {
1365                    self.health = DeviceHealth::NeedsReset;
1366                    self.last_reset = None;
1367                }
1368                Err(CommandProcessError::ResponseWrite(error))
1369            }
1370        }
1371    }
1372}
1373
1374#[derive(Clone, Copy, Debug)]
1375struct ResetProgress {
1376    backend_reusable: bool,
1377    backend_callable: bool,
1378}
1379
1380impl Default for ResetProgress {
1381    fn default() -> Self {
1382        Self {
1383            backend_reusable: true,
1384            backend_callable: true,
1385        }
1386    }
1387}
1388
1389fn validate_transfer<B>(
1390    record: &BufferRecord<B>,
1391    range: BufferRange,
1392    required_usage: BufferUsage,
1393) -> Result<(), StatusCode> {
1394    if record.in_flight() != 0 {
1395        return Err(StatusCode::BUSY);
1396    }
1397    let desc = record.info().desc();
1398    if range.end() > desc.bytes() {
1399        return Err(StatusCode::OUT_OF_BOUNDS);
1400    }
1401    if !desc.usage.contains(required_usage) {
1402        return Err(StatusCode::PERMISSION_DENIED);
1403    }
1404    Ok(())
1405}
1406
1407fn wire_device_info(info: DeviceInfo) -> WireDeviceInfo {
1408    WireDeviceInfo {
1409        uuid: info.identity.uuid,
1410        class: Le16::new(info.identity.class.get()),
1411        reserved: Le16::new(0),
1412        vendor_id: Le32::new(info.identity.vendor_id),
1413        device_id: Le32::new(info.identity.device_id),
1414        capabilities: Le64::new(info.capabilities.bits()),
1415        max_contexts: Le32::new(info.limits.max_contexts),
1416        max_buffers_per_context: Le32::new(info.limits.max_buffers_per_context),
1417        max_programs_per_context: Le32::new(info.limits.max_programs_per_context),
1418        max_queues_per_context: Le32::new(info.limits.max_queues_per_context),
1419        max_events_per_context: Le32::new(info.limits.max_events_per_context),
1420        max_bindings_per_submission: Le32::new(info.limits.max_bindings_per_submission),
1421        max_buffer_bytes: Le64::new(info.limits.max_buffer_bytes),
1422        max_artifact_bytes: Le64::new(info.limits.max_artifact_bytes),
1423    }
1424}
1425
1426fn wire_event_state(state: EventState) -> WireEventState {
1427    let (state, error) = match state {
1428        EventState::Pending => (KnownEventState::Pending, StatusCode::OK),
1429        EventState::Complete => (KnownEventState::Complete, StatusCode::OK),
1430        EventState::Failed(error) => (KnownEventState::Failed, status_from_backend_error(error)),
1431        EventState::Cancelled => (KnownEventState::Cancelled, StatusCode::OK),
1432    };
1433    WireEventState {
1434        state: Le16::new(state as u16),
1435        error: Le16::new(error.0),
1436        reserved: Le32::new(0),
1437    }
1438}