344 lines
8.0 KiB
C++
344 lines
8.0 KiB
C++
/*
|
|
* \brief Genode C API block API needed by L4Linux
|
|
* \author Stefan Kalkowski
|
|
* \date 2009-05-19
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2009-2013 Genode Labs GmbH
|
|
*
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
* under the terms of the GNU General Public License version 2.
|
|
*/
|
|
|
|
#include <base/env.h>
|
|
#include <base/printf.h>
|
|
#include <base/allocator_avl.h>
|
|
#include <block_session/connection.h>
|
|
#include <os/config.h>
|
|
#include <util/assert.h>
|
|
|
|
#include <vcpu.h>
|
|
#include <linux.h>
|
|
|
|
namespace Fiasco {
|
|
#include <genode/block.h>
|
|
#include <l4/sys/irq.h>
|
|
}
|
|
|
|
namespace {
|
|
|
|
class Req_cache
|
|
{
|
|
private:
|
|
|
|
class Req_entry
|
|
{
|
|
public:
|
|
|
|
void *pkt;
|
|
void *req;
|
|
|
|
Req_entry() : pkt(0), req(0) {}
|
|
Req_entry(void *packet, void *request)
|
|
: pkt(packet), req(request) {}
|
|
};
|
|
|
|
|
|
enum { MAX = Block::Session::TX_QUEUE_SIZE };
|
|
|
|
Req_entry _cache[MAX];
|
|
|
|
int _find(void *packet)
|
|
{
|
|
for (int i=0; i < MAX; i++)
|
|
if (_cache[i].pkt == packet)
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
public:
|
|
|
|
void insert(void *packet, void *request)
|
|
{
|
|
int idx = _find(0);
|
|
ASSERT(idx >= 0, "Req cache full!");
|
|
|
|
_cache[idx] = Req_entry(packet, request);
|
|
}
|
|
|
|
void remove(void *packet, void **request)
|
|
{
|
|
int idx = _find(packet);
|
|
ASSERT(idx >= 0, "Req cache entry not found!");
|
|
|
|
*request = _cache[idx].req;
|
|
_cache[idx].pkt = 0;
|
|
_cache[idx].req = 0;
|
|
}
|
|
};
|
|
|
|
|
|
class Block_device
|
|
{
|
|
private:
|
|
|
|
enum Dimensions {
|
|
TX_BUF_SIZE = 5 * 1024 * 1024
|
|
};
|
|
|
|
Req_cache _cache;
|
|
Genode::Allocator_avl _alloc;
|
|
Block::Connection _session;
|
|
Genode::size_t _blk_size;
|
|
Block::sector_t _blk_cnt;
|
|
Block::Session::Operations _blk_ops;
|
|
Genode::Native_capability _irq_cap;
|
|
Genode::Signal_context _tx;
|
|
char _name[32];
|
|
|
|
public:
|
|
|
|
Block_device(const char *label)
|
|
: _alloc(Genode::env()->heap()),
|
|
_session(&_alloc, TX_BUF_SIZE, label),
|
|
_irq_cap(L4lx::vcpu_connection()->alloc_irq())
|
|
{
|
|
_session.info(&_blk_cnt, &_blk_size, &_blk_ops);
|
|
Genode::strncpy(_name, label, sizeof(_name));
|
|
}
|
|
|
|
Req_cache *cache() { return &_cache; }
|
|
Block::Connection *session() { return &_session; }
|
|
Fiasco::l4_cap_idx_t irq_cap() { return _irq_cap.dst(); }
|
|
Genode::Signal_context *context() { return &_tx; }
|
|
Genode::size_t block_size() { return _blk_size; }
|
|
Genode::size_t block_count() { return _blk_cnt; }
|
|
bool writeable() {
|
|
return _blk_ops.supported(Block::Packet_descriptor::WRITE); }
|
|
const char *name() { return _name; }
|
|
};
|
|
|
|
|
|
class Signal_thread : public Genode::Thread<8192>
|
|
{
|
|
private:
|
|
|
|
unsigned _count;
|
|
Block_device **_devs;
|
|
Genode::Lock _ready_lock;
|
|
|
|
protected:
|
|
|
|
void entry()
|
|
{
|
|
using namespace Fiasco;
|
|
using namespace Genode;
|
|
|
|
Signal_receiver receiver;
|
|
for (unsigned i = 0; i < _count; i++) {
|
|
Signal_context_capability cap(receiver.manage(_devs[i]->context()));
|
|
_devs[i]->session()->tx_channel()->sigh_ready_to_submit(cap);
|
|
_devs[i]->session()->tx_channel()->sigh_ack_avail(cap);
|
|
}
|
|
|
|
_ready_lock.unlock();
|
|
|
|
while (true) {
|
|
Signal s = receiver.wait_for_signal();
|
|
for (unsigned i = 0; i < _count; i++) {
|
|
if (_devs[i]->context() == s.context()) {
|
|
if (l4_error(l4_irq_trigger(_devs[i]->irq_cap())) != -1)
|
|
PWRN("IRQ block trigger failed\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public:
|
|
|
|
Signal_thread(Block_device **devs)
|
|
: Genode::Thread<8192>("blk-signal-thread"),
|
|
_count(Fiasco::genode_block_count()), _devs(devs),
|
|
_ready_lock(Genode::Lock::LOCKED) {}
|
|
|
|
void start()
|
|
{
|
|
Genode::Thread_base::start();
|
|
|
|
/*
|
|
* Do not return until the new thread has initialized the
|
|
* signal handlers.
|
|
*/
|
|
_ready_lock.lock();
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
|
|
static FASTCALL void (*end_request)(void*, short, void*, unsigned long) = 0;
|
|
static Block_device **devices = 0;
|
|
|
|
using namespace Fiasco;
|
|
|
|
extern "C" {
|
|
|
|
unsigned genode_block_count()
|
|
{
|
|
using namespace Genode;
|
|
|
|
Linux::Irq_guard guard;
|
|
|
|
static unsigned count = 0;
|
|
if (count == 0) {
|
|
try {
|
|
Xml_node config = Genode::config()->xml_node();
|
|
size_t sn_cnt = config.num_sub_nodes();
|
|
for (unsigned i = 0; i < sn_cnt; i++)
|
|
if (config.sub_node(i).has_type("block"))
|
|
count++;
|
|
|
|
if (count == 0)
|
|
return count;
|
|
|
|
devices = (Block_device**)
|
|
env()->heap()->alloc(count * sizeof(Block_device*));
|
|
|
|
char label[64];
|
|
for (unsigned i = 0, j = 0; i < sn_cnt; i++) {
|
|
if (config.sub_node(i).has_type("block")) {
|
|
config.sub_node(i).attribute("label").value(label,
|
|
sizeof(label));
|
|
devices[j] = new (env()->heap()) Block_device(label);
|
|
j++;
|
|
}
|
|
}
|
|
} catch(...) { PWRN("config parsing error!"); }
|
|
}
|
|
return count;
|
|
}
|
|
|
|
|
|
const char* genode_block_name(unsigned idx)
|
|
{
|
|
if (idx >= genode_block_count()) {
|
|
PWRN("Invalid index!");
|
|
return 0;
|
|
}
|
|
return devices[idx]->name();
|
|
}
|
|
|
|
|
|
l4_cap_idx_t genode_block_irq_cap(unsigned idx)
|
|
{
|
|
if (idx >= genode_block_count()) {
|
|
PWRN("Invalid index!");
|
|
return 0;
|
|
}
|
|
return devices[idx]->irq_cap();
|
|
}
|
|
|
|
|
|
void genode_block_register_callback(FASTCALL void (*func)(void*, short,
|
|
void*, unsigned long))
|
|
{
|
|
Linux::Irq_guard guard;
|
|
|
|
static Signal_thread thread(devices);
|
|
if (!end_request) {
|
|
end_request = func;
|
|
thread.start();
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
genode_block_geometry(unsigned idx, unsigned long *cnt, unsigned long *sz,
|
|
int *write, unsigned long *queue_sz)
|
|
{
|
|
if (idx >= genode_block_count()) {
|
|
PWRN("Invalid index!");
|
|
return;
|
|
}
|
|
|
|
Linux::Irq_guard guard;
|
|
|
|
*cnt = devices[idx]->block_count();
|
|
*sz = devices[idx]->block_size();
|
|
*queue_sz = devices[idx]->session()->tx()->bulk_buffer_size();
|
|
*write = devices[idx]->writeable() ? 1 : 0;
|
|
}
|
|
|
|
|
|
void* genode_block_request(unsigned idx, unsigned long sz,
|
|
void *req, unsigned long *offset)
|
|
{
|
|
if (idx >= genode_block_count()) {
|
|
PWRN("Invalid index!");
|
|
return 0;
|
|
}
|
|
|
|
Linux::Irq_guard guard;
|
|
|
|
try {
|
|
Block::Connection *session = devices[idx]->session();
|
|
Block::Packet_descriptor p = session->tx()->alloc_packet(sz);
|
|
void *addr = session->tx()->packet_content(p);
|
|
devices[idx]->cache()->insert(addr, req);
|
|
*offset = p.offset();
|
|
return addr;
|
|
} catch (Block::Session::Tx::Source::Packet_alloc_failed) { }
|
|
return 0;
|
|
}
|
|
|
|
|
|
void genode_block_submit(unsigned idx, unsigned long queue_offset,
|
|
unsigned long size, unsigned long long disc_offset, int write)
|
|
{
|
|
if (idx >= genode_block_count()) {
|
|
PWRN("Invalid index!");
|
|
return;
|
|
}
|
|
|
|
Linux::Irq_guard guard;
|
|
|
|
Genode::size_t sector = disc_offset / devices[idx]->block_size();
|
|
Genode::size_t sector_cnt = size / devices[idx]->block_size();
|
|
Block::Packet_descriptor p(Block::Packet_descriptor(queue_offset, size),
|
|
write ? Block::Packet_descriptor::WRITE
|
|
: Block::Packet_descriptor::READ,
|
|
sector, sector_cnt);
|
|
devices[idx]->session()->tx()->submit_packet(p);
|
|
}
|
|
|
|
|
|
void genode_block_collect_responses(unsigned idx)
|
|
{
|
|
if (idx >= genode_block_count()) {
|
|
PWRN("Invalid index!");
|
|
return;
|
|
}
|
|
|
|
unsigned long flags;
|
|
l4x_irq_save(&flags);
|
|
|
|
Block::Connection *session = devices[idx]->session();
|
|
void *req;
|
|
while (session->tx()->ack_avail()) {
|
|
Block::Packet_descriptor packet = session->tx()->get_acked_packet();
|
|
void *addr = session->tx()->packet_content(packet);
|
|
bool write = packet.operation() == Block::Packet_descriptor::WRITE;
|
|
devices[idx]->cache()->remove(session->tx()->packet_content(packet), &req);
|
|
if (req && end_request) {
|
|
l4x_irq_restore(flags);
|
|
end_request(req, write, addr, packet.size());
|
|
l4x_irq_save(&flags);
|
|
}
|
|
session->tx()->release_packet(packet);
|
|
}
|
|
l4x_irq_restore(flags);
|
|
}
|
|
} // extern "C"
|