#pragma once #include "config.h" #include "ealanos/memory/hamstraaja.h" #include "lwip/pbuf.h" #include "mx/memory/global_heap.h" #include #include #include #include #include #include /* B-link tree includes */ #include #include /* lwIP wrapper for Genode's NIC session */ #include #include #include /* Genode includes */ #include #include #include /* MxTasking includes*/ #include #include #include #include #include /* lwIP includes */ namespace Lwip { extern "C" { #include #include #include } } namespace application::echo_server::network { class ResponseHandler; class RequestTask; class ReceiveTask; using namespace Lwip; class Server { public: enum states { NONE = 0, ACCEPTED, RECEIVED, CLOSING, CLOSED }; struct Wakeup_scheduler : Mxip::Nic_netif::Wakeup_scheduler { Mxip::Nic_netif *nic{nullptr}; void schedule_nic_server_wakeup() override { nic->wakeup_nic_server(); } void set_nic(Mxip::Nic_netif *nic) { this->nic = nic; } Wakeup_scheduler() = default; } _wakeup_scheduler; struct state { std::uint8_t state; std::uint8_t retries; struct tcp_pcb *pcb; struct pbuf *p; struct pbuf *tx; std::uint16_t channel_id; std::uint64_t id; }; Server(Libc::Env &env, std::uint64_t port, std::uint16_t count_channels, Timer::Connection &timer, Genode::Heap &alloc, Ealan::Memory::Hamstraaja<128, 4*4096> *talloc, Mxip::Nic_netif::Payload_allocator *palloc) noexcept; ~Server(); [[nodiscard]] std::uint16_t port() const noexcept { return _port; } void stop() noexcept; void send(struct Server::state *s, std::string &&message); bool listen(); void parse(struct Server::state *s, std::string &message); [[nodiscard]] bool is_running() const noexcept { return _is_running; } static void tcp_send(struct tcp_pcb *tpcb, struct state *s) { using namespace Lwip; struct pbuf *ptr; err_t rc = ERR_OK; if (!s) return; while ((rc == ERR_OK) && (s->tx != nullptr) /* && (s->tx->len <= tcp_sndbuf(tpcb) */) { ptr = s->tx; // Genode::log("Sending response"); rc = tcp_write(tpcb, ptr->payload, ptr->len, 1); if (rc == ERR_OK) { std::uint16_t plen; plen = ptr->len; s->tx = ptr->next; if (s->tx != nullptr) { pbuf_ref(s->tx); } tcp_output(tpcb); pbuf_free(ptr); } else if (rc == ERR_MEM) { Genode::warning("Low on memory. Defering to poll()"); s->tx = ptr; } else { Genode::warning("An error ", static_cast(rc), " occured."); } } } static void tcpbtree_close(struct tcp_pcb *tpcb, struct state *s) { if (!s || s->pcb != tpcb) { Genode::error("Tried closing connection with invalid session state"); return; } tcp_arg(tpcb, NULL); tcp_sent(tpcb, NULL); tcp_recv(tpcb, NULL); tcp_poll(tpcb, NULL, 0); tcp_err(tpcb, nullptr); Genode::log("Unregistered handlers"); tcp_close(tpcb); Server::tcp_free(s); } /* tcp_recv */ static err_t _handle_tcp_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err); /* tcp_err */ static void _handle_tcp_error(void *arg, err_t err) { struct state *s; LWIP_UNUSED_ARG(err); s = static_cast(arg); Server::tcp_free(s); } /* tcp_poll */ static err_t _handle_tcp_poll(void *arg, struct tcp_pcb *tpcb); /* tcp_sent */ static err_t _handle_tcp_sent(void *arg, struct tcp_pcb *tpcb, std::uint16_t len); /* helper function for free */ static void tcp_free(struct state *s) { // Genode::log("Freeing state obj s=", s); if (s) { if (s->p) pbuf_free(s->p); if (s->tx) pbuf_free(s->tx); Genode::log("Freeing state object ", s); mx::memory::GlobalHeap::free(s); // mem_free(s); Genode::log("Freed state object"); } } static Server *get_instance() { return _myself; } static void free_handler_task(std::uint16_t core_id, void* task) { mx::memory::GlobalHeap::free(task); } static void free_task(void* task) { mx::memory::GlobalHeap::free(task); } private: static Server *_myself; const std::uint64_t _port; struct tcp_pcb *_socket; Libc::Env &_env; std::array _buffer; static ReceiveTask *_receive_tasks; alignas(64) bool _is_running = true; alignas(64) std::atomic_uint64_t _next_worker_id{0U}; const std::uint16_t _count_channels; std::uint16_t add_client(tcp_pcb *client_socket); /* Genode environment for NIC session */ Genode::Attached_rom_dataspace _config; Genode::Heap &_alloc; Timer::Connection &_timer; /* lwIP network device (NIC session wrapper) */ Mxip::Nic_netif _netif; /************************************************ * lwIP callback API: TCP callback functions ************************************************/ /* tcp_accept */ static err_t _handle_tcp_connect(void *arg, struct tcp_pcb *newpcb, err_t err); /* helper function for close() */ }; class alignas(64) ReceiveTask final : public mx::tasking::TaskInterface { public: ReceiveTask(Server::state *state, pbuf *pb, std::size_t len) : _state(state), _payload(pb), _length(len) {} mx::tasking::TaskResult execute(std::uint16_t core_id, std::uint16_t channel_id) override; private: Server::state *_state; pbuf *_payload; std::size_t _length; }; } // namespace mx::io::network