{"record":{"id":"a3ab306b291eb664","repo":"firecracker-microvm/firecracker","slug":"vsock-could-not-trigger-device-interrupt","errorCode":null,"errorMessage":"vsock: Could not trigger device interrupt","messagePattern":"vsock: Could not trigger device interrupt","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/vmm/src/devices/virtio/vsock/event_handler.rs","lineNumber":218,"sourceCode":"        let evset = event.event_set();\n\n        if self.is_activated() {\n            let used_queues = match source {\n                Self::PROCESS_ACTIVATE => {\n                    self.handle_activate_event(ops);\n                    Vec::new()\n                }\n                Self::PROCESS_RXQ => self.handle_rxq_event(evset),\n                Self::PROCESS_TXQ => self.handle_txq_event(evset),\n                Self::PROCESS_EVQ => self.handle_evq_event(evset),\n                Self::PROCESS_NOTIFY_BACKEND => self.notify_backend(evset).unwrap(),\n                _ => {\n                    warn!(\"Unexpected vsock event received: {:?}\", source);\n                    Vec::new()\n                }\n            };\n            self.signal_used_queues(&used_queues)\n                .expect(\"vsock: Could not trigger device interrupt\");\n        } else {\n            warn!(\n                \"Vsock: The device is not yet activated. Spurious event received: {:?}\",\n                source\n            );\n            if source == Self::PROCESS_ACTIVATE {\n                let _ = self.activate_evt.read();\n            } else {\n                self.drain_queue_events();\n            }\n        }\n    }\n\n    fn init(&mut self, ops: &mut EventOps) {\n        // This function can be called during different points in the device lifetime:\n        //  - shortly after device creation,\n        //  - on device activation (is-activated already true at this point),\n        //  - on device restore from snapshot.","sourceCodeStart":200,"sourceCodeEnd":236,"githubUrl":"https://github.com/firecracker-microvm/firecracker/blob/9384f395f5d47f05b68a657525e209f8aa9c8264/src/vmm/src/devices/virtio/vsock/event_handler.rs#L200-L236","documentation":"Panic at event_handler.rs:218: after a vsock event was processed in the activated branch, `self.signal_used_queues(&used_queues).expect(\"vsock: Could not trigger device interrupt\")` fires because signaling returned `Err(DeviceError::FailedSignalingIrq)` — the underlying interrupt EventFd write (ioctl WRITE on the irq fd) failed. Unlike errors 60-62, the device WAS activated; the interrupt delivery itself failed.","triggerScenarios":"The device is activated, an RXQ/TXQ/EVQ or backend event is handled, and then `interrupt.trigger_queues(used_queues)` fails — typically EBADF/EFAULT on a closed or invalid irq EventFd (fd torn down during device removal, snapshot restore with stale fd, or fd exhaustion).","commonSituations":"Snapshot/restore where irq fds were recreated but the device still holds the old ones; racing device teardown (VM stop) with an in-flight backend event; exceeding the process fd limit so the eventfd write fails.","solutions":["Check for races between VM shutdown/teardown and vsock event processing — pause or detach the event handler before closing irq fds","On restore, ensure the device's interrupt objects are rebuilt with the new irq fds before event handling resumes","Replace the `.expect` with proper error propagation so a failed irq write logs and drops the notification instead of aborting the VMM","Verify process fd limits (ulimit -n) if eventfd writes fail under load"],"exampleFix":"// before\nself.signal_used_queues(&used_queues)\n    .expect(\"vsock: Could not trigger device interrupt\");\n\n// after\nif let Err(e) = self.signal_used_queues(&used_queues) {\n    error!(\"vsock: failed to signal used queues {:?}: {:?}\", used_queues, e);\n}","handlingStrategy":"try-catch","validationCode":"// Before dispatching backend events, confirm the irq fd is still alive\n// (guard against teardown races by pausing the device event loop first)\nif shutting_down { return; } // do not process events during teardown","typeGuard":null,"tryCatchPattern":"// The panic escapes the event handler thread; catch at the thread boundary to capture a core/abort cleanly\nlet result = std::panic::catch_unwind(AssertUnwindSafe(|| handler.handle_event(src, evset)));\nif result.is_err() {\n    error!(\"vsock irq signaling failed; stopping device event loop\");\n    metrics.vsock_irq_failures.add(1);\n}","preventionTips":["Pause or deregister the vsock event handler before closing irq fds during VM stop/snapshot","Recreate interrupt objects atomically on restore before resuming event dispatch","Monitor eventfd write failures and treat EBADF as a teardown signal rather than processing further events"],"tags":["rust","panic","virtio","vsock","interrupt","eventfd","firecracker"],"backgroundTag":"virtio-irq-signaling-failed","analyzedSha":"9384f395f5d47f05b68a657525e209f8aa9c8264","analyzedAt":"2026-08-19T05:27:02.517Z","contentChangedAt":"2026-08-19T05:27:02.517Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}