usb_block: switch to Request_stream API

- remove old 'Driver' interface
- implement Request stream front end
- fix some namespacing

issue #3661
This commit is contained in:
Sebastian Sumpf 2020-02-21 08:37:08 +01:00 committed by Christian Helmuth
parent 96c93dae49
commit e97524ba7d
1 changed files with 256 additions and 151 deletions

View File

@ -1,11 +1,12 @@
/*
* \brief Usb session to Block session translator
* \author Josef Soentgen
* \author Sebastian Sumpf
* \date 2016-02-08
*/
/*
* Copyright (C) 2016-2017 Genode Labs GmbH
* Copyright (C) 2016-2020 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@ -13,14 +14,13 @@
/* Genode includes */
#include <base/allocator_avl.h>
#include <base/attached_ram_dataspace.h>
#include <base/attached_rom_dataspace.h>
#include <base/component.h>
#include <base/log.h>
#include <base/heap.h>
#include <base/sleep.h>
#include <block/component.h>
#include <block/driver.h>
#include <block_session/connection.h>
#include <block/request_stream.h>
#include <os/reporter.h>
#include <timer_session/connection.h>
#include <usb/usb.h>
@ -34,8 +34,11 @@ static bool verbose_scsi = false;
namespace Usb {
using namespace Genode;
using namespace Block;
using Response = Block::Request_stream::Response;
struct Block_driver;
struct Block_session_component;
struct Main;
}
@ -44,8 +47,7 @@ namespace Usb {
** USB Mass Storage (BBB) Block::Driver implementation **
*********************************************************/
struct Usb::Block_driver : Usb::Completion,
Block::Driver
struct Usb::Block_driver : Usb::Completion
{
Env &env;
Entrypoint &ep;
@ -57,13 +59,34 @@ struct Usb::Block_driver : Usb::Completion,
*/
struct Block_request
{
Block::Packet_descriptor packet { };
Block::sector_t lba { 0 };
char *buffer { nullptr };
size_t size { 0 };
bool read { false };
bool pending { false };
} req { };
Request block_request;
addr_t address { 0 };
size_t size { 0 };
bool completed { false };
Block_request(Request &request, Request_stream::Payload const &payload)
: block_request(request)
{
payload.with_content(request, [&](void *addr, size_t sz) {
address = addr_t(addr);
size = sz;
});
}
bool read() const
{
return block_request.operation.type == Operation::Type::READ;
}
bool write() const
{
return block_request.operation.type == Operation::Type::WRITE;
}
};
Constructible<Block_request> request { };
bool request_pending() const { return request.constructed(); }
bool initialized = false;
bool device_plugged = false;
@ -74,21 +97,21 @@ struct Usb::Block_driver : Usb::Completion,
void handle_state_change()
{
if (!usb.plugged()) {
Genode::log("Device unplugged");
log("Device unplugged");
device_plugged = false;
return;
}
if (initialized) {
Genode::error("Device was already initialized");
error("Device was already initialized");
return;
}
Genode::log("Device plugged");
log("Device plugged");
if (!initialize()) {
env.parent().exit(-1);
Genode::sleep_forever();
sleep_forever();
return;
}
@ -107,23 +130,26 @@ struct Usb::Block_driver : Usb::Completion,
/*
* Read Usb session label from the configuration
*/
static char const *get_label(Xml_node node)
template <unsigned SIZE>
static Genode::String<SIZE> get_label(Xml_node node)
{
static Genode::String<256> usb_label;
Genode::String<SIZE> usb_label { "usb_storage" };
try {
node.attribute("label").value(usb_label);
return usb_label.string();
} catch (...) { }
return "usb_storage";
return usb_label;
}
/*
* USB session
*/
Allocator_avl alloc;
Usb::Connection usb { env, &alloc, get_label(config.xml()), 2 * (1<<20), state_change_dispatcher };
Usb::Device device;
Allocator_avl alloc;
Usb::Connection usb { env, &alloc,
get_label<128>(config.xml()).string(), 2 * (1<<20), state_change_dispatcher };
Usb::Device device;
Signal_handler<Main> &wake_up_handler;
/*
* Reporter
@ -188,13 +214,13 @@ struct Usb::Block_driver : Usb::Completion,
Usb::Interface iface = device.interface(interface);
if (p.type != Packet_descriptor::BULK) {
Genode::error("Can only handle BULK packets");
error("Can only handle BULK packets");
iface.release(p);
return;
}
if (!p.succeded) {
Genode::error("init complete error: packet not succeeded");
error("init complete error: packet not succeeded");
iface.release(p);
return;
}
@ -217,7 +243,7 @@ struct Usb::Block_driver : Usb::Completion,
if (verbose_scsi) r.dump();
if (!r.sbc()) {
Genode::warning("Device does not use SCSI Block Commands and may not work");
warning("Device does not use SCSI Block Commands and may not work");
}
r.get_id<Inquiry_response::Vid>(vendor, sizeof(vendor));
@ -265,7 +291,7 @@ struct Usb::Block_driver : Usb::Completion,
case NOT_READY_TO_READY_CHANGE: /* asq == 0x00 */
case POWER_ON_OR_RESET_OCCURRED: /* asq == 0x00 */
Genode::warning("Not ready - try again");
warning("Not ready - try again");
try_again = true;
break;
@ -294,7 +320,7 @@ struct Usb::Block_driver : Usb::Completion,
uint32_t const sig = csw.sig();
if (sig != Csw::SIG) {
Genode::error("CSW signature does not match: ",
error("CSW signature does not match: ",
Hex(sig, Hex::PREFIX, Hex::PAD));
break;
}
@ -302,7 +328,7 @@ struct Usb::Block_driver : Usb::Completion,
uint32_t const tag = csw.tag();
uint8_t const status = csw.sts();
if (status != Csw::PASSED) {
Genode::error("CSW failed: ", Hex(status, Hex::PREFIX, Hex::PAD),
error("CSW failed: ", Hex(status, Hex::PREFIX, Hex::PAD),
" tag: ", tag);
break;
}
@ -363,7 +389,7 @@ struct Usb::Block_driver : Usb::Completion,
Block::sector_t count, size_t size)
{
try {
Genode::Reporter::Xml_generator xml(reporter, [&] () {
Reporter::Xml_generator xml(reporter, [&] () {
xml.node("device", [&] () {
xml.attribute("vendor", vendor);
xml.attribute("product", product);
@ -372,7 +398,7 @@ struct Usb::Block_driver : Usb::Completion,
xml.attribute("writeable", _writeable);
});
});
} catch (...) { Genode::warning("Could not report block device"); }
} catch (...) { warning("Could not report block device"); }
}
/**
@ -392,10 +418,10 @@ struct Usb::Block_driver : Usb::Completion,
Usb::Interface &iface = device.interface(active_interface);
try { iface.claim(); }
catch (Usb::Session::Interface_already_claimed) {
Genode::error("Device already claimed");
error("Device already claimed");
return false;
} catch (Usb::Session::Interface_not_found) {
Genode::error("Interface not found");
error("Interface not found");
return false;
}
@ -410,7 +436,7 @@ struct Usb::Block_driver : Usb::Completion,
if (alt_iface.iclass != ICLASS_MASS_STORAGE
|| alt_iface.isubclass != ISUBCLASS_SCSI
|| alt_iface.iprotocol != IPROTO_BULK_ONLY) {
Genode::error("No mass storage SCSI bulk-only device");
error("No mass storage SCSI bulk-only device");
return false;
}
@ -437,7 +463,7 @@ struct Usb::Block_driver : Usb::Completion,
Usb::Packet_descriptor p = iface.alloc(0);
iface.control_transfer(p, 0x21, 0xff, 0, active_interface, 100);
if (!p.succeded) {
Genode::error("Could not reset device");
error("Could not reset device");
iface.release(p);
throw -1;
}
@ -475,7 +501,7 @@ struct Usb::Block_driver : Usb::Completion,
csw(init, true);
if (!init.inquiry) {
Genode::warning("Inquiry_cmd failed");
warning("Inquiry_cmd failed");
throw -1;
}
@ -502,7 +528,7 @@ struct Usb::Block_driver : Usb::Completion,
resp(Scsi::Request_sense_response::LENGTH, init, true);
csw(init, true);
if (!init.request_sense) {
Genode::warning("Request_sense failed");
warning("Request_sense failed");
throw -1;
}
@ -524,7 +550,7 @@ struct Usb::Block_driver : Usb::Completion,
timer.msleep(1000);
}
if (retries == MAX_RETRIES) {
Genode::warning("Test_unit_ready_cmd failed");
warning("Test_unit_ready_cmd failed");
throw -1;
}
}
@ -547,7 +573,7 @@ struct Usb::Block_driver : Usb::Completion,
csw(init, true);
if (!init.read_capacity) {
Genode::warning("Read_capacity_cmd failed");
warning("Read_capacity_cmd failed");
throw -1;
}
@ -563,7 +589,7 @@ struct Usb::Block_driver : Usb::Completion,
csw(init, true);
if (!init.read_capacity) {
Genode::warning("Read_capacity_cmd failed");
warning("Read_capacity_cmd failed");
throw -1;
}
@ -582,7 +608,7 @@ struct Usb::Block_driver : Usb::Completion,
device.manufactorer_string.to_char(vendor, sizeof(vendor));
device.product_string.to_char(product, sizeof(product));
Genode::log("Found USB device: ", (char const*)vendor, " (",
log("Found USB device: ", (char const*)vendor, " (",
(char const*)product, ") block size: ", _block_size,
" count: ", _block_count);
@ -592,10 +618,10 @@ struct Usb::Block_driver : Usb::Completion,
return true;
} catch (int) {
/* handle command failures */
Genode::error("Could not initialize storage device");
error("Could not initialize storage device");
} catch (...) {
/* handle Usb::Session failures */
Genode::error("Could not initialize storage device");
error("Could not initialize storage device");
}
return false;
}
@ -609,31 +635,23 @@ struct Usb::Block_driver : Usb::Completion,
bool execute_pending_request()
{
Usb::Interface &iface = device.interface(active_interface);
Usb::Endpoint ep = iface.endpoint(req.read ? ep_in : ep_out);
Usb::Packet_descriptor p = iface.alloc(req.size);
Usb::Endpoint ep = iface.endpoint(request->read() ? ep_in : ep_out);
Usb::Packet_descriptor p = iface.alloc(request->size);
if (!req.read) memcpy(iface.content(p), req.buffer, req.size);
if (request->write())
memcpy(iface.content(p), (void *)request->address, request->size);
iface.bulk_transfer(p, ep, false, this);
return true;
}
/**
* Acknowledge currently pending request
*
* After receiving the CSW ack the request at the Block session.
*/
void ack_pending_request(bool success = true)
void wakeup_client(bool success)
{
/*
* Needs to be reset bevor calling ack_packet to prevent getting a new
* request imediately and throwing Request_congestion() in io() again.
*/
req.pending = false;
request->block_request.success = success;
request->completed = true;
Block::Packet_descriptor p = req.packet;
ack_packet(p, success);
wake_up_handler.dispatch(0);
}
/**
@ -648,18 +666,19 @@ struct Usb::Block_driver : Usb::Completion,
Usb::Interface iface = device.interface(active_interface);
if (p.type != Packet_descriptor::BULK) {
Genode::error("No BULK packet");
error("No BULK packet");
iface.release(p);
return;
}
if (!p.succeded) {
Genode::error("complete error: packet not succeded");
if (req.pending) {
Genode::error("request pending: tag: ", active_tag, " read: ",
(int)req.read, " buffer: ", (void *)req.buffer, " lba: ",
req.lba, " size: ", req.size);
ack_pending_request(false);
error("complete error: packet not succeded");
if (request_pending()) {
error("request pending: tag: ", active_tag, " read: ",
request->read(), " buffer: ", Hex(request->address),
" lba: ", request->block_request.operation.block_number,
" size: ", request->size);
wakeup_client(false);
}
iface.release(p);
return;
@ -668,7 +687,7 @@ struct Usb::Block_driver : Usb::Completion,
static bool request_executed = false;
if (!p.read_transfer()) {
/* send read/write request */
if (req.pending) {
if (request_pending()) {
/*
* The CBW was successfully sent to the device, now read/write the
@ -688,16 +707,16 @@ struct Usb::Block_driver : Usb::Completion,
int actual_size = p.transfer.actual_size;
if (actual_size < 0) {
Genode::error("Transfer actual size: ", actual_size);
error("Transfer actual size: ", actual_size);
actual_size = 0;
}
/* the size indicates an IN I/O packet */
if ((uint32_t)actual_size >= _block_size) {
if (req.pending) {
if (request_pending()) {
/* the content was successfully read, get the CSW */
memcpy(req.buffer, iface.content(p), actual_size);
memcpy((void *)request->address, iface.content(p), actual_size);
csw(*this);
}
@ -707,41 +726,42 @@ struct Usb::Block_driver : Usb::Completion,
/* when ending up here, we should have gotten an CSW packet */
if (actual_size != Csw::LENGTH)
Genode::warning("This is not the actual size you are looking for");
warning("This is not the actual size you are looking for");
do {
Csw csw((addr_t)iface.content(p));
uint32_t const sig = csw.sig();
if (sig != Csw::SIG) {
Genode::error("CSW signature does not match: ",
Hex(sig, Hex::PREFIX, Hex::PAD));
error("CSW signature does not match: ",
Hex(sig, Hex::PREFIX, Hex::PAD));
break;
}
uint32_t const tag = csw.tag();
if (tag != active_tag) {
Genode::error("CSW tag mismatch. Got ", tag, " expected: ",
active_tag);
error("CSW tag mismatch. Got ", tag, " expected: ",
active_tag);
break;
}
uint8_t const status = csw.sts();
if (status != Csw::PASSED) {
Genode::error("CSW failed: ", Hex(status, Hex::PREFIX, Hex::PAD),
" read: ", (int)req.read, " buffer: ", (void *)req.buffer,
" lba: ", req.lba, " size: ", req.size);
error("CSW failed: ", Hex(status, Hex::PREFIX, Hex::PAD),
" read: ", request->read(), " buffer: ", (void *)request->address,
" lba: ", request->block_request.operation.block_number,
" size: ", request->size);
break;
}
uint32_t const dr = csw.dr();
if (dr) {
Genode::warning("CSW data residue: ", dr, " not considered");
warning("CSW data residue: ", dr, " not considered");
}
/* ack Block::Packet_descriptor */
request_executed = false;
ack_pending_request();
wakeup_client(true);
} while (0);
iface.release(p);
@ -767,12 +787,12 @@ struct Usb::Block_driver : Usb::Completion,
* \param ep Server::Endpoint
* \param sigh signal context used for annoucing Block service
*/
Block_driver(Env &env, Genode::Allocator &alloc,
Genode::Signal_context_capability sigh)
Block_driver(Env &env, Allocator &alloc,
Genode::Signal_context_capability sigh,
Signal_handler<Main> &wake_up_handler)
:
Block::Driver(env.ram()),
env(env), ep(env.ep()), announce_sigh(sigh), alloc(&alloc),
device(&alloc, usb, ep)
device(&alloc, usb, ep), wake_up_handler(wake_up_handler)
{
parse_config(config.xml());
reporter.enabled(true);
@ -789,111 +809,196 @@ struct Usb::Block_driver : Usb::Completion,
/**
* Send CBW
*/
void send_cbw(Block::sector_t lba, size_t len, bool read)
void send_cbw(block_number_t lba, block_count_t count, bool read)
{
uint32_t const t = new_tag();
char cb[Cbw::LENGTH];
if (read) {
if (force_cmd_16) Read_16 r((addr_t)cb, t, active_lun, lba, len, _block_size);
else Read_10 r((addr_t)cb, t, active_lun, lba, len, _block_size);
if (force_cmd_16) Read_16 r((addr_t)cb, t, active_lun, lba, count, _block_size);
else Read_10 r((addr_t)cb, t, active_lun, lba, count, _block_size);
} else {
if (force_cmd_16) Write_16 w((addr_t)cb, t, active_lun, lba, len, _block_size);
else Write_10 w((addr_t)cb, t, active_lun, lba, len, _block_size);
if (force_cmd_16) Write_16 w((addr_t)cb, t, active_lun, lba, count, _block_size);
else Write_10 w((addr_t)cb, t, active_lun, lba, count, _block_size);
}
cbw(cb, *this);
}
/**
* Perform IO/ request
*
* \param read set to true when reading, false when writting
* \param lba address of the starting block
* \param buffer source/destination buffer
* \param p Block::Packet_descriptor
*/
void io(bool read, Block::sector_t lba, size_t count,
char *buffer, Block::Packet_descriptor &p)
{
if (!device_plugged) throw Io_error();
if (lba+count > _block_count) throw Io_error();
if (req.pending) throw Request_congestion();
/*******************************************
** interface to Block_session_component **
*******************************************/
req.pending = true;
req.packet = p;
req.lba = lba;
req.size = count * _block_size;
req.buffer = buffer;
req.read = read;
send_cbw(lba, count, read);
}
/*******************************
** Block::Driver interface **
*******************************/
Block::Session::Info info() const override
Block::Session::Info info() const
{
return { .block_size = _block_size,
.block_count = _block_count,
.align_log2 = Genode::log2(_block_size),
.align_log2 = log2(_block_size),
.writeable = _writeable };
}
void read(Block::sector_t lba, size_t count,
char *buffer, Block::Packet_descriptor &p) override {
io(true, lba, count, buffer, p); }
Response submit(Request &block_request,
Request_stream::Payload const &payload)
{
if (device_plugged == false)
return Response::REJECTED;
void write(Block::sector_t lba, size_t count,
char const *buffer, Block::Packet_descriptor &p) override {
io(false, lba, count, const_cast<char*>(buffer), p); }
Operation const &op = block_request.operation;
void sync() override { /* maybe implement SYNCHRONIZE_CACHE_10/16? */ }
/* read only */
if (info().writeable == false &&
op.type == Operation::Type::WRITE)
return Response::REJECTED;
/* range check */
block_number_t const last = op.block_number + op.count;
if (last >= info().block_count)
return Response::REJECTED;
/* check if request is pending */
if (request_pending())
return Response::RETRY;
request.construct(block_request, payload);
/* execute */
send_cbw(op.block_number, op.count, request->read());
return Response::ACCEPTED;
}
template <typename FUNC>
void with_completed(FUNC const &fn)
{
if (request_pending() == false || request->completed == false) return;
fn(request->block_request);
request.destruct();
}
};
struct Usb::Main
struct Usb::Block_session_component : Rpc_object<Block::Session>,
Block::Request_stream
{
Env &_env;
Block_session_component(Env &env, Dataspace_capability ds,
Signal_context_capability sigh,
Block::Session::Info info)
:
Request_stream(env.rm(), ds, env.ep(), sigh, info),
_env(env)
{
_env.ep().manage(*this);
}
~Block_session_component() { _env.ep().dissolve(*this); }
Info info() const override { return Request_stream::info(); }
Capability<Tx> tx_cap() override { return Request_stream::tx_cap(); }
};
struct Usb::Main : Rpc_object<Typed_root<Block::Session>>
{
Env &env;
Heap heap { env.ram(), env.rm() };
void announce()
{
env.parent().announce(env.ep().manage(root));
}
Constructible<Attached_ram_dataspace> block_ds { };
Constructible<Block_session_component> block_session { };
Signal_handler<Main> announce_dispatcher {
env.ep(), *this, &Usb::Main::announce };
struct Factory : Block::Driver_factory
Io_signal_handler<Main> request_handler {
env.ep(), *this, &Main::handle_requests };
Block_driver driver { env, heap, announce_dispatcher, request_handler };
void announce()
{
Env &env;
Allocator &alloc;
Signal_context_capability sigh;
Usb::Block_driver driver;
env.parent().announce(env.ep().manage(*this));
}
Factory(Env &env, Allocator &alloc,
Signal_context_capability sigh)
: env(env), alloc(alloc), sigh(sigh),
driver(env, alloc, sigh) { }
/**
* There can only be one request in flight for this driver, so keep it simple
*/
void handle_requests()
{
if (!block_session.constructed()) return;
Block::Driver *create() override { return &driver; }
/* ack and release possibly pending packet */
block_session->try_acknowledge([&] (Block_session_component::Ack &ack) {
driver.with_completed([&] (Block::Request &request) {
ack.submit(request);
});
});
void destroy(Block::Driver *) override { }
block_session->with_requests([&] (Request request) {
private:
/* only read/write for now */
if (Operation::has_payload(request.operation.type) == false) {
request.success = true;
return Response::REJECTED;
}
/*
* Noncopyable
*/
Factory(Factory const &);
Factory &operator = (Factory const &);
};
Response response = Response::RETRY;
Factory factory { env, heap, announce_dispatcher };
Block::Root root { env.ep(), heap, env.rm(), factory, true };
block_session->with_payload([&] (Request_stream::Payload const &payload) {
response = driver.submit(request, payload);
});
return response;
});
block_session->wakeup_client_if_needed();
}
/*****************************
** Block session interface **
*****************************/
Genode::Session_capability session(Root::Session_args const &args,
Affinity const &) override
{
log("new block session: ", args.string());
if (block_session.constructed()) {
error("device is already in use");
throw Service_denied();
}
size_t const ds_size =
Arg_string::find_arg(args.string(), "tx_buf_size").ulong_value(0);
Ram_quota const ram_quota = ram_quota_from_args(args.string());
if (ds_size >= ram_quota.value) {
warning("communication buffer size exceeds session quota");
throw Insufficient_ram_quota();
}
block_ds.construct(env.ram(), env.rm(), ds_size);
block_session.construct(env, block_ds->cap(), request_handler,
driver.info());
return block_session->cap();
}
void upgrade(Genode::Session_capability, Root::Upgrade_args const&) override { }
void close(Genode::Session_capability cap) override
{
if (!block_session.constructed() || !(block_session->cap() == cap))
return;
block_session.destruct();
block_ds.destruct();
}
Main(Env &env) : env(env) { }
};