xhcid: associate stalls on setup stage with transfers

This commit is contained in:
Jeremy Soller
2024-04-15 15:02:05 -06:00
parent 64fcd0a323
commit 806be9bdc6
3 changed files with 54 additions and 35 deletions
+44 -32
View File
@@ -66,7 +66,7 @@ impl RingId {
#[derive(Clone, Copy, Debug)]
pub enum StateKind {
CommandCompletion { phys_ptr: u64 },
Transfer { phys_ptr: u64, ring_id: RingId },
Transfer { first_phys_ptr: u64, last_phys_ptr: u64, ring_id: RingId },
Other(TrbType),
}
@@ -259,32 +259,42 @@ impl IrqReactor {
}
}
StateKind::Transfer { phys_ptr, ring_id } if trb.trb_type() == TrbType::Transfer as u8 => {
StateKind::Transfer { first_phys_ptr, last_phys_ptr, ring_id } if trb.trb_type() == TrbType::Transfer as u8 => {
if let Some(src_trb) = trb.transfer_event_trb_pointer().map(|ptr| self.hci.get_transfer_trb(ptr, ring_id)).flatten() {
if trb.transfer_event_trb_pointer() == Some(phys_ptr) {
// Give the source transfer TRB together with the event TRB, to the future.
let state = self.states.remove(index);
*state.message.lock().unwrap() = Some(NextEventTrb {
src_trb: Some(src_trb),
event_trb: trb.clone(),
});
state.waker.wake();
return;
} else if trb.transfer_event_trb_pointer().is_none() {
// Ring Overrun, Ring Underrun, or Virtual Function Event Ring Full.
//
// These errors are caused when either an isoch transfer that shall write data, doesn't
// have any data since the ring is empty, or if an isoch receive is impossible due to a
// full ring. The Virtual Function Event Ring Full is only for Virtual Machine
// Managers, and since this isn't implemented yet, they are irrelevant.
//
// The best solution here is to differentiate between isoch transfers (and
// virtual function event rings when virtualization gets implemented), with
// regular commands and transfers, and send the error TRB to all of them, or
// possibly an error code wrapped in a Result.
self.acknowledge_failed_transfer_trbs(trb);
return;
match trb.transfer_event_trb_pointer() {
Some(phys_ptr) => {
let matches = if first_phys_ptr <= last_phys_ptr {
phys_ptr >= first_phys_ptr && phys_ptr <= last_phys_ptr
} else {
// Handle ring buffer wrap
phys_ptr >= first_phys_ptr || phys_ptr <= last_phys_ptr
};
if matches {
// Give the source transfer TRB together with the event TRB, to the future.
let state = self.states.remove(index);
*state.message.lock().unwrap() = Some(NextEventTrb {
src_trb: Some(src_trb),
event_trb: trb.clone(),
});
state.waker.wake();
return;
}
},
None => {
// Ring Overrun, Ring Underrun, or Virtual Function Event Ring Full.
//
// These errors are caused when either an isoch transfer that shall write data, doesn't
// have any data since the ring is empty, or if an isoch receive is impossible due to a
// full ring. The Virtual Function Event Ring Full is only for Virtual Machine
// Managers, and since this isn't implemented yet, they are irrelevant.
//
// The best solution here is to differentiate between isoch transfers (and
// virtual function event rings when virtualization gets implemented), with
// regular commands and transfers, and send the error TRB to all of them, or
// possibly an error code wrapped in a Result.
self.acknowledge_failed_transfer_trbs(trb);
return;
}
}
}
}
@@ -445,19 +455,21 @@ impl Xhci {
Some(function(ring_ref))
}
pub fn next_transfer_event_trb(&self, ring_id: RingId, ring: &Ring, trb: &Trb, doorbell: EventDoorbell) -> impl Future<Output = NextEventTrb> + Send + Sync + 'static {
if ! trb.is_transfer_trb() {
panic!("Invalid TRB type given to next_transfer_event_trb(): {} (TRB {:?}. Expected transfer TRB.", trb.trb_type(), trb)
pub fn next_transfer_event_trb(&self, ring_id: RingId, ring: &Ring, first_trb: &Trb, last_trb: &Trb, doorbell: EventDoorbell) -> impl Future<Output = NextEventTrb> + Send + Sync + 'static {
if ! last_trb.is_transfer_trb() {
panic!("Invalid TRB type given to next_transfer_event_trb(): {} (TRB {:?}. Expected transfer TRB.", last_trb.trb_type(), last_trb)
}
let is_isoch_or_vf = trb.trb_type() == TrbType::Isoch as u8;
let phys_ptr = ring.trb_phys_ptr(self.cap.ac64(), trb);
let is_isoch_or_vf = last_trb.trb_type() == TrbType::Isoch as u8;
let first_phys_ptr = ring.trb_phys_ptr(self.cap.ac64(), first_trb);
let last_phys_ptr = ring.trb_phys_ptr(self.cap.ac64(), last_trb);
EventTrbFuture::Pending {
state: FutureState {
is_isoch_or_vf,
state_kind: StateKind::Transfer {
ring_id,
phys_ptr,
first_phys_ptr,
last_phys_ptr,
},
message: Arc::new(Mutex::new(None)),
},
+4 -1
View File
@@ -85,12 +85,14 @@ impl Xhci {
/// Gets descriptors, before the port state is initiated.
async fn get_desc_raw<T>(&self, port: usize, slot: u8, kind: usb::DescriptorKind, index: u8, desc: &mut Dma<T>) -> Result<()> {
let len = mem::size_of::<T>();
log::debug!("get_desc_raw port {} slot {} kind {:?} index {} len {}", port, slot, kind, index, len);
let future = {
let mut port_state = self.port_states.get_mut(&port).ok_or(Error::new(ENOENT))?;
let ring = port_state.endpoint_states.get_mut(&0).ok_or(Error::new(EIO))?.ring().expect("no ring for the default control pipe");
let (cmd, cycle) = ring.next();
let first_index = ring.next_index();
let (cmd, cycle) = (&mut ring.trbs[first_index], ring.cycle);
cmd.setup(
usb::Setup::get_descriptor(kind, index, 0, len as u16),
TransferKind::In,
@@ -114,6 +116,7 @@ impl Xhci {
self.next_transfer_event_trb(
RingId::default_control_pipe(port as u8),
&ring,
&ring.trbs[first_index],
&ring.trbs[last_index],
EventDoorbell::new(self, usize::from(slot), Self::def_control_endp_doorbell())
)
+6 -2
View File
@@ -313,7 +313,8 @@ impl Xhci {
.ring()
.ok_or(Error::new(EIO))?;
let (cmd, cycle) = ring.next();
let first_index = ring.next_index();
let (cmd, cycle) = (&mut ring.trbs[first_index], ring.cycle);
cmd.setup(setup, tk, cycle);
if tk != TransferKind::NoData {
@@ -340,6 +341,7 @@ impl Xhci {
self.next_transfer_event_trb(
RingId::default_control_pipe(port_num as u8),
ring,
&ring.trbs[first_index],
&ring.trbs[last_index],
EventDoorbell::new(self, usize::from(slot), Self::def_control_endp_doorbell())
)
@@ -430,6 +432,8 @@ impl Xhci {
break self.next_transfer_event_trb(
super::irq_reactor::RingId { port: port_num as u8, endpoint_num: endp_num, stream_id },
ring,
//TODO: find first TRB
&ring.trbs[last_index],
&ring.trbs[last_index],
EventDoorbell::new(self, usize::from(slot), if has_streams {
doorbell_data_stream
@@ -2128,7 +2132,7 @@ pub fn handle_transfer_event_trb(name: &str, event_trb: &Trb, transfer_trb: &Trb
if event_trb.completion_code() == TrbCompletionCode::Success as u8 || event_trb.completion_code() == TrbCompletionCode::ShortPacket as u8 {
Ok(())
} else {
error!("{} transfer (TRB {:?}) failed with event trb {:?}", name, transfer_trb, event_trb);
error!("{} transfer {:?} failed with event {:?}", name, transfer_trb, event_trb);
Err(Error::new(EIO))
}
}