1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
//! Event Queues (EQ) are circular buffers used by the HCA to report completion events, errors and other asynchronous events.
//! This module defines the layout of an EQ, the context used to initialize an EQ and related functions.
//!
//! (PRM Section 8.19: Events and Interrupts)
use zerocopy::{U32, FromBytes};
use volatile::Volatile;
use byteorder::BigEndian;
use memory::{MappedPages, BorrowedSliceMappedPages, Mutable};
#[allow(unused_imports)]
use crate::{
log_page_size, Cqn, Eqn, UAR_MASK, LOG_QUEUE_SIZE_MASK, LOG_QUEUE_SIZE_SHIFT, LOG_PAGE_SIZE_SHIFT, HW_OWNERSHIP,
command_queue::CommandOpcode
};
/// The data structure containing EQ initialization parameters.
/// It is passed to the HCA at the time of EQ creation.
///
/// (PRM Section 8.19.17: Event Queue Context)
#[derive(FromBytes, Default)]
#[repr(C)]
pub(crate) struct EventQueueContext {
/// A multi-part field:
/// * `status`: occupies bits [31:28]
/// * `ec`: if set all the EQE's are collapsed to the first, occupies bit 18
/// * `st`: event delivery state machine, occupies bits [11:8]
status: Volatile<U32<BigEndian>>,
_padding1: u32,
/// This field must be set to zero
page_offset: Volatile<U32<BigEndian>>,
/// A multi-part field:
/// * `log_eq_size`: Log (base 2) of the EQ size (in entries), occupies bits [28:24]
/// * `uar_page`: UAR page this EQ can be accessed through, occupies bits [23:0]
uar_log_eq_size: Volatile<U32<BigEndian>>,
_padding2: u32,
/// MSI-X table entry index to be used to signal interrupts on this EQ
intr: Volatile<U32<BigEndian>>,
/// Log (base 2) of page size in units of 4KiB
log_pg_size: Volatile<U32<BigEndian>>,
_padding3: [u8;12],
/// Consumer counter. The counter is incremented for each EQE polled from the EQ.
consumer_counter: Volatile<U32<BigEndian>>,
/// Producer Counter. The counter is incremented for each EQE that is written by the HW to the EQ.
producer_counter: Volatile<U32<BigEndian>>,
_padding4: [u8;16],
}
const _: () = assert!(core::mem::size_of::<EventQueueContext>() == 64);
impl EventQueueContext {
/// Create and initialize the fields of the EQ context.
/// The EQ context is then passed to the HCA when creating the EQ.
///
/// # Arguments
/// * `uar_page`: UAR page the EQ can be accessed through.
/// * `eq_size`: number of entries in the EQ.
pub(crate) fn init(uar_page: u32, eq_size: u32) -> EventQueueContext {
// set all entries to zero
let mut ctxt = EventQueueContext::default();
// initialize all other required fields
let uar = uar_page & UAR_MASK;
let size = (libm::log2(eq_size as f64) as u32 & LOG_QUEUE_SIZE_MASK) << LOG_QUEUE_SIZE_SHIFT;
ctxt.uar_log_eq_size.write(U32::new(uar | size));
let log_eq_page_size = log_page_size(eq_size * core::mem::size_of::<EventQueueEntry>() as u32);
ctxt.log_pg_size.write(U32::new(log_eq_page_size << LOG_PAGE_SIZE_SHIFT));
ctxt
}
/// Offset that this context is written to in the mailbox buffer
pub(crate) fn mailbox_offset() -> usize { 0 }
}
/// The layout of an entry in the EQ buffer.
///
/// (PRM Section 8.19.2.2: EQE Format)
#[derive(FromBytes, Default, Debug)]
#[repr(C)]
pub struct EventQueueEntry {
event_type: Volatile<U32<BigEndian>>,
_padding1: [u8; 28],
/// delivers auxiliary data to handle the event
event_data: Volatile<[u8; 28]>,
/// A multi-part field:
/// * `signature`: byte-wise XOR of EQE, occupies bits \[15:8\]
/// * `owner`: owner of the entry, occupies bit 0
signature_owner: Volatile<U32<BigEndian>>
}
const _: () = assert!(core::mem::size_of::<EventQueueEntry>() == 64);
impl EventQueueEntry {
pub fn init(&mut self) {
// set all fields to zero
*self = EventQueueEntry::default();
// all EQEs must initially be set to HW ownership
self.signature_owner.write(U32::new(HW_OWNERSHIP));
// Snabb, I believe, has this wrong.
// They are setting the ownership bit within the padding field
}
/// Prints out the fields of an EQE in the format used by other drivers (e.g. Linux, Snabb)
pub fn dump(&self, i: usize) {
debug!("EQE {}", i);
unsafe {
let ptr = self as *const EventQueueEntry as *const u32;
debug!("{:#010x} {:#010x} {:#010x} {:#010x}", (*ptr).to_be(), (*ptr.offset(1)).to_be(), (*ptr.offset(2)).to_be(), (*ptr.offset(3)).to_be());
debug!("{:#010x} {:#010x} {:#010x} {:#010x}", (*ptr.offset(4)).to_be(), (*ptr.offset(5)).to_be(), (*ptr.offset(6)).to_be(), (*ptr.offset(7)).to_be());
debug!("{:#010x} {:#010x} {:#010x} {:#010x}", (*ptr.offset(8)).to_be(), (*ptr.offset(9)).to_be(), (*ptr.offset(10)).to_be(), (*ptr.offset(11)).to_be());
debug!("{:#010x} {:#010x} {:#010x} {:#010x} \n", (*ptr.offset(12)).to_be(), (*ptr.offset(13)).to_be(), (*ptr.offset(14)).to_be(), (*ptr.offset(15)).to_be());
}
}
}
/// A data structure that contains the EQ buffer
/// and is used to interact with the EQ once initialized.
#[allow(dead_code)]
pub struct EventQueue {
/// Physically-contiguous event queue entries
entries: BorrowedSliceMappedPages<EventQueueEntry, Mutable>,
/// EQ number that is returned by the [`CommandOpcode::CreateEq`] command
eqn: Eqn
}
impl EventQueue {
/// Creates an event queue by mapping the buffer as a slice of [`EventQueueEntry`]s.
/// Each EQE is set to an initial state.
///
/// # Arguments
/// * `mp`: memory that is to be transformed into a slice of EQEs.
/// The starting physical address should have been passed to the HCA when creating the EQ.
/// * `num_entries`: number of entries in the EQ
/// * `eqn`: EQ number returned by the HCA
pub fn init(mp: MappedPages, num_entries: usize, eqn: Eqn) -> Result<EventQueue, &'static str> {
let mut entries = mp.into_borrowed_slice_mut::<EventQueueEntry>(0, num_entries)
.map_err(|(_mp, err)| err)?;
for eqe in entries.iter_mut() {
eqe.init()
}
Ok( EventQueue { entries, eqn } )
}
/// Prints out all entries in the EQ
pub fn dump(&self) {
for (i, eqe) in self.entries.iter().enumerate() {
eqe.dump(i)
}
}
}