-
Notifications
You must be signed in to change notification settings - Fork 123
nvme_driver: allocate different DMA memory sizes if not bounce buffering #1306
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -166,11 +166,18 @@ impl PendingCommands { | |
} | ||
|
||
impl QueuePair { | ||
pub const MAX_SQ_ENTRIES: u16 = (PAGE_SIZE / 64) as u16; // Maximum SQ size in entries. | ||
pub const MAX_CQ_ENTRIES: u16 = (PAGE_SIZE / 16) as u16; // Maximum CQ size in entries. | ||
const SQ_SIZE: usize = PAGE_SIZE; // Submission Queue size in bytes. | ||
const CQ_SIZE: usize = PAGE_SIZE; // Completion Queue size in bytes. | ||
const PER_QUEUE_PAGES: usize = 128; | ||
/// Maximum SQ size in entries. | ||
pub const MAX_SQ_ENTRIES: u16 = (PAGE_SIZE / 64) as u16; | ||
/// Maximum CQ size in entries. | ||
pub const MAX_CQ_ENTRIES: u16 = (PAGE_SIZE / 16) as u16; | ||
/// Submission Queue size in bytes. | ||
const SQ_SIZE: usize = PAGE_SIZE; | ||
/// Completion Queue size in bytes. | ||
const CQ_SIZE: usize = PAGE_SIZE; | ||
/// Number of pages per queue if bounce buffering. | ||
const PER_QUEUE_PAGES_BOUNCE_BUFFER: usize = 128; | ||
/// Number of pages per queue if not bounce buffering. | ||
const PER_QUEUE_PAGES_NO_BOUNCE_BUFFER: usize = 64; | ||
|
||
pub fn new( | ||
spawner: impl SpawnDriver, | ||
|
@@ -180,9 +187,15 @@ impl QueuePair { | |
cq_entries: u16, // Requested CQ size in entries. | ||
interrupt: DeviceInterrupt, | ||
registers: Arc<DeviceRegisters<impl DeviceBacking>>, | ||
bounce_buffer: bool, | ||
) -> anyhow::Result<Self> { | ||
let total_size = | ||
QueuePair::SQ_SIZE + QueuePair::CQ_SIZE + QueuePair::PER_QUEUE_PAGES * PAGE_SIZE; | ||
let total_size = QueuePair::SQ_SIZE | ||
+ QueuePair::CQ_SIZE | ||
+ if bounce_buffer { | ||
QueuePair::PER_QUEUE_PAGES_BOUNCE_BUFFER * PAGE_SIZE | ||
} else { | ||
QueuePair::PER_QUEUE_PAGES_NO_BOUNCE_BUFFER * PAGE_SIZE | ||
}; | ||
let dma_client = device.dma_client(); | ||
let mem = dma_client | ||
.allocate_dma_buffer(total_size) | ||
|
@@ -192,7 +205,15 @@ impl QueuePair { | |
assert!(cq_entries <= Self::MAX_CQ_ENTRIES); | ||
|
||
QueuePair::new_or_restore( | ||
spawner, qid, sq_entries, cq_entries, interrupt, registers, mem, None, | ||
spawner, | ||
qid, | ||
sq_entries, | ||
cq_entries, | ||
interrupt, | ||
registers, | ||
mem, | ||
None, | ||
bounce_buffer, | ||
) | ||
} | ||
|
||
|
@@ -206,6 +227,7 @@ impl QueuePair { | |
registers: Arc<DeviceRegisters<impl DeviceBacking>>, | ||
mem: MemoryBlock, | ||
saved_state: Option<&QueueHandlerSavedState>, | ||
bounce_buffer: bool, | ||
) -> anyhow::Result<Self> { | ||
// MemoryBlock is either allocated or restored prior calling here. | ||
let sq_mem_block = mem.subblock(0, QueuePair::SQ_SIZE); | ||
|
@@ -240,12 +262,25 @@ impl QueuePair { | |
}); | ||
|
||
// Page allocator uses remaining part of the buffer for dynamic allocation. | ||
const _: () = assert!( | ||
QueuePair::PER_QUEUE_PAGES * PAGE_SIZE >= 128 * 1024 + PAGE_SIZE, | ||
"not enough room for an ATAPI IO plus a PRP list" | ||
); | ||
let alloc: PageAllocator = | ||
PageAllocator::new(mem.subblock(data_offset, QueuePair::PER_QUEUE_PAGES * PAGE_SIZE)); | ||
let alloc = if bounce_buffer { | ||
const _: () = assert!( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we package this up as a local const fn? E.g., const fn pages_to_size(pages: usize) -> usize {
let size = pages * PAGE_SIZE;
assert!(size >= ...);
size
}
// Note: using const {} to ensure the internal assertion checks are at compile time
let alloc_len = if bounce_buffer {
const { pages_to_size(QueuePair::PER_QUEUE_PAGES_BOUNCE_BUFFER) }
} else {
const { pages_to_size(QueuePair::PER_QUEUE_PAGES_NO_BOUNCE_BUFFER) }
}; There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. neat, can do |
||
QueuePair::PER_QUEUE_PAGES_BOUNCE_BUFFER * PAGE_SIZE >= 128 * 1024 + PAGE_SIZE, | ||
"not enough room for an ATAPI IO plus a PRP list" | ||
); | ||
PageAllocator::new(mem.subblock( | ||
data_offset, | ||
QueuePair::PER_QUEUE_PAGES_BOUNCE_BUFFER * PAGE_SIZE, | ||
)) | ||
} else { | ||
const _: () = assert!( | ||
QueuePair::PER_QUEUE_PAGES_NO_BOUNCE_BUFFER * PAGE_SIZE >= 128 * 1024 + PAGE_SIZE, | ||
"not enough room for an ATAPI IO plus a PRP list" | ||
); | ||
PageAllocator::new(mem.subblock( | ||
data_offset, | ||
QueuePair::PER_QUEUE_PAGES_NO_BOUNCE_BUFFER * PAGE_SIZE, | ||
)) | ||
}; | ||
|
||
Ok(Self { | ||
task, | ||
|
@@ -302,6 +337,7 @@ impl QueuePair { | |
registers: Arc<DeviceRegisters<impl DeviceBacking>>, | ||
mem: MemoryBlock, | ||
saved_state: &QueuePairSavedState, | ||
bounce_buffer: bool, | ||
) -> anyhow::Result<Self> { | ||
let QueuePairSavedState { | ||
mem_len: _, // Used to restore DMA buffer before calling this. | ||
|
@@ -321,6 +357,7 @@ impl QueuePair { | |
registers, | ||
mem, | ||
Some(handler_data), | ||
bounce_buffer, | ||
) | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -78,7 +78,7 @@ async fn test_nvme_ioqueue_max_mqes(driver: DefaultDriver) { | |
let cap: Cap = Cap::new().with_mqes_z(max_u16); | ||
device.set_mock_response_u64(Some((0, cap.into()))); | ||
|
||
let driver = NvmeDriver::new(&driver_source, CPU_COUNT, device).await; | ||
let driver = NvmeDriver::new(&driver_source, CPU_COUNT, device, false).await; | ||
assert!(driver.is_ok()); | ||
} | ||
|
||
|
@@ -113,7 +113,7 @@ async fn test_nvme_ioqueue_invalid_mqes(driver: DefaultDriver) { | |
// Setup mock response at offset 0 | ||
let cap: Cap = Cap::new().with_mqes_z(0); | ||
device.set_mock_response_u64(Some((0, cap.into()))); | ||
let driver = NvmeDriver::new(&driver_source, CPU_COUNT, device).await; | ||
let driver = NvmeDriver::new(&driver_source, CPU_COUNT, device, false).await; | ||
|
||
assert!(driver.is_err()); | ||
} | ||
|
@@ -150,7 +150,7 @@ async fn test_nvme_driver(driver: DefaultDriver, allow_dma: bool) { | |
.await | ||
.unwrap(); | ||
let device = NvmeTestEmulatedDevice::new(nvme, msi_set, dma_client.clone()); | ||
let driver = NvmeDriver::new(&driver_source, CPU_COUNT, device) | ||
let driver = NvmeDriver::new(&driver_source, CPU_COUNT, device, false) | ||
.await | ||
.unwrap(); | ||
let namespace = driver.namespace(1).await.unwrap(); | ||
|
@@ -266,7 +266,7 @@ async fn test_nvme_save_restore_inner(driver: DefaultDriver) { | |
.unwrap(); | ||
|
||
let device = NvmeTestEmulatedDevice::new(nvme_ctrl, msi_x, dma_client.clone()); | ||
let mut nvme_driver = NvmeDriver::new(&driver_source, CPU_COUNT, device) | ||
let mut nvme_driver = NvmeDriver::new(&driver_source, CPU_COUNT, device, false) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TODO: add a test case with |
||
.await | ||
.unwrap(); | ||
let _ns1 = nvme_driver.namespace(1).await.unwrap(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feels like this expression can be shortened? E.g. use one PageAllocator::new after checking all asserts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This assert is probably not needed at all?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm the assert is checking the minimum size is large enough for the given commands right? I don't know enough to say if the assert should be removed or not, but it still seems useful?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, what I meant is - we are asserting if one constant is greater than another constant. Can be done statically outside of this function, I think. But okay, leave it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a static assert, since it's a const evaluation at compile time. A bit confusing, but it doesn't require taking a dependency on
static_assert
which i think does the same thing under the hood.