diff --git a/ports/src/vancouver/disk.cc b/ports/src/vancouver/disk.cc index 46a4e81bb3..f0076ed4d5 100644 --- a/ports/src/vancouver/disk.cc +++ b/ports/src/vancouver/disk.cc @@ -1,6 +1,7 @@ /* * \brief Block interface * \author Markus Partheymueller + * \author Alexander Boettcher * \date 2012-09-15 */ @@ -25,30 +26,44 @@ #include #include #include +#include +#include /* local includes */ #include #include -static Genode::Native_utcb utcb_backup; +/* Seoul includes */ +#include + +static Genode::Signal_receiver* disk_receiver() +{ + static Genode::Signal_receiver receiver; + return &receiver; +} + + +static Genode::Heap * disk_heap() { + using namespace Genode; + static Heap heap(env()->ram_session(), env()->rm_session(), 4096); + return &heap; +} Vancouver_disk::Vancouver_disk(Synced_motherboard &mb, char * backing_store_base, char * backing_store_fb_base) : - _startup_lock(Genode::Lock::LOCKED), _motherboard(mb), _backing_store_base(backing_store_base), - _backing_store_fb_base(backing_store_fb_base) + _backing_store_fb_base(backing_store_fb_base), + _tslab_msg(disk_heap()), _tslab_dma(disk_heap()), _tslab_avl(disk_heap()) { /* initialize struct with 0 size */ for (int i=0; i < MAX_DISKS; i++) { _diskcon[i].blk_size = 0; } - start(); - /* shake hands with disk thread */ - _startup_lock.lock(); + start(); } @@ -62,12 +77,96 @@ void Vancouver_disk::entry() { Logging::printf("Hello, this is Vancouver_disk.\n"); - _startup_lock.unlock(); + while (true) { + Genode::Signal signal = disk_receiver()->wait_for_signal(); + Vancouver_disk_signal * disk_source = + reinterpret_cast(signal.context()); + + this->_signal_dispatch_entry(disk_source->disk_nr()); + } +} + + +void Vancouver_disk::_signal_dispatch_entry(unsigned disknr) +{ + Block::Session::Tx::Source *source = _diskcon[disknr].blk_con->tx(); + + while (source->ack_avail()) + { + Block::Packet_descriptor packet = source->get_acked_packet(); + char * source_addr = source->packet_content(packet); + + /* find the corresponding MessageDisk object */ + Avl_entry * obj = 0; + { + Genode::Lock::Guard lock_guard(_lookup_msg_lock); + obj = _lookup_msg.first(); + if (obj) + obj = obj->find(reinterpret_cast(source_addr)); + + if (!obj) { + PWRN("Unknown MessageDisk object - drop ack of block session"); + continue; + } + + /* remove helper object */ + _lookup_msg.remove(obj); + } + /* got the MessageDisk object */ + MessageDisk * msg = obj->msg(); + /* delete helper object */ + destroy(&_tslab_avl, obj); + + /* go ahead and tell VMM about new block event */ + if (!packet.succeeded() || + !(packet.operation() == Block::Packet_descriptor::Opcode::READ || + packet.operation() == Block::Packet_descriptor::Opcode::WRITE)) { + + PDBG("Getting block failed !"); + + MessageDiskCommit mdc(disknr, msg->usertag, + MessageDisk::DISK_STATUS_DEVICE); + _motherboard()->bus_diskcommit.send(mdc); + + } else { + + if (packet.operation() == Block::Packet_descriptor::Opcode::READ) { + + unsigned long long sector = msg->sector; + sector = (sector-packet.block_number()) * _diskcon[disknr].blk_size; + + for (unsigned i = 0; i < msg->dmacount; i++) { + char * dma_addr = _backing_store_base + + msg->dma[i].byteoffset + msg->physoffset; + + // check for bounds + if (dma_addr >= _backing_store_fb_base || + dma_addr < _backing_store_base) { + PERR("dma bounds violation"); + } else + memcpy(dma_addr, source_addr + sector, + msg->dma[i].bytecount); + + sector += msg->dma[i].bytecount; + } + + destroy(&_tslab_dma, msg->dma); + msg->dma = 0; + } + + MessageDiskCommit mdc (disknr, msg->usertag, MessageDisk::DISK_OK); + _motherboard()->bus_diskcommit.send(mdc); + } + + source->release_packet(packet); + destroy(&_tslab_msg, msg); + } } bool Vancouver_disk::receive(MessageDisk &msg) { + static Genode::Native_utcb utcb_backup; Utcb_guard guard(utcb_backup); if (msg.disknr >= MAX_DISKS) @@ -83,17 +182,26 @@ bool Vancouver_disk::receive(MessageDisk &msg) if (!_diskcon[msg.disknr].blk_size) { try { - Genode::Allocator_avl * block_alloc = new Genode::Allocator_avl(Genode::env()->heap()); - _diskcon[msg.disknr].blk_con = new Block::Connection(block_alloc, 4*512*1024, label); + Genode::Allocator_avl * block_alloc = + new Genode::Allocator_avl(disk_heap()); + + _diskcon[msg.disknr].blk_con = + new Block::Connection(block_alloc, 4*512*1024, label); + _diskcon[msg.disknr].dispatcher = + new Vancouver_disk_signal(*disk_receiver(), *this, + &Vancouver_disk::_signal_dispatch_entry, + msg.disknr); + + _diskcon[msg.disknr].blk_con->tx_channel()->sigh_ack_avail( + *_diskcon[msg.disknr].dispatcher); } catch (...) { /* there is none. */ return false; } - _diskcon[msg.disknr].blk_con->info( - &_diskcon[msg.disknr].blk_cnt, - &_diskcon[msg.disknr].blk_size, - &_diskcon[msg.disknr].ops); + _diskcon[msg.disknr].blk_con->info(&_diskcon[msg.disknr].blk_cnt, + &_diskcon[msg.disknr].blk_size, + &_diskcon[msg.disknr].ops); Logging::printf("Got info: %lu blocks (%u B), ops (R: %x, W:%x)\n ", _diskcon[msg.disknr].blk_cnt, @@ -105,10 +213,11 @@ bool Vancouver_disk::receive(MessageDisk &msg) Block::Session::Tx::Source *source = _diskcon[msg.disknr].blk_con->tx(); + msg.error = MessageDisk::DISK_OK; + switch (msg.type) { case MessageDisk::DISK_GET_PARAMS: { - msg.error = MessageDisk::DISK_OK; msg.params->flags = DiskParameter::FLAG_HARDDISK; msg.params->sectors = _diskcon[msg.disknr].blk_cnt; msg.params->sectorsize = _diskcon[msg.disknr].blk_size; @@ -120,9 +229,9 @@ bool Vancouver_disk::receive(MessageDisk &msg) case MessageDisk::DISK_READ: case MessageDisk::DISK_WRITE: { - bool read = (msg.type == MessageDisk::DISK_READ); + bool write = (msg.type == MessageDisk::DISK_WRITE); - if (!read && !_diskcon[msg.disknr].ops.supported(Block::Packet_descriptor::WRITE)) { + if (write && !_diskcon[msg.disknr].ops.supported(Block::Packet_descriptor::WRITE)) { MessageDiskCommit ro(msg.disknr, msg.usertag, MessageDisk::DISK_STATUS_DEVICE); _motherboard()->bus_diskcommit.send(ro); @@ -132,80 +241,74 @@ bool Vancouver_disk::receive(MessageDisk &msg) unsigned long long sector = msg.sector; unsigned long total = DmaDescriptor::sum_length(msg.dmacount, msg.dma); unsigned long blocks = total/_diskcon[msg.disknr].blk_size; + unsigned const blk_size = _diskcon[msg.disknr].blk_size; - if (blocks*_diskcon[msg.disknr].blk_size < total) - blocks++; + if (blocks * blk_size < total) blocks++; Block::Session::Tx::Source *source = _diskcon[msg.disknr].blk_con->tx(); - Block::Packet_descriptor - p(source->alloc_packet(blocks*_diskcon[msg.disknr].blk_size), - (read) ? Block::Packet_descriptor::READ - : Block::Packet_descriptor::WRITE, - sector, - blocks); + Block::Packet_descriptor packet; - if (read) { - source->submit_packet(p); - p = source->get_acked_packet(); + /* save original msg, required when we get acknowledgements */ + MessageDisk * msg_cpy = 0; + try { + msg_cpy = new (&_tslab_msg) MessageDisk(msg); + + packet = Block::Packet_descriptor( + source->alloc_packet(blocks * blk_size), + (write) ? Block::Packet_descriptor::WRITE + : Block::Packet_descriptor::READ, + sector, blocks); + } catch (...) { + if (msg_cpy) + destroy(&_tslab_msg, msg_cpy); + Logging::printf("could not allocate disk block elements\n"); + return false; } - if (!read || p.succeeded()) { - char * source_addr = source->packet_content(p); + char * source_addr = source->packet_content(packet); + { + Genode::Lock::Guard lock_guard(_lookup_msg_lock); + _lookup_msg.insert(new (&_tslab_avl) Avl_entry(source_addr, + msg_cpy)); + } - sector = (sector-p.block_number())*_diskcon[msg.disknr].blk_size; + /* copy DMA descriptors for read requests - they may change */ + if (!write) { + msg_cpy->dma = new (&_tslab_dma) DmaDescriptor[msg_cpy->dmacount]; + for (unsigned i = 0; i < msg_cpy->dmacount; i++) + memcpy(msg_cpy->dma + i, msg.dma + i, sizeof(DmaDescriptor)); + } - for (int i=0; i< msg.dmacount; i++) { - char * dma_addr = (char *) (msg.dma[i].byteoffset + msg.physoffset - + _backing_store_base); - /* check for bounds */ - if (dma_addr >= _backing_store_fb_base - || dma_addr < _backing_store_base) - return false; + /* for write operation */ + source_addr += (sector - packet.block_number()) * blk_size; - if (read) - memcpy(dma_addr, source_addr+sector, msg.dma[i].bytecount); - else - memcpy(source_addr+sector, dma_addr, msg.dma[i].bytecount); + /* check bounds for read and write operations */ + for (unsigned i = 0; i < msg_cpy->dmacount; i++) { + char * dma_addr = _backing_store_base + msg_cpy->dma[i].byteoffset + + msg_cpy->physoffset; - sector += msg.dma[i].bytecount; + /* check for bounds */ + if (dma_addr >= _backing_store_fb_base || + dma_addr < _backing_store_base) { + /* drop allocated objects not needed in error case */ + if (write) + destroy(&_tslab_dma, msg_cpy->dma); + destroy(&_tslab_msg, msg_cpy); + source->release_packet(packet); + return false; } - if (!read) { - source->submit_packet(p); - p = source->get_acked_packet(); - if (!p.succeeded()) { - Logging::printf("Operation failed.\n"); - { - MessageDiskCommit commit(msg.disknr, msg.usertag, - MessageDisk::DISK_STATUS_DEVICE); - _motherboard()->bus_diskcommit.send(commit); - break; - } - } - } - - { - MessageDiskCommit commit(msg.disknr, msg.usertag, - MessageDisk::DISK_OK); - _motherboard()->bus_diskcommit.send(commit); - } - } else { - - Logging::printf("Operation failed.\n"); - { - MessageDiskCommit commit(msg.disknr, msg.usertag, - MessageDisk::DISK_STATUS_DEVICE); - _motherboard()->bus_diskcommit.send(commit); + if (write) { + memcpy(source_addr, dma_addr, msg.dma[i].bytecount); + source_addr += msg.dma[i].bytecount; } } - source->release_packet(p); + source->submit_packet(packet); } break; - default: - Logging::printf("Got MessageDisk type %x\n", msg.type); return false; } diff --git a/ports/src/vancouver/disk.h b/ports/src/vancouver/disk.h index 3551a58d5a..fa579a2369 100644 --- a/ports/src/vancouver/disk.h +++ b/ports/src/vancouver/disk.h @@ -1,6 +1,7 @@ /* * \brief Block interface * \author Markus Partheymueller + * \author Alexander Boettcher * \date 2012-09-15 */ @@ -18,8 +19,8 @@ * conditions of the GNU General Public License version 2. */ -#ifndef _DISK_H_ -#define _DISK_H_ +#ifndef _VANCOUVER_DISK_H_ +#define _VANCOUVER_DISK_H_ /* Genode includes */ #include @@ -28,33 +29,98 @@ #include #include #include +#include /* local includes */ #include -/* NOVA userland includes */ -#include +class Vancouver_disk; -static const bool read_only = false; +class Vancouver_disk_signal : public Genode::Signal_dispatcher +{ + private: + + unsigned _disk_nr; + + public: + + Vancouver_disk_signal(Genode::Signal_receiver &sig_rec, + Vancouver_disk &obj, + void (Vancouver_disk::*member)(unsigned), + unsigned disk_nr) + : Genode::Signal_dispatcher(sig_rec, obj, member), + _disk_nr(disk_nr) {} + + unsigned disk_nr() { return _disk_nr; } +}; class Vancouver_disk : public Genode::Thread<8192>, public StaticReceiver { private: - enum { MAX_DISKS = 16 }; + /* helper class to lookup a MessageDisk object */ + class Avl_entry : public Genode::Avl_node + { + + private: + + Genode::addr_t _key; + MessageDisk * _msg; + + public: + + Avl_entry(void * key, MessageDisk * msg) + : _key(reinterpret_cast(key)), _msg(msg) { } + + bool higher(Avl_entry *e) { return e->_key > _key; } + + Avl_entry *find(Genode::addr_t ptr) + { + if (ptr == _key) return this; + Avl_entry *obj = this->child(ptr > _key); + return obj ? obj->find(ptr) : 0; + } + + MessageDisk * msg() { return _msg; } + }; + + /* block session used by disk models of VMM */ + enum { MAX_DISKS = 4 }; struct { Block::Connection *blk_con; Block::Session::Operations ops; Genode::size_t blk_size; Genode::size_t blk_cnt; + Vancouver_disk_signal *dispatcher; } _diskcon[MAX_DISKS]; - Genode::Lock _startup_lock; Synced_motherboard &_motherboard; char *_backing_store_base; char *_backing_store_fb_base; + /* slabs for temporary holding DMADescriptor and MessageDisk objects */ + typedef Genode::Tslab MessageDisk_Slab; + typedef Genode::Synchronized_allocator MessageDisk_Slab_Sync; + + typedef Genode::Tslab DmaDesc_Slab; + typedef Genode::Synchronized_allocator DmaDesc_Slab_Sync; + + MessageDisk_Slab_Sync _tslab_msg; + DmaDesc_Slab_Sync _tslab_dma; + + /* Structure to find back the MessageDisk object out of a Block Ack */ + typedef Genode::Tslab Avl_entry_slab; + typedef Genode::Synchronized_allocator Avl_entry_slab_sync; + + Avl_entry_slab_sync _tslab_avl; + + Genode::Avl_tree _lookup_msg; + Genode::Lock _lookup_msg_lock; + + /* entry function if signal must be dispatched */ + void _signal_dispatch_entry(unsigned); + public: /** @@ -73,4 +139,4 @@ class Vancouver_disk : public Genode::Thread<8192>, public StaticReceiver