os: use Request_stream API in NVMe driver

As a result of the API change the memory handling could be simplified.
Since the Block session dataspace is now directly used for DMA, we
actually only have to provide the memory for setting up PRP lists for
large requests (for the moment more than 8 KiB of data).

As we limit the maximum data transfer length to 2 MiB, we get by with
just a page per request. Those memory is allocated beforehand for the
maximum number of I/O requests, which got bumbed to 512 entries. Since
not all NVMe controllers support such large a maximum data transfer
length and this many entries, especially older ones, the values are
capped according to the properties of the controller during
initialization. (The memory demands of the component are around 3 MiB
due to setting up for the common case, even if a particular controller
is only able to make use of less.)

(Although there are controllers whose maximum memory page size is more
than 4K, the driver is hardcoded to solely use 4K pages.)

In addition to those changes, the driver now supports the 'SYNC' and
'TRIM' operations of the Block session by using the NVMe 'FLUSH' and
'WRITE_ZEROS' commands.

Fixes #3702.
This commit is contained in:
Josef Söntgen 2020-03-04 22:12:20 +01:00 committed by Christian Helmuth
parent e63c5e6c69
commit 150d143755
3 changed files with 692 additions and 484 deletions

View File

@ -29,6 +29,20 @@ set small_test [expr $is_qemu || [have_spec foc] || [have_spec sel4]]
#
set dd [installed_command dd]
#
# Query writeable for policy
#
proc writeable { } {
global test_write
if {$test_write} {
return yes
} else {
return no
}
}
#
# Build
#
@ -84,10 +98,10 @@ append_platform_drv_config
append config {
<start name="nvme_drv">
<resource name="RAM" quantum="16M"/>
<resource name="RAM" quantum="4M"/>
<provides> <service name="Block"/> </provides>
<config>
<policy label_prefix="block_tester" writeable="no"/>
<policy label_prefix="block_tester" writeable="} [writeable] {"/>
</config>
</start>
@ -138,6 +152,7 @@ append_if $test_write config {
<request type="write" lba="2048" count="1"/>
<request type="write" lba="5696" count="1"/>
<request type="write" lba="5696" count="1"/>
<request type="sync" lba="0" count="1"/>
<request type="write" lba="5696" count="1"/>
<request type="read" lba="4096" count="1"/>
<request type="read" lba="61440" count="16"/>

File diff suppressed because it is too large Load Diff

View File

@ -30,106 +30,6 @@ namespace Util {
virtual void free(Genode::Ram_dataspace_capability) = 0;
};
/*
* Wrap Bit_array into a convinient Bitmap allocator
*/
template <unsigned BITS>
struct Bitmap
{
struct Full : Genode::Exception { };
static constexpr addr_t INVALID { BITS - 1 };
Genode::Bit_array<BITS> _array { };
size_t _used { 0 };
addr_t _find_free(size_t const bits)
{
for (size_t i = 0; i < BITS; i += bits) {
if (_array.get(i, bits)) { continue; }
return i;
}
throw Full();
}
/**
* Return index from where given number of bits was allocated
*
* \param bits number of bits to allocate
*
* \return index of start bit
*/
addr_t alloc(size_t const bits)
{
addr_t const start = _find_free(bits);
_array.set(start, bits);
_used += bits;
return start;
}
/**
* Free given number of bits from start index
*
* \param start index of the start bit
* \param bits number of bits to free
*/
void free(addr_t const start, size_t const bits)
{
_used -= bits;
_array.clear(start, bits);
}
};
/*
* Wrap array into convinient interface
*
* The used datatype T must implement the following methods:
*
* bool valid() const returns true if the object is valid
* void invalidate() adjusts the object so that valid() returns false
*/
template <typename T, size_t CAP>
struct Slots
{
T _entries[CAP] { };
/**
* Lookup slot
*/
template <typename FUNC>
T *lookup(FUNC const &func)
{
for (size_t i = 0; i < CAP; i++) {
if (!_entries[i].valid()) { continue; }
if ( func(_entries[i])) { return &_entries[i]; }
}
return nullptr;
}
/**
* Get free slot
*/
T *get()
{
for (size_t i = 0; i < CAP; i++) {
if (!_entries[i].valid()) { return &_entries[i]; }
}
return nullptr;
}
/**
* Iterate over all slots until FUNC returns true
*/
template <typename FUNC>
bool for_each(FUNC const &func)
{
for (size_t i = 0; i < CAP; i++) {
if (!_entries[i].valid()) { continue; }
if ( func(_entries[i])) { return true; }
}
return false;
}
};
/**
* Extract string from memory
*