9#include <l4/cxx/ref_ptr>
11#include <l4/cxx/utils>
16#include <l4/l4virtio/server/virtio-block>
18#include <l4/libblock-device/debug.h>
19#include <l4/libblock-device/device.h>
20#include <l4/libblock-device/types.h>
21#include <l4/libblock-device/request.h>
23namespace Block_device {
25template <
typename DEV>
27:
public L4virtio::Svr::Block_dev_base<Mem_region_info>,
28 public L4::Epiface_t<Virtio_client<DEV>, L4virtio::Device>
31 class Generic_pending_request :
public Pending_request
34 int check_error(
int result)
36 if (result < 0 && result != -
L4_EBUSY)
37 client->handle_request_error(result,
this);
43 explicit Generic_pending_request(Virtio_client *c, cxx::unique_ptr<Request> &&req)
44 : request(cxx::move(req)), client(c)
52 cxx::unique_ptr<Request> request;
53 Virtio_client *client;
56 struct Pending_inout_request :
public Generic_pending_request
61 explicit Pending_inout_request(Virtio_client *c,
62 cxx::unique_ptr<Request> &&req)
63 : Generic_pending_request(c, cxx::move(req))
70 ~Pending_inout_request()
override
72 this->client->release_dma(
this);
76 {
return this->check_error(this->client->inout_request(
this)); }
79 struct Pending_flush_request :
public Generic_pending_request
81 using Generic_pending_request::Generic_pending_request;
84 {
return this->check_error(this->client->flush_request(
this)); }
87 struct Pending_cmd_request :
public Generic_pending_request
91 using Generic_pending_request::Generic_pending_request;
95 return this->check_error(this->client->discard_cmd_request(
this, 0));
100 using Device_type = DEV;
114 Virtio_client(cxx::Ref_ptr<Device_type>
const &dev,
unsigned numds,
115 bool readonly, L4Re::Util::Dbg msg_user = L4Re::Util::Dbg{},
116 L4Re::Util::Dbg msg_dev = L4Re::Util::Dbg{2})
117 : L4virtio::Svr::
Block_dev_base<Mem_region_info>(L4VIRTIO_VENDOR_KK, 0x100,
118 dev->capacity() >> 9,
122 _client_invalidate_cb(nullptr),
123 _client_idle_cb(nullptr),
129 init_discard_info(0);
137 if (_client_invalidate_cb)
138 _client_invalidate_cb(
false);
140 _negotiated_features.raw = 0;
151 if (_device->supports_flush())
154 _shutdown_state = Shutdown_type::Running;
155 _negotiated_features.raw = 0;
159 {
return _shutdown_state == Shutdown_type::Client_gone; }
162 using L4virtio::Svr::Block_dev_base<Mem_region_info>::check_for_new_requests;
163 using L4virtio::Svr::Block_dev_base<Mem_region_info>
::get_request;
166 void set_client_invalidate_cb(std::function<
void(
bool)> &&cb)
168 _client_invalidate_cb = cb;
171 void set_client_idle_cb(std::function<
void()> &&cb)
173 _client_idle_cb = cb;
177 void set_device_notify_irq(L4::Cap<L4::Irq> irq)
179 _device_notify_irq = irq;
184 return _device_notify_irq;
192 cxx::unique_ptr<Pending_request> start_request(cxx::unique_ptr<Request> &&req)
194 auto trace = Dbg::trace(
"virtio");
196 cxx::unique_ptr<Pending_request> pending;
198 if (_shutdown_state != Shutdown_type::Running)
200 trace.printf(
"Failing requests as the client is shutting down\n");
205 trace.printf(
"request received: type 0x%x, sector 0x%llx\n",
206 req->header().type, req->header().sector);
207 switch (req->header().type)
212 auto p = cxx::make_unique<Pending_inout_request>(
this, cxx::move(req));
213 int ret = build_inout_blocks(p.get());
215 pending.reset(p.release());
217 handle_request_error(ret, p.get());
222 auto p = cxx::make_unique<Pending_flush_request>(
this, cxx::move(req));
223 int ret = check_flush_request(p.get());
225 pending.reset(p.release());
227 handle_request_error(ret, p.get());
233 auto p = cxx::make_unique<Pending_cmd_request>(
this, cxx::move(req));
234 int ret = build_discard_cmd_blocks(p.get());
236 pending.reset(p.release());
238 handle_request_error(ret, p.get());
249 void task_finished(Generic_pending_request *preq,
int error,
l4_size_t sz)
256 if (_shutdown_state != Client_gone)
264 cxx::unique_ptr<Pending_request> ureq(preq);
270 void shutdown_event(Shutdown_type type)
276 if (_shutdown_state == Client_gone)
281 l4_assert(_shutdown_state != System_shutdown);
284 l4_assert(_shutdown_state != System_suspend
285 || type == Shutdown_type::Running);
288 _shutdown_state = type;
290 if (type == Shutdown_type::Client_shutdown)
295 l4_assert(_shutdown_state == Shutdown_type::Running);
298 if (type != Shutdown_type::Running)
300 if (_client_invalidate_cb)
301 _client_invalidate_cb(type != Shutdown_type::Client_gone);
318 L4::Cap<void> register_obj(L4::Registry_iface *registry,
319 char const *service = 0)
331 L4::Cap<void> register_obj(L4::Registry_iface *registry,
332 L4::Cap<L4::Rcv_endpoint> ep)
342 void unregister_obj(L4::Registry_iface *registry)
349 return _in_flight != 0;
352 Notification_domain
const *notification_domain()
const
353 {
return _device->notification_domain(); }
356 L4::Ipc_svr::Server_iface *
server_iface()
const override
362 void release_dma(Pending_inout_request *req)
365 Inout_block *cur = &req->blocks;
368 if (cur->num_sectors)
369 _device->dma_unmap(cur->dma_addr, cur->num_sectors, req->dir);
370 cur = cur->next.get();
374 int build_inout_blocks(Pending_inout_request *preq)
376 auto *req = preq->request.get();
377 l4_size_t sps = _device->sector_size() >> 9;
378 l4_uint64_t current_sector = req->header().sector / sps;
379 l4_uint64_t sectors = _device->capacity() / _device->sector_size();
380 auto dir = preq->dir;
386 if (device_features().ro())
390 if (_negotiated_features.config_wce())
393 flags = Block_device::Inout_f_wb;
395 else if (_negotiated_features.flush())
396 flags = Block_device::Inout_f_wb;
400 if (current_sector * sps != req->header().sector)
403 Inout_block *last_blk =
nullptr;
407 while (req->has_more())
409 Request::Data_block b;
411 if (++seg > _device->max_segments())
416 b = req->next_block();
418 catch (L4virtio::Svr::Bad_descriptor
const &e)
420 Dbg::warn().printf(
"Descriptor error: %s\n", e.
message());
427 l4_size_t sz = b.len / _device->sector_size();
429 if (sz * _device->sector_size() != b.len)
431 Dbg::warn().printf(
"Bad block size 0x%x\n", b.len);
438 if (current_sector > sectors - sz)
444 last_blk->next = cxx::make_unique<Inout_block>();
445 blk = last_blk->next.get();
451 long ret = _device->dma_map(b.mem, off, sz, dir, &phys);
455 blk->dma_addr = phys;
456 blk->virt_addr = b.addr;
457 blk->num_sectors = sz;
458 current_sector += sz;
467 void maintain_cache_before_req(Pending_inout_request
const *preq)
471 for (Inout_block
const *cur = &preq->blocks; cur; cur = cur->next.get())
476 l4_size_t vsize = cur->num_sectors * _device->sector_size();
487 void maintain_cache_after_req(Pending_inout_request
const *preq)
491 for (Inout_block
const *cur = &preq->blocks; cur; cur = cur->next.get())
496 l4_size_t vsize = cur->num_sectors * _device->sector_size();
503 int inout_request(Pending_inout_request *preq)
505 auto *req = preq->request.get();
506 l4_uint64_t sector = req->header().sector / (_device->sector_size() >> 9);
508 maintain_cache_before_req(preq);
509 int res = _device->inout_data(
510 sector, preq->blocks,
512 maintain_cache_after_req(preq);
513 task_finished(preq, error, sz);
524 int check_flush_request(Pending_flush_request *preq)
526 if (!_negotiated_features.flush())
529 auto *req = preq->request.get();
532 if (req->header().sector)
538 int flush_request(Pending_flush_request *preq)
540 int res = _device->flush([
this, preq](
int error,
l4_size_t sz) {
541 task_finished(preq, error, sz);
553 _negotiated_features = negotiated_features();
557 template <
typename T = Device_type>
558 void init_discard_info(
long) {}
560 template <
typename T = Device_type>
561 auto init_discard_info(
int)
562 ->
decltype(((T*)0)->discard_info(),
void())
564 _di = _device->discard_info();
567 size_t sps = _device->sector_size() >> 9;
568 if (_di.max_discard_sectors)
569 set_discard(_di.max_discard_sectors * sps, _di.max_discard_seg,
570 _di.discard_sector_alignment * sps);
571 if (_di.max_write_zeroes_sectors)
573 _di.max_write_zeroes_seg, _di.write_zeroes_may_unmap);
576 int build_discard_cmd_blocks(Pending_cmd_request *preq)
578 auto *req = preq->request.get();
581 if (this->device_features().ro())
585 if (req->header().sector)
590 if (!_negotiated_features.discard())
595 if (!_negotiated_features.write_zeroes())
599 auto *d = _device.get();
602 size_t max_seg = discard ? _di.max_discard_seg : _di.max_write_zeroes_seg;
605 l4_uint64_t sectors = d->capacity() / d->sector_size();
607 Inout_block *last_blk =
nullptr;
609 while (req->has_more())
611 Request::Data_block b;
615 b = req->next_block();
617 catch (L4virtio::Svr::Bad_descriptor
const &e)
619 Dbg::warn().printf(
"Descriptor error: %s\n", e.
message());
625 size_t items = b.len /
sizeof(payload[0]);
626 if (items *
sizeof(payload[0]) != b.len)
629 if (seg + items > max_seg)
633 for (
auto i = 0u; i < items; i++)
639 if (p.sector % sps != 0)
641 if (p.num_sectors % sps != 0)
646 p.num_sectors /= sps;
649 if (p.num_sectors > sectors)
651 if (p.sector > sectors - p.num_sectors)
654 if (p.flags & L4VIRTIO_BLOCK_DISCARD_F_RESERVED)
660 last_blk->next = cxx::make_unique<Inout_block>();
661 blk = last_blk->next.get();
666 blk->sector = p.sector;
667 blk->num_sectors = p.num_sectors;
671 if (p.flags & L4VIRTIO_BLOCK_DISCARD_F_UNMAP)
673 if (p.num_sectors > _di.max_discard_sectors)
678 if (p.flags & L4VIRTIO_BLOCK_DISCARD_F_UNMAP
679 && _di.write_zeroes_may_unmap)
680 blk->flags = Inout_f_unmap;
681 if (p.num_sectors > _di.max_write_zeroes_sectors)
692 template <
typename T = Device_type>
693 int discard_cmd_request(Pending_cmd_request *,
long)
696 template <
typename T = Device_type>
697 auto discard_cmd_request(Pending_cmd_request *preq,
int)
698 ->
decltype(((T*)0)->discard_info(),
int())
700 auto *req = preq->request.get();
703 int res = _device->discard(
705 [
this, preq](
int error,
l4_size_t sz) { task_finished(preq, error, sz); },
716 void handle_request_error(
int error, Generic_pending_request *pending)
718 auto trace = Dbg::trace(
"virtio");
722 trace.printf(
"Unsupported operation.\n");
728 trace.printf(
"Got IO error: %d\n", error);
734 L4::Cap<L4::Irq> _device_notify_irq;
735 std::function<void(
bool)> _client_invalidate_cb;
736 std::function<void()> _client_idle_cb;
738 Shutdown_type _shutdown_state;
739 cxx::Ref_ptr<Device_type> _device;
740 Device_discard_feature::Discard_info _di;
742 L4virtio::Svr::Block_features _negotiated_features;
l4_uint64_t Dma_addr
Data type for DMA addresses.
Direction
Direction of the DMA transfers.
@ To_device
device reads the memory
@ None
device is coherently connected to the memory
@ From_device
device writes to the memory
virtual void unregister_obj(L4::Epiface *o, bool unmap=true)=0
Unregister the given object o from the server.
virtual L4::Cap< void > register_obj(L4::Epiface *o, char const *service)=0
Register an L4::Epiface for an IPC gate available in the applications environment under the name serv...
void set_write_zeroes(l4_uint32_t max_write_zeroes_sectors, l4_uint32_t max_write_zeroes_seg, l4_uint8_t write_zeroes_may_unmap)
Block_dev_base(l4_uint32_t vendor, unsigned queue_size, l4_uint64_t capacity, bool read_only, L4Re::Util::Dbg msg_user=L4Re::Util::Dbg{}, L4Re::Util::Dbg msg_dev=L4Re::Util::Dbg{2})
virtual bool queue_stopped()=0
l4_uint8_t get_writeback()
cxx::unique_ptr< Request > get_request()
void set_config_wce(l4_uint8_t writeback)
void set_size_max(l4_uint32_t sz)
virtual void reset_device()=0
void set_discard(l4_uint32_t max_discard_sectors, l4_uint32_t max_discard_seg, l4_uint32_t discard_sector_alignment)
void set_seg_max(l4_uint32_t sz)
void finalize_request(cxx::unique_ptr< Request > req, unsigned sz, l4_uint8_t status=L4VIRTIO_BLOCK_S_OK)
virtual bool check_features()
void init_mem_info(unsigned num)
virtual L4::Cap< L4::Irq > device_notify_irq() const
unsigned int l4_size_t
Unsigned size type.
unsigned long l4_addr_t
Address type.
unsigned int l4_uint32_t
Unsigned 32bit value.
unsigned long long l4_uint64_t
Unsigned 64bit value.
int l4_cache_flush_data(unsigned long start, unsigned long end) L4_NOTHROW
Cache flush a range; writes back to PoC.
L4_BEGIN_DECLS int l4_cache_clean_data(unsigned long start, unsigned long end) L4_NOTHROW
Cache clean a range in D-cache; writes back to PoC.
int l4_cache_inv_data(unsigned long start, unsigned long end) L4_NOTHROW
Cache invalidate a range; might write back to PoC.
@ L4_EBUSY
Object currently busy, try later.
struct l4virtio_block_discard_t l4virtio_block_discard_t
Structure used for the write zeroes and discard commands.
@ L4VIRTIO_BLOCK_T_DISCARD
Discard a range of sectors.
@ L4VIRTIO_BLOCK_T_FLUSH
Flush data to disk.
@ L4VIRTIO_BLOCK_T_IN
Read from device.
@ L4VIRTIO_BLOCK_T_OUT
Write to device.
@ L4VIRTIO_BLOCK_T_WRITE_ZEROES
Write zeroes to a range of sectors.
@ L4VIRTIO_BLOCK_S_IOERR
IO error on device.
@ L4VIRTIO_BLOCK_S_UNSUPP
Operation is not supported.
Cache-consistency functions.
T chkcap(T &&cap, char const *extra="", l4_ret_t err=-L4_ENOMEM)
Check for valid capability or raise C++ exception.
T access_once(T const *a)
Read the value at an address at most once.
virtual void fail_request()=0
Callback used when a request is dropped from the queue.
virtual int handle_request()=0
Callback used when the request is ready for processing.
Server_iface * server_iface() const
Get pointer to server interface at which the object is currently registered.
char const * message() const
Get a human readable description of the error code.
#define l4_assert(expr)
Low-level assert.
Common task related definitions.
Implementation of a list of unique-ptr-managed objects.