crosvm/crosvm/sys/linux/
pci_hotplug_manager.rs

1// Copyright 2023 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! A high-level manager for hotplug PCI devices.
6
7// TODO(b/243767476): Support aarch64.
8use std::cmp::Ordering;
9use std::collections::BTreeMap;
10use std::collections::HashMap;
11use std::collections::VecDeque;
12use std::sync::mpsc;
13use std::sync::Arc;
14
15use anyhow::anyhow;
16use anyhow::bail;
17use anyhow::Context;
18use anyhow::Error;
19use arch::RunnableLinuxVm;
20use base::AsRawDescriptor;
21use base::Event;
22use base::EventToken;
23use base::RawDescriptor;
24use base::WaitContext;
25use base::WorkerThread;
26use devices::BusDevice;
27use devices::HotPlugBus;
28use devices::HotPlugKey;
29use devices::IrqEventSource;
30use devices::IrqLevelEvent;
31use devices::PciAddress;
32use devices::PciInterruptPin;
33use devices::PciRootCommand;
34use devices::ResourceCarrier;
35use log::error;
36use resources::SystemAllocator;
37#[cfg(feature = "swap")]
38use swap::SwapDeviceHelper;
39use sync::Mutex;
40use vm_memory::GuestMemory;
41
42use crate::crosvm::sys::linux::JailWarden;
43use crate::crosvm::sys::linux::JailWardenImpl;
44use crate::crosvm::sys::linux::PermissiveJailWarden;
45use crate::Config;
46
47pub type Result<T> = std::result::Result<T, Error>;
48
49/// PciHotPlugManager manages hotplug ports, and handles PCI device hot plug and hot removal.
50pub struct PciHotPlugManager {
51    /// map of ports managed
52    port_stubs: BTreeMap<PciAddress, PortManagerStub>,
53    /// map of downstream bus to upstream PCI address
54    bus_address_map: BTreeMap<u8, PciAddress>,
55    /// JailWarden for jailing hotplug devices
56    jail_warden: Box<dyn JailWarden>,
57    /// Client on Manager side of PciHotPlugWorker
58    worker_client: Option<WorkerClient>,
59}
60
61/// WorkerClient is a wrapper of the worker methods.
62struct WorkerClient {
63    /// event to signal control command is sent
64    control_evt: Event,
65    /// control channel to worker
66    command_sender: mpsc::Sender<WorkerCommand>,
67    /// response channel from worker
68    response_receiver: mpsc::Receiver<WorkerResponse>,
69    _worker_thread: WorkerThread<()>,
70}
71
72impl WorkerClient {
73    /// Constructs PciHotPlugWorker with its client.
74    fn new(rootbus_controller: mpsc::Sender<PciRootCommand>) -> Result<Self> {
75        let (command_sender, command_receiver) = mpsc::channel();
76        let (response_sender, response_receiver) = mpsc::channel();
77        let control_evt = Event::new()?;
78        let control_evt_cpy = control_evt.try_clone()?;
79        let worker_thread = WorkerThread::start("pcihp_mgr_workr", move |kill_evt| {
80            if let Err(e) = PciHotPlugWorker::new(
81                rootbus_controller,
82                command_receiver,
83                response_sender,
84                control_evt_cpy,
85                &kill_evt,
86            )
87            .and_then(move |mut worker| worker.run(kill_evt))
88            {
89                error!("PciHotPlugManager worker failed: {e:#}");
90            }
91        });
92        Ok(WorkerClient {
93            control_evt,
94            command_sender,
95            response_receiver,
96            _worker_thread: worker_thread,
97        })
98    }
99
100    /// Sends worker command, and wait for its response.
101    fn send_worker_command(&self, command: WorkerCommand) -> Result<WorkerResponse> {
102        self.command_sender.send(command)?;
103        self.control_evt.signal()?;
104        Ok(self.response_receiver.recv()?)
105    }
106}
107
108/// PortManagerStub is the manager-side copy of a port.
109struct PortManagerStub {
110    /// index of downstream bus
111    downstream_bus: u8,
112    /// Map of hotplugged devices, and system resources that can be released when device is
113    /// removed.
114    devices: HashMap<PciAddress, RecoverableResource>,
115}
116
117/// System resources that can be released when a hotplugged device is removed.
118struct RecoverableResource {
119    irq_num: u32,
120    irq_evt: IrqLevelEvent,
121}
122
123/// Control commands to worker.
124enum WorkerCommand {
125    /// Add port to the worker.
126    AddPort(PciAddress, PortWorkerStub),
127    /// Get the state of the port.
128    GetPortState(PciAddress),
129    /// Get an empty port for hotplug. Returns the least port sorted by PortKey.
130    GetEmptyPort,
131    /// Signals hot plug on port. Changes an empty port to occupied.
132    SignalHotPlug(SignalHotPlugCommand),
133    /// Signals hot unplug on port. Changes an occupied port to empty.
134    SignalHotUnplug(PciAddress),
135}
136
137#[derive(Clone)]
138struct GuestDeviceStub {
139    pci_addr: PciAddress,
140    key: HotPlugKey,
141    device: Arc<Mutex<dyn BusDevice>>,
142}
143
144#[derive(Clone)]
145struct SignalHotPlugCommand {
146    /// the upstream address of hotplug port
147    upstream_address: PciAddress,
148    /// the array of guest devices on the port
149    guest_devices: Vec<GuestDeviceStub>,
150}
151
152impl SignalHotPlugCommand {
153    fn new(upstream_address: PciAddress, guest_devices: Vec<GuestDeviceStub>) -> Result<Self> {
154        if guest_devices.is_empty() {
155            bail!("No guest devices");
156        }
157        Ok(Self {
158            upstream_address,
159            guest_devices,
160        })
161    }
162}
163
164/// PortWorkerStub is the worker-side copy of a port.
165#[derive(Clone)]
166struct PortWorkerStub {
167    /// The downstream base address of the port. Needed to send plug and unplug signal.
168    base_address: PciAddress,
169    /// Currently attached devices that should be removed.
170    attached_devices: Vec<PciAddress>,
171    /// Devices to be added each time send_hot_plug_signal is called.
172    devices_to_add: VecDeque<Vec<GuestDeviceStub>>,
173    /// hotplug port
174    port: Arc<Mutex<dyn HotPlugBus>>,
175}
176
177impl PortWorkerStub {
178    fn new(port: Arc<Mutex<dyn HotPlugBus>>, downstream_bus: u8) -> Result<Self> {
179        let base_address = PciAddress::new(0, downstream_bus.into(), 0, 0)?;
180        Ok(Self {
181            base_address,
182            devices_to_add: VecDeque::new(),
183            attached_devices: Vec::new(),
184            port,
185        })
186    }
187
188    fn add_hotplug_devices(&mut self, devices: Vec<GuestDeviceStub>) -> Result<()> {
189        if devices.is_empty() {
190            bail!("No guest devices");
191        }
192        self.devices_to_add.push_back(devices);
193        Ok(())
194    }
195
196    fn cancel_queued_add(&mut self) -> Result<()> {
197        self.devices_to_add
198            .pop_back()
199            .context("No guest device add queued")?;
200        Ok(())
201    }
202
203    fn send_hot_plug_signal(
204        &mut self,
205        rootbus_controller: &mpsc::Sender<PciRootCommand>,
206    ) -> Result<Event> {
207        let mut port_lock = self.port.lock();
208        let devices = self
209            .devices_to_add
210            .pop_front()
211            .context("Missing devices to add")?;
212        for device in devices {
213            rootbus_controller.send(PciRootCommand::Add(device.pci_addr, device.device))?;
214            self.attached_devices.push(device.pci_addr);
215            port_lock.add_hotplug_device(device.key, device.pci_addr);
216        }
217        port_lock
218            .hot_plug(self.base_address)?
219            .context("hotplug bus does not support command complete notification")
220    }
221
222    fn send_hot_unplug_signal(
223        &mut self,
224        rootbus_controller: &mpsc::Sender<PciRootCommand>,
225    ) -> Result<Event> {
226        for pci_addr in self.attached_devices.drain(..) {
227            rootbus_controller.send(PciRootCommand::Remove(pci_addr))?;
228        }
229        self.port
230            .lock()
231            .hot_unplug(self.base_address)?
232            .context("hotplug bus does not support command complete notification")
233    }
234}
235
236/// Control response from worker.
237#[derive(Debug)]
238enum WorkerResponse {
239    /// AddPort success.
240    AddPortOk,
241    /// GetEmptyPort success, use port at PciAddress.
242    GetEmptyPortOk(PciAddress),
243    /// GetPortState success. The "steps behind" field shall be considered expired, and the guest
244    /// is "less than or equal to" n steps behind.
245    GetPortStateOk(PortState),
246    /// SignalHotPlug or SignalHotUnplug success.
247    SignalOk,
248    /// Command fail because it is not valid.
249    InvalidCommand(Error),
250}
251
252impl PartialEq for WorkerResponse {
253    fn eq(&self, other: &Self) -> bool {
254        match (self, other) {
255            (Self::GetEmptyPortOk(l0), Self::GetEmptyPortOk(r0)) => l0 == r0,
256            (Self::GetPortStateOk(l0), Self::GetPortStateOk(r0)) => l0 == r0,
257            (Self::InvalidCommand(_), Self::InvalidCommand(_)) => true,
258            _ => core::mem::discriminant(self) == core::mem::discriminant(other),
259        }
260    }
261}
262
263#[derive(Debug, EventToken)]
264enum Token {
265    Kill,
266    ManagerCommand,
267    PortReady(RawDescriptor),
268    PlugComplete(RawDescriptor),
269    UnplugComplete(RawDescriptor),
270}
271
272/// PciHotPlugWorker is a worker that handles the asynchrony of slot states between crosvm and the
273/// guest OS. It is responsible for scheduling the PCIe slot control signals and handle its result.
274struct PciHotPlugWorker {
275    event_map: BTreeMap<RawDescriptor, (Event, PciAddress)>,
276    port_state_map: BTreeMap<PciAddress, PortState>,
277    port_map: BTreeMap<PortKey, PortWorkerStub>,
278    manager_evt: Event,
279    wait_ctx: WaitContext<Token>,
280    command_receiver: mpsc::Receiver<WorkerCommand>,
281    response_sender: mpsc::Sender<WorkerResponse>,
282    rootbus_controller: mpsc::Sender<PciRootCommand>,
283}
284
285impl PciHotPlugWorker {
286    fn new(
287        rootbus_controller: mpsc::Sender<PciRootCommand>,
288        command_receiver: mpsc::Receiver<WorkerCommand>,
289        response_sender: mpsc::Sender<WorkerResponse>,
290        manager_evt: Event,
291        kill_evt: &Event,
292    ) -> Result<Self> {
293        let wait_ctx: WaitContext<Token> = WaitContext::build_with(&[
294            (&manager_evt, Token::ManagerCommand),
295            (kill_evt, Token::Kill),
296        ])?;
297        Ok(Self {
298            event_map: BTreeMap::new(),
299            port_state_map: BTreeMap::new(),
300            port_map: BTreeMap::new(),
301            manager_evt,
302            wait_ctx,
303            command_receiver,
304            response_sender,
305            rootbus_controller,
306        })
307    }
308
309    /// Starts the worker. Runs until received kill request, or an error that the worker is in an
310    /// invalid state.
311    fn run(&mut self, kill_evt: Event) -> Result<()> {
312        'wait: loop {
313            let events = self.wait_ctx.wait()?;
314            for triggered_event in events.iter().filter(|e| e.is_readable) {
315                match triggered_event.token {
316                    Token::ManagerCommand => {
317                        self.manager_evt.wait()?;
318                        self.handle_manager_command()?;
319                    }
320                    Token::PortReady(descriptor) => {
321                        let (event, pci_address) = self
322                            .event_map
323                            .remove(&descriptor)
324                            .context("Cannot find event")?;
325                        event.wait()?;
326                        self.wait_ctx.delete(&event)?;
327                        self.handle_port_ready(pci_address)?;
328                    }
329                    Token::PlugComplete(descriptor) => {
330                        let (event, pci_address) = self
331                            .event_map
332                            .remove(&descriptor)
333                            .context("Cannot find event")?;
334                        event.wait()?;
335                        self.wait_ctx.delete(&event)?;
336                        self.handle_plug_complete(pci_address)?;
337                    }
338                    Token::UnplugComplete(descriptor) => {
339                        let (event, pci_address) = self
340                            .event_map
341                            .remove(&descriptor)
342                            .context("Cannot find event")?;
343                        self.wait_ctx.delete(&event)?;
344                        self.handle_unplug_complete(pci_address)?;
345                    }
346                    Token::Kill => {
347                        let _ = kill_evt.wait();
348                        break 'wait;
349                    }
350                }
351            }
352        }
353        Ok(())
354    }
355
356    fn handle_manager_command(&mut self) -> Result<()> {
357        let response = match self.command_receiver.recv()? {
358            WorkerCommand::AddPort(pci_address, port) => self.handle_add_port(pci_address, port),
359            WorkerCommand::GetPortState(pci_address) => self.handle_get_port_state(pci_address),
360            WorkerCommand::GetEmptyPort => self.handle_get_empty_port(),
361            WorkerCommand::SignalHotPlug(hotplug_command) => {
362                self.handle_plug_request(hotplug_command)
363            }
364            WorkerCommand::SignalHotUnplug(pci_address) => self.handle_unplug_request(pci_address),
365        }?;
366        Ok(self.response_sender.send(response)?)
367    }
368
369    /// Handles add port: Initiate port in EmptyNotReady state.
370    fn handle_add_port(
371        &mut self,
372        pci_address: PciAddress,
373        port: PortWorkerStub,
374    ) -> Result<WorkerResponse> {
375        if self.port_state_map.contains_key(&pci_address) {
376            return Ok(WorkerResponse::InvalidCommand(anyhow!(
377                "Conflicting upstream PCI address"
378            )));
379        }
380        let port_state = PortState::EmptyNotReady;
381        let port_ready_event = port.port.lock().get_ready_notification()?;
382        self.wait_ctx.add(
383            &port_ready_event,
384            Token::PortReady(port_ready_event.as_raw_descriptor()),
385        )?;
386        self.event_map.insert(
387            port_ready_event.as_raw_descriptor(),
388            (port_ready_event, pci_address),
389        );
390        self.port_state_map.insert(pci_address, port_state);
391        self.port_map.insert(
392            PortKey {
393                port_state,
394                pci_address,
395            },
396            port,
397        );
398        Ok(WorkerResponse::AddPortOk)
399    }
400
401    /// Handles get port state: returns the PortState.
402    fn handle_get_port_state(&self, pci_address: PciAddress) -> Result<WorkerResponse> {
403        match self.get_port_state(pci_address) {
404            Ok(ps) => Ok(WorkerResponse::GetPortStateOk(ps)),
405            Err(e) => Ok(WorkerResponse::InvalidCommand(e)),
406        }
407    }
408
409    /// Handle getting empty port: Find the most empty port, or return error if all are occupied.
410    fn handle_get_empty_port(&self) -> Result<WorkerResponse> {
411        let most_empty_port = match self.port_map.first_key_value() {
412            Some(p) => p.0,
413            None => return Ok(WorkerResponse::InvalidCommand(anyhow!("No ports added"))),
414        };
415        match most_empty_port.port_state {
416            PortState::Empty(_) | PortState::EmptyNotReady => {
417                Ok(WorkerResponse::GetEmptyPortOk(most_empty_port.pci_address))
418            }
419            PortState::Occupied(_) | PortState::OccupiedNotReady => {
420                Ok(WorkerResponse::InvalidCommand(anyhow!("No empty port")))
421            }
422        }
423    }
424
425    /// Handles plug request: Moves PortState from EmptyNotReady to OccupiedNotReady, Empty(n) to
426    /// Occupied(n+1), and schedules the next plug event if n == 0.
427    fn handle_plug_request(
428        &mut self,
429        hotplug_command: SignalHotPlugCommand,
430    ) -> Result<WorkerResponse> {
431        let pci_address = hotplug_command.upstream_address;
432        let next_state = match self.get_port_state(pci_address) {
433            Ok(PortState::Empty(n)) => {
434                self.get_port_mut(pci_address)?
435                    .add_hotplug_devices(hotplug_command.guest_devices)?;
436                if n == 0 {
437                    self.schedule_plug_event(pci_address)?;
438                }
439                PortState::Occupied(n + 1)
440            }
441            Ok(PortState::EmptyNotReady) => {
442                self.get_port_mut(pci_address)?
443                    .add_hotplug_devices(hotplug_command.guest_devices)?;
444                PortState::OccupiedNotReady
445            }
446            Ok(PortState::Occupied(_)) | Ok(PortState::OccupiedNotReady) => {
447                return Ok(WorkerResponse::InvalidCommand(anyhow!(
448                    "Attempt to plug into an occupied port"
449                )))
450            }
451            Err(e) => return Ok(WorkerResponse::InvalidCommand(e)),
452        };
453        self.set_port_state(pci_address, next_state)?;
454        Ok(WorkerResponse::SignalOk)
455    }
456
457    /// Handles unplug request: Moves PortState from OccupiedNotReady to EmptyNotReady, Occupied(n)
458    /// to Empty(n % 2 + 1), and schedules the next unplug event if n == 0.
459    ///
460    /// n % 2 + 1: When unplug request is made, it either schedule the unplug event
461    /// (n == 0 => 1 or n == 1 => 2), or cancels the corresponding plug event that has not started
462    /// (n == 2 => 1 or n == 3 => 2). Staring at the mapping, it maps n to either 1 or 2 of opposite
463    /// oddity. n % 2 + 1 is a good shorthand instead of the individual mappings.
464    fn handle_unplug_request(&mut self, pci_address: PciAddress) -> Result<WorkerResponse> {
465        let next_state = match self.get_port_state(pci_address) {
466            Ok(PortState::Occupied(n)) => {
467                if n >= 2 {
468                    self.get_port_mut(pci_address)?.cancel_queued_add()?;
469                }
470                if n == 0 {
471                    self.schedule_unplug_event(pci_address)?;
472                }
473                PortState::Empty(n % 2 + 1)
474            }
475            Ok(PortState::OccupiedNotReady) => PortState::EmptyNotReady,
476            Ok(PortState::Empty(_)) | Ok(PortState::EmptyNotReady) => {
477                return Ok(WorkerResponse::InvalidCommand(anyhow!(
478                    "Attempt to unplug from an empty port"
479                )))
480            }
481            Err(e) => return Ok(WorkerResponse::InvalidCommand(e)),
482        };
483        self.set_port_state(pci_address, next_state)?;
484        Ok(WorkerResponse::SignalOk)
485    }
486
487    /// Handles port ready: Moves PortState from EmptyNotReady to Empty(0), OccupiedNotReady to
488    /// Occupied(1), and schedules the next event if port is occupied
489    fn handle_port_ready(&mut self, pci_address: PciAddress) -> Result<()> {
490        let next_state = match self.get_port_state(pci_address)? {
491            PortState::EmptyNotReady => PortState::Empty(0),
492            PortState::OccupiedNotReady => {
493                self.schedule_plug_event(pci_address)?;
494                PortState::Occupied(1)
495            }
496            PortState::Empty(_) | PortState::Occupied(_) => {
497                bail!("Received port ready on an already enabled port");
498            }
499        };
500        self.set_port_state(pci_address, next_state)
501    }
502
503    /// Handles plug complete: Moves PortState from Any(n) to Any(n-1), and schedules the next
504    /// unplug event unless n == 1. (Any is either Empty or Occupied.)
505    fn handle_plug_complete(&mut self, pci_address: PciAddress) -> Result<()> {
506        let (n, next_state) = match self.get_port_state(pci_address)? {
507            // Note: n - 1 >= 0 as otherwise there would be no pending events.
508            PortState::Empty(n) => (n, PortState::Empty(n - 1)),
509            PortState::Occupied(n) => (n, PortState::Occupied(n - 1)),
510            PortState::EmptyNotReady | PortState::OccupiedNotReady => {
511                bail!("Received plug completed on a not enabled port");
512            }
513        };
514        if n > 1 {
515            self.schedule_unplug_event(pci_address)?;
516        }
517        self.set_port_state(pci_address, next_state)
518    }
519
520    /// Handles unplug complete: Moves PortState from Any(n) to Any(n-1), and schedules the next
521    /// plug event unless n == 1. (Any is either Empty or Occupied.)
522    fn handle_unplug_complete(&mut self, pci_address: PciAddress) -> Result<()> {
523        let (n, next_state) = match self.get_port_state(pci_address)? {
524            // Note: n - 1 >= 0 as otherwise there would be no pending events.
525            PortState::Empty(n) => (n, PortState::Empty(n - 1)),
526            PortState::Occupied(n) => (n, PortState::Occupied(n - 1)),
527            PortState::EmptyNotReady | PortState::OccupiedNotReady => {
528                bail!("Received unplug completed on a not enabled port");
529            }
530        };
531        if n > 1 {
532            self.schedule_plug_event(pci_address)?;
533        }
534        self.set_port_state(pci_address, next_state)
535    }
536
537    fn get_port_state(&self, pci_address: PciAddress) -> Result<PortState> {
538        Ok(*self
539            .port_state_map
540            .get(&pci_address)
541            .with_context(|| format!("Cannot find port state on {pci_address}"))?)
542    }
543
544    fn set_port_state(&mut self, pci_address: PciAddress, port_state: PortState) -> Result<()> {
545        let old_port_state = self.get_port_state(pci_address)?;
546        let port = self
547            .port_map
548            .remove(&PortKey {
549                port_state: old_port_state,
550                pci_address,
551            })
552            .context("Cannot find port")?;
553        self.port_map.insert(
554            PortKey {
555                port_state,
556                pci_address,
557            },
558            port,
559        );
560        self.port_state_map.insert(pci_address, port_state);
561        Ok(())
562    }
563
564    fn schedule_plug_event(&mut self, pci_address: PciAddress) -> Result<()> {
565        let rootbus_controller = self.rootbus_controller.clone();
566        let plug_event = self
567            .get_port_mut(pci_address)?
568            .send_hot_plug_signal(&rootbus_controller)?;
569        self.wait_ctx.add(
570            &plug_event,
571            Token::PlugComplete(plug_event.as_raw_descriptor()),
572        )?;
573        self.event_map
574            .insert(plug_event.as_raw_descriptor(), (plug_event, pci_address));
575        Ok(())
576    }
577
578    fn schedule_unplug_event(&mut self, pci_address: PciAddress) -> Result<()> {
579        let rootbus_controller = self.rootbus_controller.clone();
580        let unplug_event = self
581            .get_port_mut(pci_address)?
582            .send_hot_unplug_signal(&rootbus_controller)?;
583        self.wait_ctx.add(
584            &unplug_event,
585            Token::UnplugComplete(unplug_event.as_raw_descriptor()),
586        )?;
587        self.event_map.insert(
588            unplug_event.as_raw_descriptor(),
589            (unplug_event, pci_address),
590        );
591        Ok(())
592    }
593
594    fn get_port_mut(&mut self, pci_address: PciAddress) -> Result<&mut PortWorkerStub> {
595        let port_state = self.get_port_state(pci_address)?;
596        self.port_map
597            .get_mut(&PortKey {
598                port_state,
599                pci_address,
600            })
601            .context("PciHotPlugWorker is in invalid state")
602    }
603}
604
605/// PortState indicates the state of the port.
606///
607/// The initial PortState is EmptyNotReady (EmpNR). 9 PortStates are possible, and transition
608/// between the states are only possible by the following 3 groups of functions:
609/// handle_port_ready(R): guest notification of port ready to accept hot plug events.
610/// handle_plug_request(P) and handle_unplug_request(U): host initated requests.
611/// handle_plug_complete(PC) and handle_unplug_complete(UC): guest notification of event completion.
612/// When a port is not ready, PC and UC are not expected as no events are scheduled.
613/// The state transition is as follows:
614///    Emp0<-UC--Emp1<-PC--Emp2            |
615///  ^     \    ^    \^   ^    \^          |
616/// /       P  /      P\ /      P\         |
617/// |        \/        \\        \\        |
618/// |        /\        /\\        \\       |
619/// R       U  \      U  \U        \U      |
620/// |      /    v    /    v\        v\     |
621/// |  Occ0<-PC--Occ1<-UC--Occ2<-PC--Occ3  |
622/// |              ^                       |
623/// \              R                       |
624///   EmpNR<-P,U->OccNR                    |
625
626#[derive(Clone, Copy, Debug, PartialEq, Eq)]
627enum PortState {
628    /// Port is empty on crosvm. The state on the guest OS is n steps behind.
629    Empty(u8),
630    /// Port is empty on crosvm. The port is not enabled on the guest OS yet.
631    EmptyNotReady,
632    /// Port is occupied on crosvm. The state on the guest OS is n steps behind.
633    Occupied(u8),
634    /// Port is occupied on crosvm. The port is not enabled on the guest OS yet.
635    OccupiedNotReady,
636}
637
638impl PortState {
639    fn variant_order_index(&self) -> u8 {
640        match self {
641            PortState::Empty(_) => 0,
642            PortState::EmptyNotReady => 1,
643            PortState::Occupied(_) => 2,
644            PortState::OccupiedNotReady => 3,
645        }
646    }
647}
648
649/// Ordering on PortState defined by "most empty".
650impl Ord for PortState {
651    fn cmp(&self, other: &Self) -> Ordering {
652        // First compare by the variant: Empty < EmptyNotReady < Occupied < OccupiedNotReady.
653        match self.variant_order_index().cmp(&other.variant_order_index()) {
654            Ordering::Less => {
655                return Ordering::Less;
656            }
657            Ordering::Equal => {}
658            Ordering::Greater => return Ordering::Greater,
659        }
660        // For the diagonals, prioritize ones with less step behind.
661        match (self, other) {
662            (PortState::Empty(lhs), PortState::Empty(rhs)) => lhs.cmp(rhs),
663            (PortState::Occupied(lhs), PortState::Occupied(rhs)) => lhs.cmp(rhs),
664            _ => Ordering::Equal,
665        }
666    }
667}
668
669impl PartialOrd for PortState {
670    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
671        Some(self.cmp(other))
672    }
673}
674
675/// PortKey is a unique identifier of ports with an ordering defined on it.
676///
677/// Ports are ordered by whose downstream device would be discovered first by the guest OS.
678/// Empty ports without pending events are ordered before those with pending events. When multiple
679/// empty ports without pending events are available, they are ordered by PCI enumeration.
680#[derive(PartialEq, Eq, PartialOrd, Ord)]
681struct PortKey {
682    port_state: PortState,
683    pci_address: PciAddress,
684}
685
686impl PciHotPlugManager {
687    /// Constructs PciHotPlugManager.
688    ///
689    /// Constructor uses forking, therefore has to be called early, before crosvm enters a
690    /// multi-threaded context.
691    pub fn new(
692        guest_memory: GuestMemory,
693        config: &Config,
694        #[cfg(feature = "swap")] swap_device_helper: Option<SwapDeviceHelper>,
695    ) -> Result<Self> {
696        let jail_warden: Box<dyn JailWarden> = match config.jail_config {
697            Some(_) => Box::new(
698                JailWardenImpl::new(
699                    guest_memory,
700                    config,
701                    #[cfg(feature = "swap")]
702                    swap_device_helper,
703                )
704                .context("jail warden construction")?,
705            ),
706            None => Box::new(
707                PermissiveJailWarden::new(
708                    guest_memory,
709                    config,
710                    #[cfg(feature = "swap")]
711                    swap_device_helper,
712                )
713                .context("jail warden construction")?,
714            ),
715        };
716        Ok(Self {
717            jail_warden,
718            port_stubs: BTreeMap::new(),
719            bus_address_map: BTreeMap::new(),
720            worker_client: None,
721        })
722    }
723
724    /// Starts PciHotPlugManager. Required before any other commands.
725    ///
726    /// PciHotPlugManager::new must be called in a single-threaded context as it forks.
727    /// However, rootbus_controller is only available after VM boots when crosvm is multi-threaded.
728    ///
729    /// TODO(293801301): Remove unused after aarch64 support
730    #[allow(unused)]
731    pub fn set_rootbus_controller(
732        &mut self,
733        rootbus_controller: mpsc::Sender<PciRootCommand>,
734    ) -> Result<()> {
735        // Spins the PciHotPlugWorker.
736        self.worker_client = Some(WorkerClient::new(rootbus_controller)?);
737        Ok(())
738    }
739
740    /// Adds a hotplug capable port to manage.
741    ///
742    /// PciHotPlugManager assumes exclusive control for adding and removing devices to this port.
743    /// TODO(293801301): Remove unused_variables after aarch64 support
744    #[allow(unused)]
745    pub fn add_port(&mut self, port: Arc<Mutex<dyn HotPlugBus>>) -> Result<()> {
746        let worker_client = self
747            .worker_client
748            .as_ref()
749            .context("No worker thread. Is set_rootbus_controller not called?")?;
750        let port_lock = port.lock();
751        // Rejects hotplug bus with downstream devices.
752        if !port_lock.is_empty() {
753            bail!("invalid hotplug bus");
754        }
755        let pci_address = port_lock
756            .get_address()
757            .context("Hotplug bus PCI address missing")?;
758        // Reject hotplug buses not on rootbus, since otherwise the order of enumeration depends on
759        // the topology of PCI.
760        if pci_address.bus != 0 {
761            bail!("hotplug port on non-root bus not supported");
762        }
763        let downstream_bus = port_lock
764            .get_secondary_bus_number()
765            .context("cannot get downstream bus")?;
766        drop(port_lock);
767        if let Some(prev_address) = self.bus_address_map.insert(downstream_bus, pci_address) {
768            bail!(
769                "Downstream bus of new port is conflicting with previous port at {}",
770                &prev_address
771            );
772        }
773        self.port_stubs.insert(
774            pci_address,
775            PortManagerStub {
776                downstream_bus,
777                devices: HashMap::new(),
778            },
779        );
780        match worker_client.send_worker_command(WorkerCommand::AddPort(
781            pci_address,
782            PortWorkerStub::new(port, downstream_bus)?,
783        ))? {
784            WorkerResponse::AddPortOk => Ok(()),
785            WorkerResponse::InvalidCommand(e) => Err(e),
786            r => bail!("Unexpected response from worker: {:?}", &r),
787        }
788    }
789
790    /// hotplugs up to 8 PCI devices as "functions of a device" (in PCI Bus Device Function sense).
791    ///
792    /// returns the bus number of the bus on success.
793    pub fn hotplug_device(
794        &mut self,
795        resource_carriers: Vec<ResourceCarrier>,
796        linux: &mut RunnableLinuxVm,
797        resources: &mut SystemAllocator,
798    ) -> Result<u8> {
799        let worker_client = self
800            .worker_client
801            .as_ref()
802            .context("No worker thread. Is set_rootbus_controller not called?")?;
803        if resource_carriers.len() > 8 || resource_carriers.is_empty() {
804            bail!("PCI function count has to be 1 to 8 inclusive");
805        }
806        let pci_address = match worker_client.send_worker_command(WorkerCommand::GetEmptyPort)? {
807            WorkerResponse::GetEmptyPortOk(p) => Ok(p),
808            WorkerResponse::InvalidCommand(e) => Err(e),
809            r => bail!("Unexpected response from worker: {:?}", &r),
810        }?;
811        let port_stub = self
812            .port_stubs
813            .get_mut(&pci_address)
814            .context("Cannot find port")?;
815        let downstream_bus = port_stub.downstream_bus;
816        let mut devices = Vec::new();
817        for (func_num, mut resource_carrier) in resource_carriers.into_iter().enumerate() {
818            let device_address = PciAddress::new(0, downstream_bus as u32, 0, func_num as u32)?;
819            let hotplug_key = HotPlugKey::GuestDevice {
820                guest_addr: device_address,
821            };
822            resource_carrier.allocate_address(device_address, resources)?;
823            let irq_evt = IrqLevelEvent::new()?;
824            let (pin, irq_num) = match downstream_bus % 4 {
825                0 => (PciInterruptPin::IntA, 0),
826                1 => (PciInterruptPin::IntB, 1),
827                2 => (PciInterruptPin::IntC, 2),
828                _ => (PciInterruptPin::IntD, 3),
829            };
830            resource_carrier.assign_irq(irq_evt.try_clone()?, pin, irq_num);
831            let (proxy_device, pid) = self
832                .jail_warden
833                .make_proxy_device(resource_carrier)
834                .context("make proxy device")?;
835            let device_id = proxy_device.lock().device_id();
836            let device_name = proxy_device.lock().debug_label();
837            linux.irq_chip.register_level_irq_event(
838                irq_num,
839                &irq_evt,
840                IrqEventSource {
841                    device_id,
842                    queue_id: 0,
843                    device_name: device_name.clone(),
844                },
845            )?;
846            let pid: u32 = pid.try_into().context("fork fail")?;
847            if pid > 0 {
848                linux.pid_debug_label_map.insert(pid, device_name);
849            }
850            devices.push(GuestDeviceStub {
851                pci_addr: device_address,
852                key: hotplug_key,
853                device: proxy_device,
854            });
855            port_stub
856                .devices
857                .insert(device_address, RecoverableResource { irq_num, irq_evt });
858        }
859        // Ask worker to schedule hotplug signal.
860        match worker_client.send_worker_command(WorkerCommand::SignalHotPlug(
861            SignalHotPlugCommand::new(pci_address, devices)?,
862        ))? {
863            WorkerResponse::SignalOk => Ok(downstream_bus),
864            WorkerResponse::InvalidCommand(e) => Err(e),
865            r => bail!("Unexpected response from worker: {:?}", &r),
866        }
867    }
868
869    /// Removes all hotplugged devices on the hotplug bus.
870    pub fn remove_hotplug_device(
871        &mut self,
872        bus: u8,
873        linux: &mut RunnableLinuxVm,
874        resources: &mut SystemAllocator,
875    ) -> Result<()> {
876        let worker_client = self
877            .worker_client
878            .as_ref()
879            .context("No worker thread. Is set_rootbus_controller not called?")?;
880        let pci_address = self
881            .bus_address_map
882            .get(&bus)
883            .with_context(|| format!("Port {} is not known", &bus))?;
884        match worker_client.send_worker_command(WorkerCommand::GetPortState(*pci_address))? {
885            WorkerResponse::GetPortStateOk(PortState::Occupied(_)) => {}
886            WorkerResponse::GetPortStateOk(PortState::Empty(_)) => {
887                bail!("Port {} is empty", &bus)
888            }
889            WorkerResponse::InvalidCommand(e) => {
890                return Err(e);
891            }
892            wr => bail!("Unexpected response from worker: {:?}", &wr),
893        };
894        // Performs a surprise removal. That is, not waiting for hot removal completion before
895        // deleting the resources.
896        match worker_client.send_worker_command(WorkerCommand::SignalHotUnplug(*pci_address))? {
897            WorkerResponse::SignalOk => {}
898            WorkerResponse::InvalidCommand(e) => {
899                return Err(e);
900            }
901            wr => bail!("Unexpected response from worker: {:?}", &wr),
902        }
903        // Remove all devices on the hotplug bus.
904        let port_stub = self
905            .port_stubs
906            .get_mut(pci_address)
907            .with_context(|| format!("Port {} is not known", &bus))?;
908        for (downstream_address, recoverable_resource) in port_stub.devices.drain() {
909            // port_stub.port does not have remove_hotplug_device method, as devices are removed
910            // when hot_unplug is called.
911            resources.release_pci(downstream_address);
912            linux.irq_chip.unregister_level_irq_event(
913                recoverable_resource.irq_num,
914                &recoverable_resource.irq_evt,
915            )?;
916        }
917        Ok(())
918    }
919}
920
921#[cfg(test)]
922mod tests {
923    use std::thread;
924    use std::time::Duration;
925
926    use devices::MockDevice;
927
928    use super::*;
929
930    /// A MockPort that only supports hot_plug and hot_unplug commands, and signaling command
931    /// complete manually, which is sufficient for PciHotPlugWorker unit test.
932    struct MockPort {
933        cc_event: Event,
934        downstream_bus: u8,
935        ready_events: Vec<Event>,
936    }
937
938    impl MockPort {
939        fn new(downstream_bus: u8) -> Self {
940            Self {
941                cc_event: Event::new().unwrap(),
942                downstream_bus,
943                ready_events: Vec::new(),
944            }
945        }
946
947        fn signal_cc(&self) {
948            self.cc_event.reset().unwrap();
949            self.cc_event.signal().unwrap();
950        }
951
952        fn signal_ready(&mut self) {
953            for event in self.ready_events.drain(..) {
954                event.reset().unwrap();
955                event.signal().unwrap();
956            }
957        }
958    }
959
960    impl HotPlugBus for MockPort {
961        fn hot_plug(&mut self, _addr: PciAddress) -> anyhow::Result<Option<Event>> {
962            self.cc_event = Event::new().unwrap();
963            Ok(Some(self.cc_event.try_clone().unwrap()))
964        }
965
966        fn hot_unplug(&mut self, _addr: PciAddress) -> anyhow::Result<Option<Event>> {
967            self.cc_event = Event::new().unwrap();
968            Ok(Some(self.cc_event.try_clone().unwrap()))
969        }
970
971        fn get_ready_notification(&mut self) -> anyhow::Result<Event> {
972            let event = Event::new()?;
973            self.ready_events.push(event.try_clone()?);
974            Ok(event)
975        }
976
977        fn is_match(&self, _host_addr: PciAddress) -> Option<u8> {
978            None
979        }
980
981        fn get_address(&self) -> Option<PciAddress> {
982            None
983        }
984
985        fn get_secondary_bus_number(&self) -> Option<u8> {
986            Some(self.downstream_bus)
987        }
988
989        fn add_hotplug_device(&mut self, _hotplug_key: HotPlugKey, _guest_addr: PciAddress) {}
990
991        fn get_hotplug_device(&self, _hotplug_key: HotPlugKey) -> Option<PciAddress> {
992            None
993        }
994
995        fn is_empty(&self) -> bool {
996            true
997        }
998
999        fn get_hotplug_key(&self) -> Option<HotPlugKey> {
1000            None
1001        }
1002    }
1003
1004    fn new_port(downstream_bus: u8) -> Arc<Mutex<MockPort>> {
1005        Arc::new(Mutex::new(MockPort::new(downstream_bus)))
1006    }
1007
1008    fn poll_until_with_timeout<F>(f: F, timeout: Duration) -> bool
1009    where
1010        F: Fn() -> bool,
1011    {
1012        for _ in 0..timeout.as_millis() {
1013            if f() {
1014                return true;
1015            }
1016            thread::sleep(Duration::from_millis(1));
1017        }
1018        false
1019    }
1020
1021    #[test]
1022    fn worker_empty_port_ordering() {
1023        let (rootbus_controller, _rootbus_recvr) = mpsc::channel();
1024        let client = WorkerClient::new(rootbus_controller).unwrap();
1025        // Port A: upstream 00:01.1, downstream 2.
1026        let upstream_addr_a = PciAddress {
1027            bus: 0,
1028            dev: 1,
1029            func: 1,
1030        };
1031        let bus_a = 2;
1032        let downstream_addr_a = PciAddress {
1033            bus: bus_a,
1034            dev: 0,
1035            func: 0,
1036        };
1037        let hotplug_key_a = HotPlugKey::GuestDevice {
1038            guest_addr: downstream_addr_a,
1039        };
1040        let device_a = GuestDeviceStub {
1041            pci_addr: downstream_addr_a,
1042            key: hotplug_key_a,
1043            device: Arc::new(Mutex::new(MockDevice::new())),
1044        };
1045        let hotplug_command_a =
1046            SignalHotPlugCommand::new(upstream_addr_a, [device_a].to_vec()).unwrap();
1047        let port_a = new_port(bus_a);
1048        // Port B: upstream 00:01.0, downstream 3.
1049        let upstream_addr_b = PciAddress {
1050            bus: 0,
1051            dev: 1,
1052            func: 0,
1053        };
1054        let bus_b = 3;
1055        let downstream_addr_b = PciAddress {
1056            bus: bus_b,
1057            dev: 0,
1058            func: 0,
1059        };
1060        let hotplug_key_b = HotPlugKey::GuestDevice {
1061            guest_addr: downstream_addr_b,
1062        };
1063        let device_b = GuestDeviceStub {
1064            pci_addr: downstream_addr_b,
1065            key: hotplug_key_b,
1066            device: Arc::new(Mutex::new(MockDevice::new())),
1067        };
1068        let hotplug_command_b =
1069            SignalHotPlugCommand::new(upstream_addr_b, [device_b].to_vec()).unwrap();
1070        let port_b = new_port(bus_b);
1071        // Port C: upstream 00:02.0, downstream 4.
1072        let upstream_addr_c = PciAddress {
1073            bus: 0,
1074            dev: 2,
1075            func: 0,
1076        };
1077        let bus_c = 4;
1078        let downstream_addr_c = PciAddress {
1079            bus: bus_c,
1080            dev: 0,
1081            func: 0,
1082        };
1083        let hotplug_key_c = HotPlugKey::GuestDevice {
1084            guest_addr: downstream_addr_c,
1085        };
1086        let device_c = GuestDeviceStub {
1087            pci_addr: downstream_addr_c,
1088            key: hotplug_key_c,
1089            device: Arc::new(Mutex::new(MockDevice::new())),
1090        };
1091        let hotplug_command_c =
1092            SignalHotPlugCommand::new(upstream_addr_c, [device_c].to_vec()).unwrap();
1093        let port_c = new_port(bus_c);
1094        assert_eq!(
1095            WorkerResponse::AddPortOk,
1096            client
1097                .send_worker_command(WorkerCommand::AddPort(
1098                    upstream_addr_a,
1099                    PortWorkerStub::new(port_a.clone(), bus_a).unwrap()
1100                ))
1101                .unwrap()
1102        );
1103        assert_eq!(
1104            WorkerResponse::AddPortOk,
1105            client
1106                .send_worker_command(WorkerCommand::AddPort(
1107                    upstream_addr_b,
1108                    PortWorkerStub::new(port_b.clone(), bus_b).unwrap()
1109                ))
1110                .unwrap()
1111        );
1112        assert_eq!(
1113            WorkerResponse::AddPortOk,
1114            client
1115                .send_worker_command(WorkerCommand::AddPort(
1116                    upstream_addr_c,
1117                    PortWorkerStub::new(port_c.clone(), bus_c).unwrap()
1118                ))
1119                .unwrap()
1120        );
1121        port_a.lock().signal_ready();
1122        assert!(poll_until_with_timeout(
1123            || client
1124                .send_worker_command(WorkerCommand::GetPortState(upstream_addr_a))
1125                .unwrap()
1126                == WorkerResponse::GetPortStateOk(PortState::Empty(0)),
1127            Duration::from_millis(500)
1128        ));
1129        port_b.lock().signal_ready();
1130        assert!(poll_until_with_timeout(
1131            || client
1132                .send_worker_command(WorkerCommand::GetPortState(upstream_addr_b))
1133                .unwrap()
1134                == WorkerResponse::GetPortStateOk(PortState::Empty(0)),
1135            Duration::from_millis(500)
1136        ));
1137        port_c.lock().signal_ready();
1138        assert!(poll_until_with_timeout(
1139            || client
1140                .send_worker_command(WorkerCommand::GetPortState(upstream_addr_c))
1141                .unwrap()
1142                == WorkerResponse::GetPortStateOk(PortState::Empty(0)),
1143            Duration::from_millis(500)
1144        ));
1145        // All ports empty and in sync. Should get port B.
1146        assert_eq!(
1147            WorkerResponse::GetEmptyPortOk(upstream_addr_b),
1148            client
1149                .send_worker_command(WorkerCommand::GetEmptyPort)
1150                .unwrap()
1151        );
1152        assert_eq!(
1153            WorkerResponse::SignalOk,
1154            client
1155                .send_worker_command(WorkerCommand::SignalHotPlug(hotplug_command_b))
1156                .unwrap()
1157        );
1158        // Should get port A.
1159        assert_eq!(
1160            WorkerResponse::GetEmptyPortOk(upstream_addr_a),
1161            client
1162                .send_worker_command(WorkerCommand::GetEmptyPort)
1163                .unwrap()
1164        );
1165        assert_eq!(
1166            WorkerResponse::SignalOk,
1167            client
1168                .send_worker_command(WorkerCommand::SignalHotPlug(hotplug_command_a))
1169                .unwrap()
1170        );
1171        // Should get port C.
1172        assert_eq!(
1173            WorkerResponse::GetEmptyPortOk(upstream_addr_c),
1174            client
1175                .send_worker_command(WorkerCommand::GetEmptyPort)
1176                .unwrap()
1177        );
1178        assert_eq!(
1179            WorkerResponse::SignalOk,
1180            client
1181                .send_worker_command(WorkerCommand::SignalHotPlug(hotplug_command_c))
1182                .unwrap()
1183        );
1184        // Should get an error since no port is empty.
1185        if let WorkerResponse::InvalidCommand(_) = client
1186            .send_worker_command(WorkerCommand::GetEmptyPort)
1187            .unwrap()
1188        {
1189            // Assert result is of Error type.
1190        } else {
1191            unreachable!();
1192        }
1193        // Remove device from port A, immediately it should be available.
1194        assert_eq!(
1195            WorkerResponse::SignalOk,
1196            client
1197                .send_worker_command(WorkerCommand::SignalHotUnplug(upstream_addr_a))
1198                .unwrap()
1199        );
1200        assert_eq!(
1201            WorkerResponse::GetEmptyPortOk(upstream_addr_a),
1202            client
1203                .send_worker_command(WorkerCommand::GetEmptyPort)
1204                .unwrap()
1205        );
1206        // Moreover, it should be 2 steps behind.
1207        assert_eq!(
1208            WorkerResponse::GetPortStateOk(PortState::Empty(2)),
1209            client
1210                .send_worker_command(WorkerCommand::GetPortState(upstream_addr_a))
1211                .unwrap()
1212        );
1213    }
1214
1215    #[test]
1216    fn worker_port_state_transitions() {
1217        let (rootbus_controller, _rootbus_recvr) = mpsc::channel();
1218        let client = WorkerClient::new(rootbus_controller).unwrap();
1219        let upstream_addr = PciAddress {
1220            bus: 0,
1221            dev: 1,
1222            func: 1,
1223        };
1224        let bus = 2;
1225        let downstream_addr = PciAddress {
1226            bus,
1227            dev: 0,
1228            func: 0,
1229        };
1230        let hotplug_key = HotPlugKey::GuestDevice {
1231            guest_addr: downstream_addr,
1232        };
1233        let device = GuestDeviceStub {
1234            pci_addr: downstream_addr,
1235            key: hotplug_key,
1236            device: Arc::new(Mutex::new(MockDevice::new())),
1237        };
1238        let hotplug_command = SignalHotPlugCommand::new(upstream_addr, [device].to_vec()).unwrap();
1239        let port = new_port(bus);
1240        assert_eq!(
1241            WorkerResponse::AddPortOk,
1242            client
1243                .send_worker_command(WorkerCommand::AddPort(
1244                    upstream_addr,
1245                    PortWorkerStub::new(port.clone(), bus).unwrap()
1246                ))
1247                .unwrap()
1248        );
1249        port.lock().signal_ready();
1250        assert!(poll_until_with_timeout(
1251            || client
1252                .send_worker_command(WorkerCommand::GetPortState(upstream_addr))
1253                .unwrap()
1254                == WorkerResponse::GetPortStateOk(PortState::Empty(0)),
1255            Duration::from_millis(500)
1256        ));
1257        assert_eq!(
1258            WorkerResponse::SignalOk,
1259            client
1260                .send_worker_command(WorkerCommand::SignalHotPlug(hotplug_command.clone()))
1261                .unwrap()
1262        );
1263        assert!(poll_until_with_timeout(
1264            || client
1265                .send_worker_command(WorkerCommand::GetPortState(upstream_addr))
1266                .unwrap()
1267                == WorkerResponse::GetPortStateOk(PortState::Occupied(1)),
1268            Duration::from_millis(500)
1269        ));
1270        assert_eq!(
1271            WorkerResponse::SignalOk,
1272            client
1273                .send_worker_command(WorkerCommand::SignalHotUnplug(upstream_addr))
1274                .unwrap()
1275        );
1276        assert!(poll_until_with_timeout(
1277            || client
1278                .send_worker_command(WorkerCommand::GetPortState(upstream_addr))
1279                .unwrap()
1280                == WorkerResponse::GetPortStateOk(PortState::Empty(2)),
1281            Duration::from_millis(500)
1282        ));
1283        assert_eq!(
1284            WorkerResponse::SignalOk,
1285            client
1286                .send_worker_command(WorkerCommand::SignalHotPlug(hotplug_command.clone()))
1287                .unwrap()
1288        );
1289        assert!(poll_until_with_timeout(
1290            || client
1291                .send_worker_command(WorkerCommand::GetPortState(upstream_addr))
1292                .unwrap()
1293                == WorkerResponse::GetPortStateOk(PortState::Occupied(3)),
1294            Duration::from_millis(500)
1295        ));
1296        port.lock().signal_cc();
1297        assert!(poll_until_with_timeout(
1298            || client
1299                .send_worker_command(WorkerCommand::GetPortState(upstream_addr))
1300                .unwrap()
1301                == WorkerResponse::GetPortStateOk(PortState::Occupied(2)),
1302            Duration::from_millis(500)
1303        ));
1304        assert_eq!(
1305            WorkerResponse::SignalOk,
1306            client
1307                .send_worker_command(WorkerCommand::SignalHotUnplug(upstream_addr))
1308                .unwrap()
1309        );
1310        // Moves from Occupied(2) to Empty(1) since it is redundant to unplug a device that is yet
1311        // to be plugged in.
1312        assert!(poll_until_with_timeout(
1313            || client
1314                .send_worker_command(WorkerCommand::GetPortState(upstream_addr))
1315                .unwrap()
1316                == WorkerResponse::GetPortStateOk(PortState::Empty(1)),
1317            Duration::from_millis(500)
1318        ));
1319        port.lock().signal_cc();
1320        assert!(poll_until_with_timeout(
1321            || client
1322                .send_worker_command(WorkerCommand::GetPortState(upstream_addr))
1323                .unwrap()
1324                == WorkerResponse::GetPortStateOk(PortState::Empty(0)),
1325            Duration::from_millis(500)
1326        ));
1327    }
1328
1329    #[test]
1330    fn worker_port_early_plug_state_transitions() {
1331        let (rootbus_controller, _rootbus_recvr) = mpsc::channel();
1332        let client = WorkerClient::new(rootbus_controller).unwrap();
1333        let upstream_addr = PciAddress {
1334            bus: 0,
1335            dev: 1,
1336            func: 1,
1337        };
1338        let bus = 2;
1339        let downstream_addr = PciAddress {
1340            bus,
1341            dev: 0,
1342            func: 0,
1343        };
1344        let hotplug_key = HotPlugKey::GuestDevice {
1345            guest_addr: downstream_addr,
1346        };
1347        let device = GuestDeviceStub {
1348            pci_addr: downstream_addr,
1349            key: hotplug_key,
1350            device: Arc::new(Mutex::new(MockDevice::new())),
1351        };
1352        let hotplug_command = SignalHotPlugCommand::new(upstream_addr, [device].to_vec()).unwrap();
1353        let port = new_port(bus);
1354        assert_eq!(
1355            WorkerResponse::AddPortOk,
1356            client
1357                .send_worker_command(WorkerCommand::AddPort(
1358                    upstream_addr,
1359                    PortWorkerStub::new(port.clone(), bus).unwrap()
1360                ))
1361                .unwrap()
1362        );
1363        assert!(poll_until_with_timeout(
1364            || client
1365                .send_worker_command(WorkerCommand::GetPortState(upstream_addr))
1366                .unwrap()
1367                == WorkerResponse::GetPortStateOk(PortState::EmptyNotReady),
1368            Duration::from_millis(500)
1369        ));
1370        assert_eq!(
1371            WorkerResponse::SignalOk,
1372            client
1373                .send_worker_command(WorkerCommand::SignalHotPlug(hotplug_command.clone()))
1374                .unwrap()
1375        );
1376        assert!(poll_until_with_timeout(
1377            || client
1378                .send_worker_command(WorkerCommand::GetPortState(upstream_addr))
1379                .unwrap()
1380                == WorkerResponse::GetPortStateOk(PortState::OccupiedNotReady),
1381            Duration::from_millis(500)
1382        ));
1383        port.lock().signal_ready();
1384        assert!(poll_until_with_timeout(
1385            || client
1386                .send_worker_command(WorkerCommand::GetPortState(upstream_addr))
1387                .unwrap()
1388                == WorkerResponse::GetPortStateOk(PortState::Occupied(1)),
1389            Duration::from_millis(500)
1390        ));
1391        port.lock().signal_cc();
1392        assert!(poll_until_with_timeout(
1393            || client
1394                .send_worker_command(WorkerCommand::GetPortState(upstream_addr))
1395                .unwrap()
1396                == WorkerResponse::GetPortStateOk(PortState::Occupied(0)),
1397            Duration::from_millis(500)
1398        ));
1399    }
1400}