LIBDMA: Added DMA Manager domain.
authorReto Achermann <acreto@student.ethz.ch>
Mon, 21 Jul 2014 16:39:09 +0000 (18:39 +0200)
committerStefan Kaestle <stefan.kaestle@inf.ethz.ch>
Wed, 20 Aug 2014 21:39:54 +0000 (23:39 +0200)
14 files changed:
errors/errno.fugu
hake/symbolic_targets.mk
if/Hakefile
if/dma.if
if/dma_mgr.if [new file with mode: 0644]
include/dma/dma_manager_client.h [new file with mode: 0644]
lib/dma/dma_mgr_client.c [new file with mode: 0644]
lib/dma/include/dma_client_internal.h [new file with mode: 0644]
usr/device_managers/dma/Hakefile [new file with mode: 0644]
usr/device_managers/dma/debug.h [new file with mode: 0644]
usr/device_managers/dma/dma_mgr.c [new file with mode: 0644]
usr/device_managers/dma/dma_mgr.h [new file with mode: 0644]
usr/device_managers/dma/dma_mgr_drivers.c [new file with mode: 0644]
usr/device_managers/dma/dma_mgr_svc.c [new file with mode: 0644]

index 641558f..bc405da 100755 (executable)
@@ -1062,7 +1062,7 @@ errors xeon_phi XEON_PHI_ERR_ {
 };
 
 errors dma DMA_ERR_ {
-       failure PCI_ADDRESS           "The PCI address of the device is not as expected",
+    failure PCI_ADDRESS           "The PCI address of the device is not as expected",
     failure DEVICE_UNSUPPORTED    "Device ID not supported /  wrong configuration",
     failure DEVICE_IDLE           "The device is idle, no transfers finished",
     failure ARG_INVALID           "Supplied argument was not valid",
@@ -1076,7 +1076,8 @@ errors dma DMA_ERR_ {
     failure MEM_OVERLAP           "The memory regions overlap",
     failure MEM_NOT_REGISTERED    "The memory region was not registered",
     failure MEM_OUT_OF_RANGE      "Memory region is out of supported range",
-    failure SVC_REJECT           "Service request was rejected",
+    failure SVC_REJECT             "Service request was rejected",
     failure SVC_RESOURCES         "No resources to handle the service",
+    failure SVC_VOID              "There is no service that could serve the request",
        
 };
index 70612c3..f61f0fc 100644 (file)
@@ -181,7 +181,9 @@ MODULES_x86_64= \
        sbin/xeon_phi_test \
        sbin/virtio_blk_host \
        sbin/virtio_blk \
-       sbin/ioat_dma
+       sbin/ioat_dma \
+       sbin/dma_test \
+       sbin/dma_mgr 
 #      sbin/block_server \
 #      sbin/block_server_client \
 #      sbin/bs_user \
index dc0f4cd..f2800f8 100644 (file)
@@ -21,6 +21,7 @@
                "bulkbench",
                "diskd",
                "dma",
+               "dma_mgr",
                "e10k",
                "ehci",
                "net_queue_manager",
index 29438f5..f6af00d 100644 (file)
--- a/if/dma.if
+++ b/if/dma.if
@@ -1,7 +1,3 @@
-/** \file
- *  \brief Xeon Phi Service Interface
- */
-
 /*
  * Copyright (c) 2012, ETH Zurich.
  * All rights reserved.
diff --git a/if/dma_mgr.if b/if/dma_mgr.if
new file mode 100644 (file)
index 0000000..3c3a59a
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2012, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+interface dma_mgr "DMA Manager Interface" {
+    
+    /**
+     * \brief registers a driver with the DMA device manager
+     * 
+     * \param IN  mem_low     low end of the supported memory range
+     * \param IN  mem_high    high end of the supported memory range
+     * \param IN  numa_node   numa node the domain is running on
+     * \param IN  type        the driver type
+     * \param IN  svc_iref    iref of the exported service
+     * \param OUT msgerr      the result of the operation
+     */
+    rpc register_driver(in  uint64 mem_low, 
+                        in  uint64 mem_high, 
+                        in  uint8 numa_node, 
+                        in  uint8 type,
+                        in  iref svc_iref, 
+                        out errval msgerr);
+    
+    /**
+     * \brief queries the DMA device manager if there is a service with the
+     *        needed memory range
+     * 
+     * \param IN  base        low end of the supported memory range
+     * \param IN  size        size of the memory region
+     * \param IN  numa_node   numa node the domain is running on
+     * \param OUT msgerr      the result of the operation
+     * \param OUT driver_info information about the driver
+     */
+    rpc lookup_driver(in uint64 base,
+                      in uint64 size,
+                      in uint8 numa,
+                      out errval msgerr,
+                      out uint64 mem_low, 
+                      out uint64 mem_high, 
+                      out uint8 numa_node, 
+                      out uint8 type,
+                      out iref svc_iref);    
+};
\ No newline at end of file
diff --git a/include/dma/dma_manager_client.h b/include/dma/dma_manager_client.h
new file mode 100644 (file)
index 0000000..cdaf346
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2014 ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#ifndef LIB_DMA_MGR_CLIENT_H
+#define LIB_DMA_MGR_CLIENT_H
+
+#include <dma/dma.h>
+
+/// name of the DMA manager service
+#define DMA_MGR_SVC_NAME "dma_mgr_svc"
+
+struct dma_mgr_driver_info
+{
+    lpaddr_t mem_low;
+    lpaddr_t mem_high;
+    iref_t iref;
+    dma_dev_type_t type;
+    uint8_t numa_node;
+};
+
+/**
+ * \brief initializes the connection to the DMA manager service in an eager way
+ *
+ * \returns SYS_ERR_OK on success
+ *          errval on error
+ */
+errval_t dma_manager_client_init(void);
+
+/**
+ * \brief registers a DMA driver with the DMA driver manager
+ *
+ * \param mem_low   lower end of the supported memory range
+ * \param mem_high  upper end of the supported memory range
+ * \param type      DMA device type
+ * \param iref      the iref of the exported DMA service
+ *
+ * \returns SYS_ERR_OK on success
+ *          DMA_ERR_* on failure
+ */
+errval_t dma_manager_register_driver(lpaddr_t mem_low,
+                                     lpaddr_t mem_high,
+                                     uint8_t type,
+                                     iref_t iref);
+
+/**
+ * \brief queries the DMA manager for a suitable DMA driver for a address, size
+ *        pair
+ *
+ * \param addr      address of the transfer
+ * \param size      size of the desired transfer
+ * \param info      returns the driver info
+ *
+ * \returns SYS_ERR_OK on success
+ *          DMA_ERR_* on failure
+ */
+errval_t dma_manager_lookup_driver(lpaddr_t addr,
+                                   lpaddr_t size,
+                                   struct dma_mgr_driver_info *info);
+
+#endif  /* LIB_DMA_CLIENT_H */
diff --git a/lib/dma/dma_mgr_client.c b/lib/dma/dma_mgr_client.c
new file mode 100644 (file)
index 0000000..c15c764
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2014 ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#include <string.h>
+#include <barrelfish/barrelfish.h>
+#include <barrelfish/nameservice_client.h>
+
+#include <dma_internal.h>
+#include <dma/dma_manager_client.h>
+#include <debug.h>
+
+#include <if/dma_mgr_defs.h>
+#include <if/dma_mgr_rpcclient_defs.h>
+
+/// DMA manager RPC client
+static struct dma_mgr_rpc_client dma_mgr_client;
+
+/// connected flag
+uint8_t dma_mgr_connected = 0x0;
+
+/// state for binding
+struct dma_mgr_bind_st
+{
+    uint8_t bound;
+    errval_t err;
+};
+
+/*
+ * ---------------------------------------------------------------------------
+ * Service Binding
+ * ---------------------------------------------------------------------------
+ */
+
+static void bind_cb(void *st,
+                    errval_t err,
+                    struct dma_mgr_binding *b)
+{
+    assert(st);
+    struct dma_mgr_bind_st *bindst = st;
+
+    bindst->err = err;
+
+    if (err_is_fail(err)) {
+        DMAMGR_DEBUG("connect: connection to {%s} failed.\n", DMA_MGR_SVC_NAME);
+        bindst->bound = 0x1;
+        return;
+    }
+
+    DMAMGR_DEBUG("connect: connection to {%s} established.\n", DMA_MGR_SVC_NAME);
+
+    bindst->err = dma_mgr_rpc_client_init(&dma_mgr_client, b);
+
+    if (err_is_fail(bindst->err)) {
+        DMAMGR_DEBUG("connect: RPC client init failed.\n");
+    }
+
+    bindst->bound = 0x1;
+}
+
+static errval_t dma_manager_connect(void)
+{
+    errval_t err;
+    iref_t iref;
+
+    DMAMGR_DEBUG("connect: looking up service name {%s}\n", DMA_MGR_SVC_NAME);
+    err = nameservice_blocking_lookup(DMA_MGR_SVC_NAME, &iref);
+    if (err_is_fail(err)) {
+        return err;
+    }
+
+    struct dma_mgr_bind_st st = {
+        .bound = 0x0
+    };
+
+    DMAMGR_DEBUG("connect: binding to iref [%"PRIxIREF"]\n", iref);
+    struct waitset *ws = get_default_waitset();
+    err = dma_mgr_bind(iref, bind_cb, &st, ws, IDC_BIND_FLAGS_DEFAULT);
+    if (err_is_fail(err)) {
+        return err;
+    }
+
+    while (st.bound == 0x0) {
+        messages_wait_and_handle_next();
+    }
+
+    if (err_is_ok(st.err)) {
+        dma_mgr_connected = 0x1;
+    }
+
+    return st.err;
+}
+
+/*
+ * ============================================================================
+ * Public Interface
+ * ============================================================================
+ */
+
+/**
+ * \brief initializes the connection to the DMA manager service in an eager way
+ *
+ * \returns SYS_ERR_OK on success
+ *          errval on error
+ */
+errval_t dma_manager_client_init(void)
+{
+    errval_t err;
+
+    if (dma_mgr_connected == 0) {
+        err = dma_manager_connect();
+        if (err_is_fail(err)) {
+            return err;
+        }
+    }
+    return SYS_ERR_OK;
+}
+
+/**
+ * \brief registers a DMA driver with the DMA driver manager
+ *
+ * \param mem_low   lower end of the supported memory range
+ * \param mem_high  upper end of the supported memory range
+ * \param type      DMA device type
+ * \param iref      the iref of the exported DMA service
+ *
+ * \returns SYS_ERR_OK on success
+ *          DMA_ERR_* on failure
+ */
+errval_t dma_manager_register_driver(lpaddr_t mem_low,
+                                     lpaddr_t mem_high,
+                                     uint8_t type,
+                                     iref_t iref)
+{
+    errval_t err;
+    errval_t msgerr;
+
+    if (dma_mgr_connected == 0) {
+        err = dma_manager_connect();
+        if (err_is_fail(err)) {
+            return err;
+        }
+    }
+
+    DMAMGR_DEBUG("register driver: [%016lx, %016lx, %"PRIxIREF"]\n", mem_low,
+                 mem_high, iref);
+
+    //XXX need to figure this out otherwise
+    uint8_t numa_node = (disp_get_core_id() >= 20);
+
+    err = dma_mgr_client.vtbl.register_driver(&dma_mgr_client, mem_low, mem_high,
+                                              numa_node, type, iref, &msgerr);
+    if (err_is_fail(err)) {
+        DMAMGR_DEBUG("register driver: RPC failed %s\n", err_getstring(err));
+        return err;
+    }
+
+    return msgerr;
+}
+
+/**
+ * \brief queries the DMA manager for a suitable DMA driver for a address, size
+ *        pair
+ *
+ * \param addr      address of the transfer
+ * \param size      size of the desired transfer
+ * \param info      returns the driver info
+ *
+ * \returns SYS_ERR_OK on success
+ *          DMA_ERR_* on failure
+ */
+errval_t dma_manager_lookup_driver(lpaddr_t addr,
+                                   lpaddr_t size,
+                                   struct dma_mgr_driver_info *info)
+{
+    errval_t err, msgerr;
+
+    if (dma_mgr_connected == 0) {
+        err = dma_manager_connect();
+        if (err_is_fail(err)) {
+            return err;
+        }
+    }
+
+    DMAMGR_DEBUG("lookup driver: [%016lx, %016lx]\n", addr, size);
+
+    uint8_t numa_node = (disp_get_core_id() >= 20);
+
+    err = dma_mgr_client.vtbl.lookup_driver(&dma_mgr_client, addr, size, numa_node,
+                                            &msgerr, &info->mem_low, &info->mem_high,
+                                            &info->numa_node, (uint8_t*)&info->type,
+                                            &info->iref);
+    if (err_is_fail(err)) {
+        DMAMGR_DEBUG("register driver: RPC failed %s\n", err_getstring(err));
+        return err;
+    }
+
+    return msgerr;
+}
+
diff --git a/lib/dma/include/dma_client_internal.h b/lib/dma/include/dma_client_internal.h
new file mode 100644 (file)
index 0000000..e208965
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2014 ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#ifndef DMA_CLIENT_INTERNAL_H
+#define DMA_CLIENT_INTERNAL_H
+
+#include <dma/dma_client.h>
+
+
+
+#endif /* DMA_CLIENT_INTERNAL_H */
diff --git a/usr/device_managers/dma/Hakefile b/usr/device_managers/dma/Hakefile
new file mode 100644 (file)
index 0000000..47568c3
--- /dev/null
@@ -0,0 +1,23 @@
+--------------------------------------------------------------------------
+-- Copyright (c) 2014, ETH Zurich.
+-- All rights reserved.
+--
+-- This file is distributed under the terms in the attached LICENSE file.
+-- If you do not find this file, copies can be found by writing to:
+-- ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
+--
+-- Hakefile for /usr/device_managers/dma_mgr
+--
+--------------------------------------------------------------------------
+
+[ build application { 
+    target = "dma_mgr",
+    cFiles = [ 
+        "dma_mgr.c",
+        "dma_mgr_svc.c",
+        "dma_mgr_drivers.c"
+    ],
+    flounderBindings = [ "dma_mgr" ],
+    architectures = [ "x86_64"]
+  }  
+]
diff --git a/usr/device_managers/dma/debug.h b/usr/device_managers/dma/debug.h
new file mode 100644 (file)
index 0000000..a0d28fb
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2014 ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#ifndef DMA_MGR_DEBUG_H_
+#define DMA_MGR_DEBUG_H_
+
+
+/*
+ * Debug output switches
+ */
+#define DEBUG_ENABLED     1
+#define DEBUG_SVC_ENABLED 1
+#define DEBUG_DS_ENABLED 1
+
+
+/*
+ * --------------------------------------------------------------------------
+ * Debug output generation
+ */
+#if DEBUG_ENABLED
+#define DEBUGPRINT(x...) debug_printf(x)
+#else
+#define DEBUGPRINT(x... )
+#endif
+#if DEBUG_SVC_ENABLED
+#define SVC_DEBUG(x...) DEBUGPRINT("svc | " x)
+#else
+#define SVC_DEBUG(x...)
+#endif
+#if DEBUG_DS_ENABLED
+#define DS_DEBUG(x...)  DEBUGPRINT("ds  | " x)
+#else
+#define DS_DEBUG(x...)
+#endif
+
+
+#define ERRPRINT(x...) debug_printf(x)
+#define SVC_ERR(x...) ERRPRINT("svc | ERROR: " x)
+#define DS_ERR(x...)  ERRPRINT("ds  | ERROR: " x)
+
+#endif /* DMA_MGR_DEBUG_H_ */
diff --git a/usr/device_managers/dma/dma_mgr.c b/usr/device_managers/dma/dma_mgr.c
new file mode 100644 (file)
index 0000000..1f871bc
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2014, ETH Zurich. All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+/*
+ *
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+
+#include <barrelfish/barrelfish.h>
+
+#include "dma_mgr.h"
+#include "debug.h"
+
+#define BUFFER_SIZE (1<<22)
+
+
+int main(int argc,
+         char *argv[])
+{
+    errval_t err;
+
+    debug_printf("DMA Manager started\n");
+
+    err = driver_store_init();
+    if (err_is_fail(err)) {
+        USER_PANIC_ERR(err, "could not initialize the driver store\n");
+    }
+
+    err = dma_mgr_svc_start();
+    if (err_is_fail(err)) {
+        USER_PANIC_ERR(err, "could not start the service\n");
+    }
+
+    while(1) {
+        messages_wait_and_handle_next();
+    }
+
+    debug_printf("DMA Manager terminated\n");
+
+    return 0;
+}
+
diff --git a/usr/device_managers/dma/dma_mgr.h b/usr/device_managers/dma/dma_mgr.h
new file mode 100644 (file)
index 0000000..1cc0e40
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2014, ETH Zurich. All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#ifndef DMA_MGR_H_
+#define DMA_MGR_H_
+
+struct dma_mgr_driver_info;
+
+errval_t driver_store_init(void);
+
+errval_t driver_store_insert(lpaddr_t mem_low,
+                             lpaddr_t mem_high,
+                             uint8_t numa_node,
+                             uint8_t type,
+                             iref_t iref);
+
+
+errval_t driver_store_lookup(lpaddr_t mem_low,
+                             size_t size,
+                             uint8_t numa_node,
+                             struct dma_mgr_driver_info **info);
+
+/**
+ * \brief initializes the DMA manager service service
+ *
+ * \param svc_name  The name of the service for nameservice registration
+ * \param cb        Callback function pointers
+ *
+ * \returns SYS_ERR_OK on success
+ *          errval on error
+ */
+errval_t dma_mgr_svc_start(void);
+
+#endif  /* DMA_MGR_H_ */
diff --git a/usr/device_managers/dma/dma_mgr_drivers.c b/usr/device_managers/dma/dma_mgr_drivers.c
new file mode 100644 (file)
index 0000000..88397ff
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2014, ETH Zurich. All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+/*
+ *
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+
+#include <barrelfish/barrelfish.h>
+
+#include <dma/dma_manager_client.h>
+
+#include "dma_mgr.h"
+#include "debug.h"
+
+
+
+struct dma_service
+{
+    struct dma_mgr_driver_info info;
+    struct dma_service *prev;
+    struct dma_service *next;
+};
+
+static struct dma_service *dma_services = NULL;
+
+static void service_insert(struct dma_service *svc)
+{
+    if (dma_services == NULL) {
+        svc->next = NULL;
+        svc->prev = NULL;
+        dma_services = svc;
+        return;
+    }
+
+    struct dma_service *current = dma_services;
+    while(current) {
+        if (current->info.mem_low > svc->info.mem_low) {
+            if (current->prev) {
+                svc->prev = current->prev;
+                current->prev->next = svc;
+            } else {
+                svc->prev = NULL;
+                dma_services = svc;
+            }
+            svc->next = current;
+            current->prev = svc;
+            break;
+        }
+        current = current->next;
+    }
+}
+
+static inline uint8_t get_diff(uint8_t a, uint8_t b)
+{
+    if (a > b) {
+        return a-b;
+    } else {
+        return b-a;
+    }
+}
+
+static struct dma_service *service_lookup(lpaddr_t mem_low,
+                                          size_t size,
+                                          uint8_t numa_node)
+{
+    struct dma_service *current = dma_services;
+    struct dma_service *best = NULL;
+    while(current) {
+        if (current->info.mem_low <= mem_low) {
+            if(current->info.mem_high >= (mem_low + size)) {
+                /* we have a match */
+                if (best == NULL) {
+                    best = current;
+                } else {
+                    uint8_t best_numa_diff = get_diff(numa_node, best->info.numa_node);
+                    uint8_t curr_numa_diff = get_diff(numa_node, current->info.numa_node);
+                    if (curr_numa_diff < best_numa_diff) {
+                        best = current;
+                    }
+                }
+            }
+        }
+        current = current->next;
+    }
+    return best;
+}
+
+/*
+ * ============================================================================
+ *
+ * ============================================================================
+ */
+
+errval_t driver_store_init(void)
+{
+
+    return SYS_ERR_OK;
+}
+
+errval_t driver_store_insert(lpaddr_t mem_low,
+                             lpaddr_t mem_high,
+                             uint8_t numa_node,
+                             uint8_t type,
+                             iref_t iref)
+{
+    struct dma_service *driver = calloc(1, sizeof(*driver));
+    if (driver == NULL) {
+        return LIB_ERR_MALLOC_FAIL;
+    }
+    driver->info.mem_low = mem_low;
+    driver->info.mem_high = mem_high;
+    driver->info.numa_node = numa_node;
+    driver->info.type = type;
+    driver->info.iref = iref;
+
+    service_insert(driver);
+
+    return SYS_ERR_OK;
+}
+
+
+errval_t driver_store_lookup(lpaddr_t mem_low,
+                             size_t size,
+                             uint8_t numa_node,
+                             struct dma_mgr_driver_info **info)
+{
+    struct dma_service *svc = service_lookup(mem_low, size, numa_node);
+    if (svc == NULL) {
+        return DMA_ERR_SVC_VOID;
+    }
+    *info = &svc->info;
+
+    return SYS_ERR_OK;
+}
diff --git a/usr/device_managers/dma/dma_mgr_svc.c b/usr/device_managers/dma/dma_mgr_svc.c
new file mode 100644 (file)
index 0000000..bf88e62
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+ * Copyright (c) 2014, ETH Zurich. All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+/*
+ *
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+
+#include <barrelfish/barrelfish.h>
+#include <barrelfish/nameservice_client.h>
+
+#include <dma/dma_manager_client.h>
+
+#include <if/dma_mgr_defs.h>
+
+#include "dma_mgr.h"
+#include "debug.h"
+
+/**
+ * enumration of all possible states of the service exportation process
+ */
+enum svc_state
+{
+    SVC_STATE_EXPORTING,
+    SVC_STATE_EXPORT_OK,
+    SVC_STATE_EXPORT_FAIL,
+    SVC_STATE_NS_REGISTERING,
+    SVC_STATE_NS_REGISTER_OK,
+    SVC_STATE_NS_REGISTER_FAIL,
+    SVC_STATE_RUNNING
+};
+
+/// represents the current state of the exporting process
+static enum svc_state dma_svc_state = SVC_STATE_EXPORTING;
+
+/// our own iref of the exported service
+static iref_t svc_iref;
+
+/*
+ * ----------------------------------------------------------------------------
+ * Reply State Cache
+ * ----------------------------------------------------------------------------
+ */
+struct svc_reply_st
+{
+    void (*op)(void *arg);          ///<
+    struct svc_reply_st *next;      ///<
+    struct dma_mgr_binding *b;      ///<
+    errval_t err;                   ///<
+
+    /* union of arguments */
+    union
+    {
+        struct dma_mgr_driver_info *lookup;
+    } args;
+};
+
+static struct svc_reply_st *dma_reply_st_cache = NULL;
+
+static struct svc_reply_st *reply_st_alloc(void)
+{
+    if (dma_reply_st_cache) {
+        struct svc_reply_st *st = dma_reply_st_cache;
+        dma_reply_st_cache = dma_reply_st_cache->next;
+        return st;
+    }
+    return calloc(1, sizeof(struct svc_reply_st));
+}
+
+static inline void reply_st_free(struct svc_reply_st *st)
+{
+    st->next = dma_reply_st_cache;
+    dma_reply_st_cache = st;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ * Reply send queue
+ * ----------------------------------------------------------------------------
+ */
+
+static struct
+{
+    struct svc_reply_st *head;
+    struct svc_reply_st *tail;
+} svc_reply_txq;
+
+static void send_reply_cont(void *a)
+{
+    struct svc_reply_st *st = (struct svc_reply_st *) a;
+
+    /* we are likely to be able to send the reply so send it */
+    assert(svc_reply_txq.head == st);
+
+    svc_reply_txq.head->op(st);
+}
+
+static void send_reply_done(void *a)
+{
+    struct svc_reply_st *st = (struct svc_reply_st *) a;
+
+    /* callback when the message has been sent */
+    if (svc_reply_txq.head == NULL) {
+        reply_st_free((struct svc_reply_st *) a);
+        return;
+    }
+    assert(svc_reply_txq.head == st);
+
+    svc_reply_txq.head = svc_reply_txq.head->next;
+    if (svc_reply_txq.head == NULL) {
+        svc_reply_txq.tail = NULL;
+    } else {
+        struct svc_reply_st *next = svc_reply_txq.head;
+        /* it should not matter if we already registered */
+        svc_reply_txq.head->b->register_send(svc_reply_txq.head->b,
+                                             get_default_waitset(),
+                                             MKCONT(send_reply_cont, next));
+    }
+    reply_st_free(st);
+}
+
+static errval_t send_reply_enqueue(struct svc_reply_st *st)
+{
+    st->next = NULL;
+
+    if (svc_reply_txq.tail == NULL) {
+        svc_reply_txq.head = st;
+    } else {
+        svc_reply_txq.tail->next = st;
+    }
+    svc_reply_txq.tail = st;
+
+    /* register if queue was empty i.e. head == tail */
+    if (svc_reply_txq.tail == svc_reply_txq.head) {
+        return st->b->register_send(st->b, get_default_waitset(),
+                                    MKCONT(send_reply_cont, NULL));
+    }
+
+    return SYS_ERR_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ * Driver Registration
+ * ----------------------------------------------------------------------------
+ */
+
+static void svc_register_response_tx(void *a)
+{
+    errval_t err;
+
+    struct svc_reply_st *st = a;
+
+    err = dma_mgr_register_driver_response__tx(st->b, MKCONT(send_reply_done, a),
+                                               st->err);
+    switch (err_no(err)) {
+        case SYS_ERR_OK:
+            break;
+        case FLOUNDER_ERR_TX_BUSY:
+            err = send_reply_enqueue(st);
+            if (err_is_fail(err)) {
+                USER_PANIC_ERR(err, "could not send reply");
+            }
+            break;
+        default:
+            USER_PANIC_ERR(err, "could not send reply");
+            break;
+    }
+}
+
+static void svc_register_call_rx(struct dma_mgr_binding *_binding,
+                                 uint64_t mem_low,
+                                 uint64_t mem_high,
+                                 uint8_t numa_node,
+                                 uint8_t type,
+                                 iref_t iref)
+{
+    SVC_DEBUG("register call: [%016lx, %016lx]\n", mem_low, mem_high);
+
+    struct svc_reply_st *state = reply_st_alloc();
+    assert(state);
+
+    state->b = _binding;
+    state->op = svc_register_response_tx;
+
+    state->err = driver_store_insert(mem_low, mem_high, numa_node, type, iref);
+
+    svc_register_response_tx(state);
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ * Query for DMA driver service
+ * ----------------------------------------------------------------------------
+ */
+
+static void svc_lookup_response_tx(void *a)
+{
+    errval_t err;
+
+    struct svc_reply_st *st = a;
+
+    struct dma_mgr_driver_info *di = st->args.lookup;
+
+    if (err_is_fail(st->err)) {
+        err = dma_mgr_lookup_driver_response__tx(st->b, MKCONT(send_reply_done, a),
+                                                 st->err, 0, 0, 0, 0, 0);
+    } else {
+        err = dma_mgr_lookup_driver_response__tx(st->b, MKCONT(send_reply_done, a),
+                                                 st->err, di->mem_low, di->mem_high,
+                                                 di->numa_node, di->type, di->iref);
+    }
+
+    switch (err_no(err)) {
+        case SYS_ERR_OK:
+            break;
+        case FLOUNDER_ERR_TX_BUSY:
+            err = send_reply_enqueue(st);
+            if (err_is_fail(err)) {
+                USER_PANIC_ERR(err, "could not send reply");
+            }
+            break;
+        default:
+            USER_PANIC_ERR(err, "could not send reply");
+            break;
+    }
+}
+
+static void svc_lookup_call_rx(struct dma_mgr_binding *_binding,
+                               uint64_t addr,
+                               uint64_t size,
+                               uint8_t numa_node)
+{
+    SVC_DEBUG("lookup call: [%016lx, %016lx]\n", addr, size);
+
+    struct svc_reply_st *st = reply_st_alloc();
+    assert(st);
+
+    st->b = _binding;
+    st->op = svc_lookup_response_tx;
+
+    st->err = driver_store_lookup(addr, size, numa_node, &st->args.lookup);
+
+    svc_lookup_response_tx(st);
+}
+
+struct dma_mgr_rx_vtbl svc_rx_vtbl = {
+    .register_driver_call = svc_register_call_rx,
+    .lookup_driver_call = svc_lookup_call_rx,
+};
+
+/*
+ * ----------------------------------------------------------------------------
+ * Service export and connect handling
+ * ----------------------------------------------------------------------------
+ */
+
+static errval_t svc_connect_cb(void *st,
+                               struct dma_mgr_binding *binding)
+{
+    SVC_DEBUG("New connection to the DMA service\n");
+
+    binding->rx_vtbl = svc_rx_vtbl;
+
+    return SYS_ERR_OK;
+}
+
+static void svc_export_cb(void *st,
+                          errval_t err,
+                          iref_t iref)
+{
+    *((errval_t *) st) = err;
+
+    if (err_is_fail(err)) {
+        dma_svc_state = SVC_STATE_EXPORT_FAIL;
+        return;
+    }
+
+    svc_iref = iref;
+    dma_svc_state = SVC_STATE_EXPORT_OK;
+}
+
+/*
+ * ============================================================================
+ * Public Interface
+ * ============================================================================
+ */
+
+/**
+ * \brief initializes the DMA manager service service
+ *
+ * \param svc_name  The name of the service for nameservice registration
+ * \param cb        Callback function pointers
+ *
+ * \returns SYS_ERR_OK on success
+ *          errval on error
+ */
+errval_t dma_mgr_svc_start(void)
+{
+    errval_t err, export_err;
+
+    SVC_DEBUG("Initializing DMA service...\n");
+
+    svc_reply_txq.head = NULL;
+    svc_reply_txq.tail = NULL;
+
+    err = dma_mgr_export(&export_err, svc_export_cb, svc_connect_cb,
+                         get_default_waitset(),
+                         IDC_EXPORT_FLAGS_DEFAULT);
+    if (err_is_fail(err)) {
+        return err;
+    }
+
+    while (dma_svc_state == SVC_STATE_EXPORTING) {
+        messages_wait_and_handle_next();
+    }
+
+    if (dma_svc_state == SVC_STATE_EXPORT_FAIL) {
+        return export_err;
+    }
+
+    dma_svc_state = SVC_STATE_NS_REGISTERING;
+
+    SVC_DEBUG("Registering service [%s] with iref [0x%"PRIxIREF"]\n",
+              DMA_MGR_SVC_NAME, svc_iref);
+
+    err = nameservice_register(DMA_MGR_SVC_NAME, svc_iref);
+    if (err_is_fail(err)) {
+        dma_svc_state = SVC_STATE_NS_REGISTER_FAIL;
+        return err;
+    }
+
+    dma_svc_state = SVC_STATE_RUNNING;
+
+    SVC_DEBUG("DMA service up and running.\n");
+
+    return SYS_ERR_OK;
+}
+