mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-18 18:56:29 +00:00
block: extend generic driver API (Ref #750)
* allow to handle a maximum of packets in parallel that fits free slots in the ack queue * stop processing packets, when the driver can't handle more requests in parallel, and resume packet handling, when the driver is ready again
This commit is contained in:
parent
b10b9e20a2
commit
56a7d00a44
@ -45,7 +45,7 @@ class Storage_device : public Genode::List<Storage_device>::Element,
|
||||
if (verbose)
|
||||
PDBG("ACK packet for block: %llu status: %d", packet->block_number(), cmnd->result);
|
||||
|
||||
session->complete_packet(*packet);
|
||||
session->ack_packet(*packet);
|
||||
Genode::destroy(Genode::env()->heap(), packet);
|
||||
_scsi_free_command(cmnd);
|
||||
}
|
||||
@ -129,7 +129,7 @@ class Storage_device : public Genode::List<Storage_device>::Element,
|
||||
|
||||
/* send command to host driver */
|
||||
if (_sdev->host->hostt->queuecommand(_sdev->host, cmnd)) {
|
||||
throw Io_error();
|
||||
throw Request_congestion();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,7 @@ class Driver : public Block::Driver
|
||||
{
|
||||
_http.cmd_get(block_nr * _block_size, block_count * _block_size,
|
||||
(addr_t)buffer);
|
||||
session->complete_packet(packet);
|
||||
session->ack_packet(packet);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -30,21 +30,6 @@ namespace Block {
|
||||
|
||||
class Block::Session_component : public Block::Session_rpc_object
|
||||
{
|
||||
public:
|
||||
|
||||
void complete_packet(Packet_descriptor &packet, bool success = true)
|
||||
{
|
||||
packet.succeeded(success);
|
||||
|
||||
/* acknowledge packet to the client */
|
||||
if (!tx_sink()->ready_to_ack()) {
|
||||
PWRN("need to wait until ready-for-ack");
|
||||
return;
|
||||
}
|
||||
|
||||
tx_sink()->acknowledge_packet(packet);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Driver_factory &_driver_factory;
|
||||
@ -53,80 +38,127 @@ class Block::Session_component : public Block::Session_rpc_object
|
||||
addr_t _rq_phys;
|
||||
Signal_dispatcher<Session_component> _sink_ack;
|
||||
Signal_dispatcher<Session_component> _sink_submit;
|
||||
bool _req_queue_full;
|
||||
Packet_descriptor _p_to_handle;
|
||||
unsigned _p_in_fly;
|
||||
|
||||
void _ready_to_submit(unsigned)
|
||||
/**
|
||||
* Acknowledge a packet already handled
|
||||
*/
|
||||
inline void _ack_packet(Packet_descriptor &packet)
|
||||
{
|
||||
/* handle requests */
|
||||
while (tx_sink()->packet_avail()) {
|
||||
if (!tx_sink()->ready_to_ack())
|
||||
PERR("Not ready to ack!");
|
||||
|
||||
/* blocking-get packet from client */
|
||||
Packet_descriptor packet = tx_sink()->get_packet();
|
||||
if (!packet.valid()) {
|
||||
PWRN("received invalid packet");
|
||||
continue;
|
||||
}
|
||||
|
||||
packet.succeeded(true);
|
||||
|
||||
try {
|
||||
switch (packet.operation()) {
|
||||
|
||||
case Block::Packet_descriptor::READ:
|
||||
if (_driver.dma_enabled())
|
||||
_driver.read_dma(packet.block_number(),
|
||||
packet.block_count(),
|
||||
_rq_phys + packet.offset(),
|
||||
packet);
|
||||
else
|
||||
_driver.read(packet.block_number(),
|
||||
packet.block_count(),
|
||||
tx_sink()->packet_content(packet),
|
||||
packet);
|
||||
break;
|
||||
|
||||
case Block::Packet_descriptor::WRITE:
|
||||
if (_driver.dma_enabled())
|
||||
_driver.write_dma(packet.block_number(),
|
||||
packet.block_count(),
|
||||
_rq_phys + packet.offset(),
|
||||
packet);
|
||||
else
|
||||
_driver.write(packet.block_number(),
|
||||
packet.block_count(),
|
||||
tx_sink()->packet_content(packet),
|
||||
packet);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Driver::Io_error();
|
||||
}
|
||||
} catch (Driver::Io_error) {
|
||||
complete_packet(packet, false);
|
||||
tx_sink()->acknowledge_packet(packet);
|
||||
_p_in_fly--;
|
||||
}
|
||||
|
||||
/**
|
||||
* Range check packet request
|
||||
*/
|
||||
inline bool _range_check(Packet_descriptor &p) {
|
||||
return p.block_number() + p.block_count() - 1
|
||||
< _driver.block_count(); }
|
||||
|
||||
/**
|
||||
* Handle a single request
|
||||
*/
|
||||
void _handle_packet(Packet_descriptor packet)
|
||||
{
|
||||
_p_to_handle = packet;
|
||||
_p_to_handle.succeeded(false);
|
||||
|
||||
/* ignore invalid packets */
|
||||
if (!packet.valid() || !_range_check(_p_to_handle)) {
|
||||
_ack_packet(_p_to_handle);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
switch (_p_to_handle.operation()) {
|
||||
|
||||
case Block::Packet_descriptor::READ:
|
||||
if (_driver.dma_enabled())
|
||||
_driver.read_dma(packet.block_number(),
|
||||
packet.block_count(),
|
||||
_rq_phys + packet.offset(),
|
||||
_p_to_handle);
|
||||
else
|
||||
_driver.read(packet.block_number(),
|
||||
packet.block_count(),
|
||||
tx_sink()->packet_content(packet),
|
||||
_p_to_handle);
|
||||
break;
|
||||
|
||||
case Block::Packet_descriptor::WRITE:
|
||||
if (_driver.dma_enabled())
|
||||
_driver.write_dma(packet.block_number(),
|
||||
packet.block_count(),
|
||||
_rq_phys + packet.offset(),
|
||||
_p_to_handle);
|
||||
else
|
||||
_driver.write(packet.block_number(),
|
||||
packet.block_count(),
|
||||
tx_sink()->packet_content(packet),
|
||||
_p_to_handle);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Driver::Io_error();
|
||||
}
|
||||
} catch (Driver::Request_congestion) {
|
||||
_req_queue_full = true;
|
||||
} catch (Driver::Io_error) {
|
||||
_ack_packet(_p_to_handle);
|
||||
}
|
||||
}
|
||||
|
||||
void _ack_avail(unsigned) { }
|
||||
/**
|
||||
* Triggered when a packet was placed into the empty submit queue
|
||||
*/
|
||||
void _packet_avail(unsigned)
|
||||
{
|
||||
/*
|
||||
* as long as more packets are available, and we're able to ack
|
||||
* them, and the driver's request queue isn't full,
|
||||
* direct the packet request to the driver backend
|
||||
*/
|
||||
for (; !_req_queue_full && tx_sink()->packet_avail() &&
|
||||
!(_p_in_fly >= tx_sink()->ack_slots_free()); _p_in_fly++)
|
||||
_handle_packet(tx_sink()->get_packet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered when an ack got removed from the full ack queue
|
||||
*/
|
||||
void _ready_to_ack(unsigned) { _packet_avail(0); }
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param rq_ds shared dataspace for packet stream
|
||||
* \param driver block driver backend
|
||||
* \param driver_factory factory to create and destroy driver objects
|
||||
* \param ep entrypoint handling this session component
|
||||
* \param receiver signal receiver managing signals of the client
|
||||
*/
|
||||
Session_component(Ram_dataspace_capability rq_ds,
|
||||
Driver &driver,
|
||||
Driver_factory &driver_factory,
|
||||
Rpc_entrypoint &ep,
|
||||
Signal_receiver &receiver)
|
||||
:
|
||||
Session_rpc_object(rq_ds, ep),
|
||||
_driver_factory(driver_factory),
|
||||
_driver(driver),
|
||||
_rq_ds(rq_ds),
|
||||
_rq_phys(Dataspace_client(_rq_ds).phys_addr()),
|
||||
_sink_ack(receiver, *this, &Session_component::_ack_avail),
|
||||
_sink_submit(receiver, *this,
|
||||
&Session_component::_ready_to_submit)
|
||||
: Session_rpc_object(rq_ds, ep),
|
||||
_driver_factory(driver_factory),
|
||||
_driver(driver),
|
||||
_rq_ds(rq_ds),
|
||||
_rq_phys(Dataspace_client(_rq_ds).phys_addr()),
|
||||
_sink_ack(receiver, *this, &Session_component::_ready_to_ack),
|
||||
_sink_submit(receiver, *this, &Session_component::_packet_avail),
|
||||
_req_queue_full(false),
|
||||
_p_in_fly(0)
|
||||
{
|
||||
_tx.sigh_ready_to_ack(_sink_ack);
|
||||
_tx.sigh_packet_avail(_sink_submit);
|
||||
@ -135,11 +167,34 @@ class Block::Session_component : public Block::Session_rpc_object
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
* Acknowledges a packet processed by the driver to the client
|
||||
*
|
||||
* \param packet the packet to acknowledge
|
||||
* \param success indicated whether the processing was successful
|
||||
*
|
||||
* \throw Ack_congestion
|
||||
*/
|
||||
~Session_component()
|
||||
void ack_packet(Packet_descriptor &packet, bool success = true)
|
||||
{
|
||||
_driver_factory.destroy(&_driver);
|
||||
bool ack_queue_full = _p_in_fly >= tx_sink()->ack_slots_free();
|
||||
|
||||
packet.succeeded(success);
|
||||
_ack_packet(packet);
|
||||
|
||||
if (!_req_queue_full && !ack_queue_full)
|
||||
return;
|
||||
|
||||
/*
|
||||
* when the driver's request queue was full,
|
||||
* handle last unprocessed packet taken out of submit queue
|
||||
*/
|
||||
if (_req_queue_full) {
|
||||
_req_queue_full = false;
|
||||
_handle_packet(_p_to_handle);
|
||||
}
|
||||
|
||||
/* resume packet processing */
|
||||
_packet_avail(0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -217,6 +272,14 @@ class Block::Root :
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param session_ep entrypoint handling this root component
|
||||
* \param md_alloc allocator to allocate session components
|
||||
* \param driver_factory factory to create and destroy driver backend
|
||||
* \param receiver signal receiver managing signals of the client
|
||||
*/
|
||||
Root(Rpc_entrypoint *session_ep, Allocator *md_alloc,
|
||||
Driver_factory &driver_factory, Signal_receiver &receiver)
|
||||
:
|
||||
|
@ -35,17 +35,19 @@ namespace Block {
|
||||
*/
|
||||
struct Block::Driver
|
||||
{
|
||||
Session_component *session;
|
||||
Session_component * session; /* single session component of the driver
|
||||
* might get used to acknowledge requests */
|
||||
|
||||
/**
|
||||
* Exceptions
|
||||
*/
|
||||
class Io_error : public ::Genode::Exception { };
|
||||
class Io_error : public ::Genode::Exception { };
|
||||
class Request_congestion : public ::Genode::Exception { };
|
||||
|
||||
/**
|
||||
* Request block size for driver and medium
|
||||
*/
|
||||
virtual Genode::size_t block_size() = 0;
|
||||
virtual Genode::size_t block_size() = 0;
|
||||
|
||||
/**
|
||||
* Request capacity of medium in blocks
|
||||
@ -63,6 +65,11 @@ struct Block::Driver
|
||||
* \param block_number number of first block to read
|
||||
* \param block_count number of blocks to read
|
||||
* \param buffer output buffer for read request
|
||||
* \param packet packet descriptor from the client
|
||||
*
|
||||
* \throw Request_congestion
|
||||
*
|
||||
* Note: should be overridden by DMA non-capable devices
|
||||
*/
|
||||
virtual void read(sector_t block_number,
|
||||
Genode::size_t block_count,
|
||||
@ -76,6 +83,11 @@ struct Block::Driver
|
||||
* \param block_number number of first block to write
|
||||
* \param block_count number of blocks to write
|
||||
* \param buffer buffer for write request
|
||||
* \param packet packet descriptor from the client
|
||||
*
|
||||
* \throw Request_congestion
|
||||
*
|
||||
* Note: should be overridden by DMA non-capable, non-ROM devices
|
||||
*/
|
||||
virtual void write(sector_t block_number,
|
||||
Genode::size_t block_count,
|
||||
@ -89,6 +101,11 @@ struct Block::Driver
|
||||
* \param block_number number of first block to read
|
||||
* \param block_count number of blocks to read
|
||||
* \param phys phyiscal address of read buffer
|
||||
* \param packet packet descriptor from the client
|
||||
*
|
||||
* \throw Request_congestion
|
||||
*
|
||||
* Note: should be overridden by DMA capable devices
|
||||
*/
|
||||
virtual void read_dma(sector_t block_number,
|
||||
Genode::size_t block_count,
|
||||
@ -102,6 +119,11 @@ struct Block::Driver
|
||||
* \param block_number number of first block to write
|
||||
* \param block_count number of blocks to write
|
||||
* \param phys physical address of write buffer
|
||||
* \param packet packet descriptor from the client
|
||||
*
|
||||
* \throw Request_congestion
|
||||
*
|
||||
* Note: should be overridden by DMA capable, non-ROM devices
|
||||
*/
|
||||
virtual void write_dma(sector_t block_number,
|
||||
Genode::size_t block_count,
|
||||
@ -113,6 +135,8 @@ struct Block::Driver
|
||||
* Check if DMA is enabled for driver
|
||||
*
|
||||
* \return true if DMA is enabled, false otherwise
|
||||
*
|
||||
* Note: has to be overriden by DMA-capable devices
|
||||
*/
|
||||
virtual bool dma_enabled() { return false; }
|
||||
|
||||
@ -124,7 +148,10 @@ struct Block::Driver
|
||||
return Genode::env()->ram_session()->alloc(size, false); }
|
||||
|
||||
/**
|
||||
* Synchronize with with device.
|
||||
* Synchronize with device.
|
||||
*
|
||||
* Note: should be overriden by (e.g. intermediate) components,
|
||||
* which cache data
|
||||
*/
|
||||
virtual void sync() {}
|
||||
};
|
||||
|
@ -131,8 +131,8 @@ class Packet_descriptor_queue
|
||||
{
|
||||
private:
|
||||
|
||||
int _head;
|
||||
int _tail;
|
||||
unsigned _head;
|
||||
unsigned _tail;
|
||||
PACKET_DESCRIPTOR _queue[QUEUE_SIZE];
|
||||
|
||||
public:
|
||||
@ -200,10 +200,18 @@ class Packet_descriptor_queue
|
||||
*/
|
||||
bool single_element() { return (_tail + 1)%QUEUE_SIZE == _head; }
|
||||
|
||||
|
||||
/**
|
||||
* Return true if a single slot is left to be put into the queue
|
||||
*/
|
||||
bool single_slot_free() { return (_head + 2)%QUEUE_SIZE == _tail; }
|
||||
|
||||
/**
|
||||
* Return number of slots left to be put into the queue
|
||||
*/
|
||||
unsigned slots_free() {
|
||||
return ((_tail > _head) ? _tail - _head
|
||||
: QUEUE_SIZE - _head + _tail) - 1; }
|
||||
};
|
||||
|
||||
|
||||
@ -280,6 +288,11 @@ class Packet_descriptor_transmitter
|
||||
if (_tx_queue->single_element())
|
||||
_rx_ready.submit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return number of slots left to be put into the tx queue
|
||||
*/
|
||||
unsigned tx_slots_free() { return _tx_queue->slots_free(); }
|
||||
};
|
||||
|
||||
|
||||
@ -775,6 +788,12 @@ class Packet_stream_sink : private Packet_stream_base
|
||||
*/
|
||||
bool ready_to_ack() { return _ack_transmitter.ready_for_tx(); }
|
||||
|
||||
/**
|
||||
* Returns number of slots left in the the ack queue
|
||||
*/
|
||||
unsigned ack_slots_free() {
|
||||
return _ack_transmitter.tx_slots_free(); }
|
||||
|
||||
/**
|
||||
* Tell the source that the processing of the specified packet is completed
|
||||
*
|
||||
|
@ -61,7 +61,7 @@ class Ahci_driver : public Block::Driver
|
||||
{
|
||||
if (_ncq_command(block_nr, block_cnt, phys, 0))
|
||||
throw Io_error();
|
||||
session->complete_packet(packet);
|
||||
session->ack_packet(packet);
|
||||
}
|
||||
|
||||
void write_dma(Block::sector_t block_nr, size_t block_cnt, addr_t phys,
|
||||
@ -69,7 +69,7 @@ class Ahci_driver : public Block::Driver
|
||||
{
|
||||
if (_ncq_command(block_nr, block_cnt, phys, 1))
|
||||
throw Io_error();
|
||||
session->complete_packet(packet);
|
||||
session->ack_packet(packet);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -66,7 +66,7 @@ class Ahci_driver_base : public Block::Driver
|
||||
{
|
||||
_sanity_check(block_number, block_count);
|
||||
_device->read(block_number, block_count, phys);
|
||||
if (session) session->complete_packet(packet);
|
||||
if (session) session->ack_packet(packet);
|
||||
}
|
||||
|
||||
void write_dma(Block::sector_t block_number,
|
||||
@ -76,7 +76,7 @@ class Ahci_driver_base : public Block::Driver
|
||||
{
|
||||
_sanity_check(block_number, block_count);
|
||||
_device->write(block_number, block_count, phys);
|
||||
if (session) session->complete_packet(packet);
|
||||
if (session) session->ack_packet(packet);
|
||||
}
|
||||
|
||||
Ram_dataspace_capability alloc_dma_buffer(size_t size) {
|
||||
|
@ -124,7 +124,7 @@ namespace Ata {
|
||||
Block::Packet_descriptor &packet)
|
||||
{
|
||||
_read(block_number, block_count, buffer, false);
|
||||
session->complete_packet(packet);
|
||||
session->ack_packet(packet);
|
||||
}
|
||||
|
||||
void write(Block::sector_t block_number,
|
||||
@ -133,7 +133,7 @@ namespace Ata {
|
||||
Block::Packet_descriptor &packet)
|
||||
{
|
||||
_write(block_number, block_count, buffer, false);
|
||||
session->complete_packet(packet);
|
||||
session->ack_packet(packet);
|
||||
}
|
||||
|
||||
void read_dma(Block::sector_t block_number,
|
||||
@ -142,7 +142,7 @@ namespace Ata {
|
||||
Block::Packet_descriptor &packet)
|
||||
{
|
||||
_read(block_number, block_count, (char*)phys, true);
|
||||
session->complete_packet(packet);
|
||||
session->ack_packet(packet);
|
||||
}
|
||||
|
||||
void write_dma(Block::sector_t block_number,
|
||||
@ -151,7 +151,7 @@ namespace Ata {
|
||||
Block::Packet_descriptor &packet)
|
||||
{
|
||||
_write(block_number, block_count, (char*)phys, true);
|
||||
session->complete_packet(packet);
|
||||
session->ack_packet(packet);
|
||||
}
|
||||
|
||||
bool dma_enabled() { return _dma; }
|
||||
|
@ -104,7 +104,7 @@ class Block::Exynos5_driver : public Block::Driver
|
||||
{
|
||||
if (!_controller.read_blocks_dma(block_number, block_count, phys))
|
||||
throw Io_error();
|
||||
session->complete_packet(packet);
|
||||
session->ack_packet(packet);
|
||||
}
|
||||
|
||||
void write_dma(Block::sector_t block_number,
|
||||
@ -114,7 +114,7 @@ class Block::Exynos5_driver : public Block::Driver
|
||||
{
|
||||
if (!_controller.write_blocks_dma(block_number, block_count, phys))
|
||||
throw Io_error();
|
||||
session->complete_packet(packet);
|
||||
session->ack_packet(packet);
|
||||
}
|
||||
|
||||
bool dma_enabled() { return _use_dma; }
|
||||
|
@ -97,7 +97,7 @@ class Block::Omap4_driver : public Block::Driver
|
||||
{
|
||||
if (!_controller.read_blocks(block_number, block_count, out_buffer))
|
||||
throw Io_error();
|
||||
session->complete_packet(packet);
|
||||
session->ack_packet(packet);
|
||||
}
|
||||
|
||||
void write(Block::sector_t block_number,
|
||||
@ -107,7 +107,7 @@ class Block::Omap4_driver : public Block::Driver
|
||||
{
|
||||
if (!_controller.write_blocks(block_number, block_count, buffer))
|
||||
throw Io_error();
|
||||
session->complete_packet(packet);
|
||||
session->ack_packet(packet);
|
||||
}
|
||||
|
||||
void read_dma(Block::sector_t block_number,
|
||||
@ -117,7 +117,7 @@ class Block::Omap4_driver : public Block::Driver
|
||||
{
|
||||
if (!_controller.read_blocks_dma(block_number, block_count, phys))
|
||||
throw Io_error();
|
||||
session->complete_packet(packet);
|
||||
session->ack_packet(packet);
|
||||
}
|
||||
|
||||
void write_dma(Block::sector_t block_number,
|
||||
@ -127,7 +127,7 @@ class Block::Omap4_driver : public Block::Driver
|
||||
{
|
||||
if (!_controller.write_blocks_dma(block_number, block_count, phys))
|
||||
throw Io_error();
|
||||
session->complete_packet(packet);
|
||||
session->ack_packet(packet);
|
||||
}
|
||||
|
||||
bool dma_enabled() { return _use_dma; }
|
||||
|
@ -107,7 +107,7 @@ class Sd_card : public Block::Driver
|
||||
length, &resp);
|
||||
_hd.read_data(length, out_buffer + (i * BLOCK_SIZE));
|
||||
}
|
||||
session->complete_packet(packet);
|
||||
session->ack_packet(packet);
|
||||
}
|
||||
|
||||
void write(Block::sector_t block_number,
|
||||
@ -129,7 +129,7 @@ class Sd_card : public Block::Driver
|
||||
length, &resp);
|
||||
_hd.write_data(length, buffer + (i * BLOCK_SIZE));
|
||||
}
|
||||
session->complete_packet(packet);
|
||||
session->ack_packet(packet);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -76,7 +76,7 @@ class Rom_blk : public Block::Driver
|
||||
/* copy file content to packet payload */
|
||||
memcpy((void*)buffer, (void*)(_file_addr + offset), size);
|
||||
|
||||
session->complete_packet(packet);
|
||||
session->ack_packet(packet);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -74,7 +74,7 @@ class Driver : public Block::Driver
|
||||
Genode::size_t size = block_count * BLOCK_SIZE;
|
||||
|
||||
Genode::memcpy((void*)buffer, (void*)(_fb_addr + offset), size);
|
||||
session->complete_packet(packet);
|
||||
session->ack_packet(packet);
|
||||
}
|
||||
|
||||
void write(Block::sector_t block_number,
|
||||
@ -94,7 +94,7 @@ class Driver : public Block::Driver
|
||||
|
||||
Genode::memcpy((void*)(_fb_addr + offset), (void*)buffer, size);
|
||||
_fb.refresh(0, 0, _fb_mode.width(), _fb_mode.height());
|
||||
session->complete_packet(packet);
|
||||
session->ack_packet(packet);
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user