L4Re Operating System Framework
Interface and Usage Documentation
Loading...
Searching...
No Matches
virtio_client.h
1/*
2 * Copyright (C) 2018-2025 Kernkonzept GmbH.
3 * Author(s): Sarah Hoffmann <sarah.hoffmann@kernkonzept.com>
4 *
5 * License: see LICENSE.spdx (in this directory or the directories above)
6 */
7#pragma once
8
9#include <l4/cxx/ref_ptr>
11#include <l4/cxx/utils>
12#include <l4/sys/cache.h>
13
14#include <l4/sys/task>
15
16#include <l4/l4virtio/server/virtio-block>
17
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>
22
23namespace Block_device {
24
25template <typename DEV>
26class Virtio_client
27: public L4virtio::Svr::Block_dev_base<Mem_region_info>,
28 public L4::Epiface_t<Virtio_client<DEV>, L4virtio::Device>
29{
30protected:
31 class Generic_pending_request : public Pending_request
32 {
33 protected:
34 int check_error(int result)
35 {
36 if (result < 0 && result != -L4_EBUSY)
37 client->handle_request_error(result, this);
38
39 return result;
40 }
41
42 public:
43 explicit Generic_pending_request(Virtio_client *c, cxx::unique_ptr<Request> &&req)
44 : request(cxx::move(req)), client(c)
45 {}
46
47 void fail_request() override
48 {
49 client->finalize_request(cxx::move(request), 0, L4VIRTIO_BLOCK_S_IOERR);
50 }
51
52 cxx::unique_ptr<Request> request;
53 Virtio_client *client;
54 };
55
56 struct Pending_inout_request : public Generic_pending_request
57 {
58 Inout_block blocks;
60
61 explicit Pending_inout_request(Virtio_client *c,
62 cxx::unique_ptr<Request> &&req)
63 : Generic_pending_request(c, cxx::move(req))
64 {
65 dir = this->request->header().type == L4VIRTIO_BLOCK_T_OUT
68 }
69
70 ~Pending_inout_request() override
71 {
72 this->client->release_dma(this);
73 }
74
75 int handle_request() override
76 { return this->check_error(this->client->inout_request(this)); }
77 };
78
79 struct Pending_flush_request : public Generic_pending_request
80 {
81 using Generic_pending_request::Generic_pending_request;
82
83 int handle_request() override
84 { return this->check_error(this->client->flush_request(this)); }
85 };
86
87 struct Pending_cmd_request : public Generic_pending_request
88 {
89 Inout_block blocks;
90
91 using Generic_pending_request::Generic_pending_request;
92
93 int handle_request() override
94 {
95 return this->check_error(this->client->discard_cmd_request(this, 0));
96 }
97 };
98
99public:
100 using Device_type = DEV;
101
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,
119 dev->is_read_only()
120 || readonly,
121 msg_user, msg_dev),
122 _client_invalidate_cb(nullptr),
123 _client_idle_cb(nullptr),
124 _numds(numds),
125 _device(dev),
126 _in_flight(0)
127 {
128 reset_client();
129 init_discard_info(0);
130 }
131
135 void reset_device() override
136 {
137 if (_client_invalidate_cb)
138 _client_invalidate_cb(false);
139 _device->reset();
140 _negotiated_features.raw = 0;
141 }
142
146 void reset_client()
147 {
148 init_mem_info(_numds);
149 set_seg_max(_device->max_segments());
150 set_size_max(_device->max_size());
151 if (_device->supports_flush())
152 set_flush();
153 set_config_wce(0); // starting in write-through mode
154 _shutdown_state = Shutdown_type::Running;
155 _negotiated_features.raw = 0;
156 }
157
158 bool queue_stopped() override
159 { return _shutdown_state == Shutdown_type::Client_gone; }
160
161 // make these interfaces public so that a request scheduler can invoke them
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;
164
165 // make it possible for the request scheduler to register a direct callback
166 void set_client_invalidate_cb(std::function<void(bool)> &&cb)
167 {
168 _client_invalidate_cb = cb;
169 }
170
171 void set_client_idle_cb(std::function<void()> &&cb)
172 {
173 _client_idle_cb = cb;
174 }
175
176 // make it possible for the request scheduler to register a device notify IRQ
177 void set_device_notify_irq(L4::Cap<L4::Irq> irq)
178 {
179 _device_notify_irq = irq;
180 }
181
182 L4::Cap<L4::Irq> device_notify_irq() const override
183 {
184 return _device_notify_irq;
185 }
186
192 cxx::unique_ptr<Pending_request> start_request(cxx::unique_ptr<Request> &&req)
193 {
194 auto trace = Dbg::trace("virtio");
195
196 cxx::unique_ptr<Pending_request> pending;
197
198 if (_shutdown_state != Shutdown_type::Running)
199 {
200 trace.printf("Failing requests as the client is shutting down\n");
201 this->finalize_request(cxx::move(req), 0, L4VIRTIO_BLOCK_S_IOERR);
202 return pending;
203 }
204
205 trace.printf("request received: type 0x%x, sector 0x%llx\n",
206 req->header().type, req->header().sector);
207 switch (req->header().type)
208 {
211 {
212 auto p = cxx::make_unique<Pending_inout_request>(this, cxx::move(req));
213 int ret = build_inout_blocks(p.get());
214 if (ret == L4_EOK)
215 pending.reset(p.release());
216 else
217 handle_request_error(ret, p.get());
218 break;
219 }
221 {
222 auto p = cxx::make_unique<Pending_flush_request>(this, cxx::move(req));
223 int ret = check_flush_request(p.get());
224 if (ret == L4_EOK)
225 pending.reset(p.release());
226 else
227 handle_request_error(ret, p.get());
228 break;
229 }
232 {
233 auto p = cxx::make_unique<Pending_cmd_request>(this, cxx::move(req));
234 int ret = build_discard_cmd_blocks(p.get());
235 if (ret == L4_EOK)
236 pending.reset(p.release());
237 else
238 handle_request_error(ret, p.get());
239 break;
240 }
241 default:
242 finalize_request(cxx::move(req), 0, L4VIRTIO_BLOCK_S_UNSUPP);
243 break;
244 }
245
246 return pending;
247 }
248
249 void task_finished(Generic_pending_request *preq, int error, l4_size_t sz)
250 {
251 _in_flight--;
252
253 // move on to the next request
254
255 // Only finalize if the client is still alive
256 if (_shutdown_state != Client_gone)
257 finalize_request(cxx::move(preq->request), sz, error);
258
259 // New requests might be schedulable
260 if (_client_idle_cb)
261 _client_idle_cb();
262
263 // pending request can be dropped
264 cxx::unique_ptr<Pending_request> ureq(preq);
265 }
266
270 void shutdown_event(Shutdown_type type)
271 {
272 // If the client is already in the Client_gone state, it means that it was
273 // already shutdown and this is another go at its removal. This situation
274 // can occur because at the time of its previous removal attempt there were
275 // still I/O requests in progress.
276 if (_shutdown_state == Client_gone)
277 return;
278
279 // Transitions from System_shutdown are also not allowed, the initiator
280 // should take care of graceful handling of this.
281 l4_assert(_shutdown_state != System_shutdown);
282 // If we are transitioning from System_suspend, it must be only to Running,
283 // the initiator should handle this gracefully.
284 l4_assert(_shutdown_state != System_suspend
285 || type == Shutdown_type::Running);
286
287 // Update shutdown state of the client
288 _shutdown_state = type;
289
290 if (type == Shutdown_type::Client_shutdown)
291 {
292 reset();
293 reset_client();
294 // Client_shutdown must transit to the Running state
295 l4_assert(_shutdown_state == Shutdown_type::Running);
296 }
297
298 if (type != Shutdown_type::Running)
299 {
300 if (_client_invalidate_cb)
301 _client_invalidate_cb(type != Shutdown_type::Client_gone);
302 _device->reset();
303 }
304 }
305
318 L4::Cap<void> register_obj(L4::Registry_iface *registry,
319 char const *service = 0)
320 {
321 L4::Cap<void> ret;
322 if (service)
323 ret = registry->register_obj(this, service);
324 else
325 ret = registry->register_obj(this);
326 L4Re::chkcap(ret);
327
328 return ret;
329 }
330
331 L4::Cap<void> register_obj(L4::Registry_iface *registry,
332 L4::Cap<L4::Rcv_endpoint> ep)
333 {
334 return L4Re::chkcap(registry->register_obj(this, ep));
335 }
336
342 void unregister_obj(L4::Registry_iface *registry)
343 {
344 registry->unregister_obj(this);
345 }
346
347 bool busy() const
348 {
349 return _in_flight != 0;
350 }
351
352 Notification_domain const *notification_domain() const
353 { return _device->notification_domain(); }
354
355protected:
356 L4::Ipc_svr::Server_iface *server_iface() const override
357 {
358 return this->L4::Epiface::server_iface();
359 }
360
361private:
362 void release_dma(Pending_inout_request *req)
363 {
364 // unmap DMA regions
365 Inout_block *cur = &req->blocks;
366 while (cur)
367 {
368 if (cur->num_sectors)
369 _device->dma_unmap(cur->dma_addr, cur->num_sectors, req->dir);
370 cur = cur->next.get();
371 }
372 }
373
374 int build_inout_blocks(Pending_inout_request *preq)
375 {
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;
381
382 l4_uint32_t flags = 0;
383 if (req->header().type == L4VIRTIO_BLOCK_T_OUT)
384 {
385 // If RO was offered, every write must fail
386 if (device_features().ro())
387 return -L4_EIO;
388
389 // Figure out whether the write has a write-through or write-back semantics
390 if (_negotiated_features.config_wce())
391 {
392 if (get_writeback() == 1)
393 flags = Block_device::Inout_f_wb;
394 }
395 else if (_negotiated_features.flush())
396 flags = Block_device::Inout_f_wb;
397 }
398
399 // Check alignment of the first sector
400 if (current_sector * sps != req->header().sector)
401 return -L4_EIO;
402
403 Inout_block *last_blk = nullptr;
404
405 size_t seg = 0;
406
407 while (req->has_more())
408 {
409 Request::Data_block b;
410
411 if (++seg > _device->max_segments())
412 return -L4_EIO;
413
414 try
415 {
416 b = req->next_block();
417 }
418 catch (L4virtio::Svr::Bad_descriptor const &e)
419 {
420 Dbg::warn().printf("Descriptor error: %s\n", e.message());
421 return -L4_EIO;
422 }
423
424 l4_size_t off = b.mem->ds_offset() + (l4_addr_t) b.addr
425 - (l4_addr_t) b.mem->local_base();
426
427 l4_size_t sz = b.len / _device->sector_size();
428
429 if (sz * _device->sector_size() != b.len)
430 {
431 Dbg::warn().printf("Bad block size 0x%x\n", b.len);
432 return -L4_EIO;
433 };
434
435 // Check bounds
436 if (sz > sectors)
437 return -L4_EIO;
438 if (current_sector > sectors - sz)
439 return -L4_EIO;
440
441 Inout_block *blk;
442 if (last_blk)
443 {
444 last_blk->next = cxx::make_unique<Inout_block>();
445 blk = last_blk->next.get();
446 }
447 else
448 blk = &preq->blocks;
449
451 long ret = _device->dma_map(b.mem, off, sz, dir, &phys);
452 if (ret < 0)
453 return ret;
454
455 blk->dma_addr = phys;
456 blk->virt_addr = b.addr;
457 blk->num_sectors = sz;
458 current_sector += sz;
459 blk->flags = flags;
460
461 last_blk = blk;
462 }
463
464 return L4_EOK;
465 }
466
467 void maintain_cache_before_req(Pending_inout_request const *preq)
468 {
469 if (preq->dir == L4Re::Dma_space::None)
470 return;
471 for (Inout_block const *cur = &preq->blocks; cur; cur = cur->next.get())
472 {
473 l4_addr_t vstart = (l4_addr_t)cur->virt_addr;
474 if (vstart)
475 {
476 l4_size_t vsize = cur->num_sectors * _device->sector_size();
477 if (preq->dir == L4Re::Dma_space::From_device)
478 l4_cache_inv_data(vstart, vstart + vsize);
479 else if (preq->dir == L4Re::Dma_space::To_device)
480 l4_cache_clean_data(vstart, vstart + vsize);
481 else // L4Re::Dma_space::Bidirectional
482 l4_cache_flush_data(vstart, vstart + vsize);
483 }
484 }
485 }
486
487 void maintain_cache_after_req(Pending_inout_request const *preq)
488 {
489 if (preq->dir == L4Re::Dma_space::None)
490 return;
491 for (Inout_block const *cur = &preq->blocks; cur; cur = cur->next.get())
492 {
493 l4_addr_t vstart = (l4_addr_t)cur->virt_addr;
494 if (vstart)
495 {
496 l4_size_t vsize = cur->num_sectors * _device->sector_size();
497 if (preq->dir != L4Re::Dma_space::To_device)
498 l4_cache_inv_data(vstart, vstart + vsize);
499 }
500 }
501 }
502
503 int inout_request(Pending_inout_request *preq)
504 {
505 auto *req = preq->request.get();
506 l4_uint64_t sector = req->header().sector / (_device->sector_size() >> 9);
507
508 maintain_cache_before_req(preq);
509 int res = _device->inout_data(
510 sector, preq->blocks,
511 [this, preq](int error, l4_size_t sz) {
512 maintain_cache_after_req(preq);
513 task_finished(preq, error, sz);
514 },
515 preq->dir);
516
517 // request successfully submitted to device
518 if (res >= 0)
519 _in_flight++;
520
521 return res;
522 }
523
524 int check_flush_request(Pending_flush_request *preq)
525 {
526 if (!_negotiated_features.flush())
527 return -L4_ENOSYS;
528
529 auto *req = preq->request.get();
530
531 // sector must be zero for FLUSH
532 if (req->header().sector)
533 return -L4_ENOSYS;
534
535 return L4_EOK;
536 }
537
538 int flush_request(Pending_flush_request *preq)
539 {
540 int res = _device->flush([this, preq](int error, l4_size_t sz) {
541 task_finished(preq, error, sz);
542 });
543
544 // request successfully submitted to device
545 if (res >= 0)
546 _in_flight++;
547
548 return res;
549 }
550
551 bool check_features(void) override
552 {
553 _negotiated_features = negotiated_features();
554 return true;
555 }
556
557 template <typename T = Device_type>
558 void init_discard_info(long) {}
559
560 template <typename T = Device_type>
561 auto init_discard_info(int)
562 -> decltype(((T*)0)->discard_info(), void())
563 {
564 _di = _device->discard_info();
565
566 // Convert sector sizes to virtio 512-byte sectors.
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)
572 set_write_zeroes(_di.max_write_zeroes_sectors * sps,
573 _di.max_write_zeroes_seg, _di.write_zeroes_may_unmap);
574 }
575
576 int build_discard_cmd_blocks(Pending_cmd_request *preq)
577 {
578 auto *req = preq->request.get();
579 bool discard = (req->header().type == L4VIRTIO_BLOCK_T_DISCARD);
580
581 if (this->device_features().ro())
582 return -L4_EIO;
583
584 // sector is used only for inout requests, it must be zero for WzD
585 if (req->header().sector)
586 return -L4_ENOSYS;
587
588 if (discard)
589 {
590 if (!_negotiated_features.discard())
591 return -L4_ENOSYS;
592 }
593 else
594 {
595 if (!_negotiated_features.write_zeroes())
596 return -L4_ENOSYS;
597 }
598
599 auto *d = _device.get();
600
601 size_t seg = 0;
602 size_t max_seg = discard ? _di.max_discard_seg : _di.max_write_zeroes_seg;
603
604 l4_size_t sps = d->sector_size() >> 9;
605 l4_uint64_t sectors = d->capacity() / d->sector_size();
606
607 Inout_block *last_blk = nullptr;
608
609 while (req->has_more())
610 {
611 Request::Data_block b;
612
613 try
614 {
615 b = req->next_block();
616 }
617 catch (L4virtio::Svr::Bad_descriptor const &e)
618 {
619 Dbg::warn().printf("Descriptor error: %s\n", e.message());
620 return -L4_EIO;
621 }
622
623 auto *payload = reinterpret_cast<l4virtio_block_discard_t *>(b.addr);
624
625 size_t items = b.len / sizeof(payload[0]);
626 if (items * sizeof(payload[0]) != b.len)
627 return -L4_EIO;
628
629 if (seg + items > max_seg)
630 return -L4_EIO;
631 seg += items;
632
633 for (auto i = 0u; i < items; i++)
634 {
635 auto p = cxx::access_once<l4virtio_block_discard_t>(&payload[i]);
636
637 // Check sector size alignment. Discard sector alignment is not
638 // strictly enforced as it is merely a hint to the driver.
639 if (p.sector % sps != 0)
640 return -L4_EIO;
641 if (p.num_sectors % sps != 0)
642 return -L4_EIO;
643
644 // Convert to the device sector size
645 p.sector /= sps;
646 p.num_sectors /= sps;
647
648 // Check bounds
649 if (p.num_sectors > sectors)
650 return -L4_EIO;
651 if (p.sector > sectors - p.num_sectors)
652 return -L4_EIO;
653
654 if (p.flags & L4VIRTIO_BLOCK_DISCARD_F_RESERVED)
655 return -L4_ENOSYS;
656
657 Inout_block *blk;
658 if (last_blk)
659 {
660 last_blk->next = cxx::make_unique<Inout_block>();
661 blk = last_blk->next.get();
662 }
663 else
664 blk = &preq->blocks;
665
666 blk->sector = p.sector;
667 blk->num_sectors = p.num_sectors;
668
669 if (discard)
670 {
671 if (p.flags & L4VIRTIO_BLOCK_DISCARD_F_UNMAP)
672 return -L4_ENOSYS;
673 if (p.num_sectors > _di.max_discard_sectors)
674 return -L4_EIO;
675 }
676 else
677 {
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)
682 return -L4_EIO;
683 }
684
685 last_blk = blk;
686 }
687 }
688
689 return L4_EOK;
690 }
691
692 template <typename T = Device_type>
693 int discard_cmd_request(Pending_cmd_request *, long)
694 { return -L4_EIO; }
695
696 template <typename T = Device_type>
697 auto discard_cmd_request(Pending_cmd_request *preq, int)
698 -> decltype(((T*)0)->discard_info(), int())
699 {
700 auto *req = preq->request.get();
701 bool discard = (req->header().type == L4VIRTIO_BLOCK_T_DISCARD);
702
703 int res = _device->discard(
704 0, preq->blocks,
705 [this, preq](int error, l4_size_t sz) { task_finished(preq, error, sz); },
706 discard);
707
708 // request successfully submitted to device
709 if (res >= 0)
710 _in_flight++;
711
712 return res;
713 }
714
715 // only use on errors that are not busy
716 void handle_request_error(int error, Generic_pending_request *pending)
717 {
718 auto trace = Dbg::trace("virtio");
719
720 if (error == -L4_ENOSYS)
721 {
722 trace.printf("Unsupported operation.\n");
723 finalize_request(cxx::move(pending->request), 0,
725 }
726 else
727 {
728 trace.printf("Got IO error: %d\n", error);
729 finalize_request(cxx::move(pending->request), 0, L4VIRTIO_BLOCK_S_IOERR);
730 }
731 }
732
733protected:
734 L4::Cap<L4::Irq> _device_notify_irq;
735 std::function<void(bool)> _client_invalidate_cb;
736 std::function<void()> _client_idle_cb;
737 unsigned _numds;
738 Shutdown_type _shutdown_state;
739 cxx::Ref_ptr<Device_type> _device;
740 Device_discard_feature::Discard_info _di;
741
742 L4virtio::Svr::Block_features _negotiated_features;
743
744 unsigned _in_flight;
745};
746
747} //name space
l4_uint64_t Dma_addr
Data type for DMA addresses.
Definition dma_space:57
Direction
Direction of the DMA transfers.
Definition dma_space:67
@ To_device
device reads the memory
Definition dma_space:69
@ None
device is coherently connected to the memory
Definition dma_space:71
@ From_device
device writes to the memory
Definition dma_space:70
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)
Definition virtio-block:422
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})
Definition virtio-block:448
void set_discard(l4_uint32_t max_discard_sectors, l4_uint32_t max_discard_seg, l4_uint32_t discard_sector_alignment)
Definition virtio-block:402
void finalize_request(cxx::unique_ptr< Request > req, unsigned sz, l4_uint8_t status=L4VIRTIO_BLOCK_S_OK)
Definition virtio-block:488
virtual L4::Cap< L4::Irq > device_notify_irq() const
Definition l4virtio:867
unsigned int l4_size_t
Unsigned size type.
Definition l4int.h:22
unsigned long l4_addr_t
Address type.
Definition l4int.h:34
unsigned int l4_uint32_t
Unsigned 32bit value.
Definition l4int.h:29
unsigned long long l4_uint64_t
Unsigned 64bit value.
Definition l4int.h:31
int l4_cache_flush_data(unsigned long start, unsigned long end) L4_NOTHROW
Cache flush a range; writes back to PoC.
Definition cache.h:76
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.
Definition cache.h:68
int l4_cache_inv_data(unsigned long start, unsigned long end) L4_NOTHROW
Cache invalidate a range; might write back to PoC.
Definition cache.h:84
@ L4_ENOSYS
No sys.
Definition err.h:52
@ L4_EBUSY
Object currently busy, try later.
Definition err.h:43
@ L4_EIO
I/O error.
Definition err.h:36
@ L4_EOK
Ok.
Definition err.h:33
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.
Definition error_helper:149
T access_once(T const *a)
Read the value at an address at most once.
Definition utils:40
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.
Definition ipc_epiface:325
char const * message() const
Get a human readable description of the error code.
Definition virtio:430
#define l4_assert(expr)
Low-level assert.
Definition assert.h:32
Common task related definitions.
Implementation of a list of unique-ptr-managed objects.