Devq: e10k backend non VF version working
authorRoni Häcki <roni.haecki@inf.ethz.ch>
Mon, 27 Feb 2017 13:37:42 +0000 (14:37 +0100)
committerRoni Häcki <roni.haecki@inf.ethz.ch>
Mon, 27 Feb 2017 13:37:42 +0000 (14:37 +0100)
Signed-off-by: Roni Häcki <roni.haecki@inf.ethz.ch>

include/devif/backends/net/e10k_devif.h [new file with mode: 0644]
lib/devif/backends/net/e10k/Hakefile [new file with mode: 0644]
lib/devif/backends/net/e10k/debug.h [new file with mode: 0644]
lib/devif/backends/net/e10k/devif_backend_e10k.c [new file with mode: 0644]
lib/devif/backends/net/e10k/e10k_devif_vf.c [new file with mode: 0644]
lib/devif/backends/net/e10k/e10k_devif_vf.h [new file with mode: 0644]
lib/devif/backends/net/e10k/e10k_queue.h [new file with mode: 0644]
lib/devif/backends/net/e10k/helper.c [new file with mode: 0644]
lib/devif/backends/net/e10k/helper.h [new file with mode: 0644]
lib/devif/backends/net/e10k/sleep.c [new file with mode: 0644]
lib/devif/backends/net/e10k/sleep.h [new file with mode: 0644]

diff --git a/include/devif/backends/net/e10k_devif.h b/include/devif/backends/net/e10k_devif.h
new file mode 100644 (file)
index 0000000..d99c780
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2017 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, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+#ifndef E10K_DEVIF_H_
+#define E10K_DEVIF_H_ 1
+
+struct e10k_queue;
+typedef void (*e10k_event_cb_t)(void* q);
+
+errval_t e10k_queue_create(struct e10k_queue** q, e10k_event_cb_t cb, 
+                           bool use_vf, bool interrupts);
+errval_t e10k_queue_destroy(struct e10k_queue* q);
+#endif
diff --git a/lib/devif/backends/net/e10k/Hakefile b/lib/devif/backends/net/e10k/Hakefile
new file mode 100644 (file)
index 0000000..e175988
--- /dev/null
@@ -0,0 +1,28 @@
+--------------------------------------------------------------------------
+-- Copyright (c) 2017, 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, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
+--
+-- Hakefile for lib/backend/net/e10k/
+-- 
+-- Intel e10k backend implementation
+--
+--------------------------------------------------------------------------
+
+  [ 
+    build library { target = "devif_backend_e10k",
+                      cFiles = [ "devif_backend_e10k.c", "e10k_devif_vf.c", 
+                                 "helper.c", "sleep.c"],
+                      flounderBindings = [ "e10k_vf"],
+                      flounderExtraBindings = [ ("e10k_vf", ["rpcclient"])],
+                      flounderDefs = [ "e10k_vf" ],
+                      flounderExtraDefs = [ ("e10k_vf",["rpcclient"]) ],
+                      mackerelDevices = [ "e10k_vf", "e10k"],
+                      addLibraries = libDeps ["pci", "skb", "acpi_client"]
+                  }
+  ]
+
+
diff --git a/lib/devif/backends/net/e10k/debug.h b/lib/devif/backends/net/e10k/debug.h
new file mode 100644 (file)
index 0000000..29ef43d
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2017 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, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#ifndef __DEBUG_H__
+#define __DEBUG_H__
+
+//#define DEBUG_E10K_VF 1
+//#define DEBUG_E10K_QUEUE 1 
+
+/*****************************************************************
+ * Debug printer:
+ *****************************************************************/
+
+#if defined(DEBUG_E10K_VF) 
+#define DEBUG_VF(x...) do { printf("e10k_vf:%s.%d:%s:%d: ", \
+            disp_name(), disp_get_core_id(), __func__, __LINE__); \
+                printf(x);\
+        } while (0)
+
+#else
+#define DEBUG_VF(x...) ((void)0)
+#endif 
+
+#if defined(DEBUG_E10K_QUEUE) 
+#define DEBUG_QUEUE(x...) do { printf("e10k_queue:%s.%d:%s:%d: ", \
+            disp_name(), disp_get_core_id(), __func__, __LINE__); \
+                printf(x);\
+        } while (0)
+#else
+#define DEBUG_QUEUE(x...) ((void)0)
+#endif 
+
+#endif
diff --git a/lib/devif/backends/net/e10k/devif_backend_e10k.c b/lib/devif/backends/net/e10k/devif_backend_e10k.c
new file mode 100644 (file)
index 0000000..5376449
--- /dev/null
@@ -0,0 +1,558 @@
+/*
+ * Copyright (c) 2007-2011, 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, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#include <barrelfish/barrelfish.h>
+#include <barrelfish/nameservice_client.h>
+#include <devif/backends/net/e10k_devif.h>
+#include <devif/queue_interface.h>
+#include <dev/e10k_q_dev.h>
+#include <dev/e10k_dev.h>
+#include <dev/e10k_vf_dev.h>
+#include <skb/skb.h>
+
+
+#include <if/e10k_vf_defs.h>
+#include <if/e10k_vf_rpcclient_defs.h>
+
+#include "e10k_devif_vf.h"
+#include "helper.h"
+#include "e10k_queue.h"
+#include "debug.h"
+
+#define NUM_TX_DESC 2048
+#define NUM_RX_DESC 2048
+
+#define ETHHDR_LEN 14
+#define IPHDR_LEN 20
+#define UDPHDR_LEN 8
+
+/******************************************************************************/
+/* Misc functions */
+
+
+static inline bool buf_use_tcpxsm(uint64_t flags)
+{
+    return (flags & NETIF_TXFLAG_TCPCHECKSUM);
+}
+
+static inline bool buf_use_udpxsm(uint64_t flags)
+{
+    return (flags & NETIF_TXFLAG_UDPCHECKSUM);
+}
+
+static inline bool buf_use_ipxsm(uint64_t flags)
+{
+    return (flags & NETIF_TXFLAG_IPCHECKSUM) ||
+        buf_use_tcpxsm(flags) || buf_use_udpxsm(flags);
+}
+
+static inline uint8_t buf_tcphdrlen(uint64_t flags)
+{
+    return ((flags & NETIF_TXFLAG_TCPHDRLEN_MASK) >>
+        NETIF_TXFLAG_TCPHDRLEN_SHIFT) * 4;
+}
+
+static errval_t update_txtail(struct e10k_queue* q, size_t tail)
+{
+    assert(q->d != NULL);
+
+    if (q->use_vf) {
+        e10k_vf_vftdt_wr(q->d, q->id, tail);
+    } else {
+        e10k_tdt_wr(q->d, q->id, tail);
+    }
+    return SYS_ERR_OK;
+}
+
+static errval_t update_rxtail(struct e10k_queue* q, size_t tail)
+{
+    assert(q->d != NULL);
+
+    if (q->use_vf) {
+        e10k_vf_vfrdt_wr(q->d, q->id, tail);
+    } else {
+        e10k_rdt_1_wr(q->d, q->id, tail);
+    }
+    return SYS_ERR_OK;
+}
+
+
+static struct region_entry* get_region(struct e10k_queue* q, regionid_t rid)
+{
+    struct region_entry* entry = q->regions;
+    while (entry != NULL) {
+        if (entry->rid == rid) {
+            return entry;
+        }
+        entry = entry->next;
+    }
+    return NULL;
+}
+
+
+static errval_t enqueue_tx_buf(struct e10k_queue* q, regionid_t rid,
+                               bufferid_t bid, lpaddr_t base, size_t len, 
+                               uint64_t flags)
+{
+    DEBUG_QUEUE("Enqueueing TX buf \n");
+
+    if (e10k_queue_free_txslots(q) == 0) {
+        DEBUG_QUEUE("e10k_%d: Not enough space in TX ring, not adding buffer\n", 
+                q->id);
+        // TODO better error
+        return SFN_ERR_ENQUEUE;
+    }
+
+    bool last = flags & NETIF_TXFLAG_LAST;
+    bool first = flags & NETIF_TXFLAG_FIRST;
+    // Prepare checksum offload
+    if (buf_use_ipxsm(flags)) {
+        e10k_q_l4_type_t l4t = 0;
+        uint8_t l4len = 0;
+
+        if (buf_use_tcpxsm(flags)) {
+            l4t = e10k_q_tcp;
+            l4len = buf_tcphdrlen(flags);
+        } else if (buf_use_udpxsm(flags)) {
+            l4t = e10k_q_udp;
+            l4len = UDPHDR_LEN;
+        }
+        e10k_queue_add_txcontext(q, 0, ETHHDR_LEN, IPHDR_LEN, l4len, l4t);
+
+           if (q->use_vtd) {
+            // get virtual address of buffer
+            struct region_entry* entry = get_region(q, rid);
+            assert(entry != NULL);
+
+            lpaddr_t addr = 0;
+            addr = (lpaddr_t) entry->virt + (base-entry->phys);
+            e10k_queue_add_txbuf_ctx(q, rid, bid, addr, len, flags, 
+                                     first, last, len, 0, true, l4len !=0);
+           } else {
+
+            e10k_queue_add_txbuf_ctx(q, rid, bid, base, len, flags, 
+                                     first, last, len, 0, true, l4len != 0);
+        }
+    } else {
+        if (q->use_vtd) {
+            // get virtual address of buffer
+            struct region_entry* entry = get_region(q, rid);
+            assert(entry != NULL);
+
+            lpaddr_t addr = 0;
+            addr = (lpaddr_t) entry->virt + (base-entry->phys);
+            e10k_queue_add_txbuf(q, rid, bid, addr, len, flags, 
+                                 first, last, len);
+        } else {
+            e10k_queue_add_txbuf(q, rid, bid, base, len, flags, first, last, len);
+        }
+    }
+    e10k_queue_bump_txtail(q);
+    return SYS_ERR_OK;
+}
+
+
+static errval_t enqueue_rx_buf(struct e10k_queue* q, regionid_t rid,
+                               bufferid_t bid, lpaddr_t base, size_t len, 
+                               uint64_t flags)
+{
+    DEBUG_QUEUE("Enqueueing RX buf \n");
+    // check if there is space
+    if (e10k_queue_free_rxslots(q) == 0) {
+        DEBUG_QUEUE("e10k_%d: Not enough space in RX ring, not adding buffer\n", 
+                q->id);
+        // TODO better error
+        return SFN_ERR_ENQUEUE;
+    }
+
+    if (q->use_vtd) {
+        // get virtual address of buffer
+        struct region_entry* entry = get_region(q, rid);
+        assert(entry != NULL);
+
+        lpaddr_t addr = 0;
+        addr = (lpaddr_t) entry->virt + (base-entry->phys);
+        e10k_queue_add_rxbuf(q, rid, bid, addr, len, flags);
+    } else {
+        e10k_queue_add_rxbuf(q, rid, bid, base, len, flags);
+    }
+
+    DEBUG_QUEUE("before bump tail\n");
+    e10k_queue_bump_rxtail(q);
+    DEBUG_QUEUE("Enqueueing RX buf: terminated\n");
+    return SYS_ERR_OK;
+}
+
+/******************************************************************************/
+/* Queue functions */
+
+static errval_t e10k_enqueue(struct devq* q, regionid_t rid, bufferid_t bid, 
+                             lpaddr_t base, size_t len, uint64_t flags)
+{
+    errval_t err;
+
+
+    struct e10k_queue* queue = (struct e10k_queue*) q;
+    if (flags & NETIF_RXFLAG) {
+        /* can not enqueue receive buffer larger than 2048 bytes */
+        assert(len <= 2048);
+
+        err = enqueue_rx_buf(queue, rid, bid, base, len, flags);
+        if (err_is_fail(err)) {
+            return err;
+        }      
+    } else if (flags & NETIF_TXFLAG) {
+
+        assert(len <= 2048);
+        
+        err = enqueue_tx_buf(queue, rid, bid, base, len, flags);
+        if (err_is_fail(err)) {
+            return err;
+        } 
+    }
+
+    return SYS_ERR_OK;
+}
+
+
+static errval_t e10k_dequeue(struct devq* q, regionid_t* rid, bufferid_t* bid, 
+                             lpaddr_t* base, size_t* len, uint64_t* flags)
+{
+    struct e10k_queue* que = (struct e10k_queue*) q;
+    int last;
+    errval_t err = SYS_ERR_OK;
+
+    if (!e10k_queue_get_rxbuf(que, rid, bid, base, len, flags, &last)) {
+        err = DEVQ_ERR_RX_EMPTY;
+    } else {
+        return SYS_ERR_OK;
+    }
+     
+    if (!e10k_queue_get_txbuf(que, rid, bid, base, len, flags)) {
+        err = DEVQ_ERR_RX_EMPTY;
+    }  else {
+        return SYS_ERR_OK;
+    }
+
+    return err;
+}
+
+static errval_t e10k_register(struct devq* q, struct capref cap, regionid_t rid) 
+{
+    errval_t err;
+    struct e10k_queue* queue = (struct e10k_queue*) q;
+
+    struct frame_identity id;
+    err = invoke_frame_identify(cap, &id);
+    if (err_is_fail(err)) {
+        return err;
+    }
+
+    struct capref cr;
+    err = slot_alloc(&cr);
+    if (err_is_fail(err)) {
+        return err;
+    }
+
+    err = cap_copy(cr, cap);
+    if (err_is_fail(err)) {
+        return err;
+    }
+
+    void* va;
+    err = vspace_map_one_frame_attr(&va, id.bytes, cr, 
+                                    VREGION_FLAGS_READ_WRITE_NOCACHE,
+                                    NULL, NULL);
+    if (err_is_fail(err)) {
+        return err;
+    }
+
+    // keep track of regions since we need the virtual address ...
+    struct region_entry* entry = malloc(sizeof(struct region_entry));
+    entry->rid = rid;
+    entry->cap = cap;
+    entry->phys = id.base;
+    entry->virt = (lvaddr_t)va;
+    entry->size = id.bytes;     
+    entry->next = NULL;
+
+    // linked list of regions
+    struct region_entry* cur = queue->regions;
+    if (cur == NULL) {
+        queue->regions = entry;
+        return SYS_ERR_OK;
+    }
+
+    while (cur->next != NULL) {
+        cur = cur->next;
+    }
+    
+    cur->next = entry;
+
+    return SYS_ERR_OK;
+}
+
+static errval_t e10k_deregister(struct devq* q, regionid_t rid) 
+{
+    return SYS_ERR_OK;
+}
+
+static errval_t e10k_control(struct devq* q, uint64_t cmd, uint64_t value)
+{
+    return SYS_ERR_OK;
+}
+
+
+static errval_t e10k_notify(struct devq* q)
+{
+    return SYS_ERR_OK;
+}
+
+/******************************************************************
+ * Management functions
+ *
+ */
+
+static void bind_cb(void *st, errval_t err, struct e10k_vf_binding *b)
+{
+    struct e10k_queue* q = (struct e10k_queue*) st;
+    assert(err_is_ok(err));
+
+    DEBUG_QUEUE("Sucessfully connected to management interface\n");
+
+    struct e10k_vf_rpc_client *r = malloc(sizeof(*r));
+    assert(r != NULL);
+    err = e10k_vf_rpc_client_init(r, b);
+    assert(err_is_ok(err));
+    b->st = q;
+    q->binding = r;
+    q->bound = true;
+
+}
+
+/** Connect to the management interface */
+static void connect_to_mngif(struct e10k_queue* q)
+{
+    errval_t r;
+    iref_t iref;
+
+    q->bound = false;
+    char name[strlen("e10k_vf") + 2];
+
+    // Build label for interal management service
+    sprintf(name, "%s%u", "e10k_vf", q->pci_function);
+
+    // Connect to service
+    DEBUG_QUEUE("Looking up management interface (%s)\n", name);
+    r = nameservice_blocking_lookup(name, &iref);
+    assert(err_is_ok(r));
+
+    DEBUG_QUEUE("Binding to management interface\n");
+    r = e10k_vf_bind(iref, bind_cb, q, get_default_waitset(),
+                     IDC_BIND_FLAGS_DEFAULT);
+    assert(err_is_ok(r));
+
+    while (!q->bound) {
+        event_dispatch(get_default_waitset());
+    }
+}
+
+/*********************************************************
+ * Queue creation and destruction 
+ */
+
+
+errval_t e10k_queue_destroy(struct e10k_queue* queue)
+{
+    //TODO: do the cleanup
+    return SYS_ERR_OK;
+}
+
+static errval_t map_device_memory(struct e10k_queue* q,
+                                  struct capref regs) 
+{
+
+    struct frame_identity id = {.base = 0, .bytes = 0};
+    errval_t err;
+
+    err = invoke_frame_identify(regs, &id);
+    if (err_is_fail(err)) {
+        return err;
+    }
+
+    void* va;
+    err = vspace_map_one_frame_attr(&va, id.bytes, regs, 
+                                    VREGION_FLAGS_READ_WRITE_NOCACHE, 
+                                    NULL, NULL);
+    if (err_is_fail(err)) {
+        return err;
+    }
+      
+
+    DEBUG_QUEUE("mapped %zu bytes at address %p\n", id.bytes, va);
+    q->d = malloc(sizeof(e10k_t));
+    assert(q->d != NULL);
+    e10k_initialize(q->d, va);
+    return SYS_ERR_OK;
+}
+// TODO mostly cleanup when fail
+errval_t e10k_queue_create(struct e10k_queue** queue, e10k_event_cb_t cb, 
+                           bool use_vf, bool interrupts)
+{
+
+    errval_t err;
+    struct e10k_queue* q;
+    // start VF driver
+    
+    q = malloc(sizeof(struct e10k_queue));    
+    q->pci_function = 0; // TODO allow also function 1
+
+    if (use_vf) {
+        USER_PANIC("NOT YET WORKING \n");
+        // Start VF
+        if (!e10k_vf_started()) {
+            err = e10k_init_vf_driver(q->pci_function, interrupts); 
+            if (err_is_fail(err)) {
+                return err;
+            }
+        }  
+
+        // If i can not create any more queues -> start new VF
+        if (!e10k_vf_can_create_queue()) {
+            err = e10k_init_vf_driver(q->pci_function, interrupts);
+            if (err_is_fail(err)) {
+                return err;
+            }
+        }
+
+        // TODO: VF queues only work with VT-d enabled?
+        err = skb_client_connect();
+        assert(err_is_ok(err));
+        err = skb_execute_query("vtd_enabled(0,_).");
+        if (err_is_fail(err)) {
+            DEBUG_QUEUE("Assume disabled VT-d \n");
+            //q->use_vtd = false;
+        } else {
+            DEBUG_QUEUE("Assume enabled VT-d \n");
+            //q->use_vtd = true;
+        }
+
+    } else {
+        q->use_vtd = false;
+        // need to set up communicaton to PF
+        connect_to_mngif(q);
+    }
+
+    // allocate memory for RX/TX rings
+    struct capref tx_frame;
+    size_t tx_size = e10k_q_tdesc_legacy_size*NUM_TX_DESC;
+    void* tx_virt = alloc_map_frame(VREGION_FLAGS_READ_WRITE, tx_size, &tx_frame);
+    if (tx_virt == NULL) {
+        return DEVQ_ERR_INIT_QUEUE;
+    }
+
+
+    struct capref rx_frame;
+    size_t rx_size = e10k_q_tdesc_legacy_size*NUM_TX_DESC;
+    void* rx_virt = alloc_map_frame(VREGION_FLAGS_READ_WRITE, rx_size, &rx_frame);
+    if (rx_virt == NULL) {
+        return DEVQ_ERR_INIT_QUEUE;
+    }
+
+    struct e10k_queue_ops ops = {
+        .update_txtail = update_txtail,
+        .update_rxtail = update_rxtail,
+    };
+
+    struct capref txhwb_frame = NULL_CAP;
+    void* txhwb_virt = NULL;
+#if 0
+    if (use_txhwb) {
+        txhwb_virt = alloc_map_frame(VREGION_FLAGS_READ_WRITE, BASE_PAGE_SIZE, 
+        &txhwb_frame);
+        if (txhwb_virt == NULL) {
+            return DEVQ_ERR_INIT_QUEUE;
+        }
+        memset(txhwb_virt, 0, sizeof(uint32_t));
+    }
+#endif
+    e10k_queue_init(q, tx_virt, NUM_TX_DESC, txhwb_virt,
+                    rx_virt, NUM_RX_DESC, &ops);
+
+    DEBUG_QUEUE("Local queue init done\n");
+
+    q->use_vf = use_vf;
+    q->rx_frame = rx_frame;
+    q->tx_frame = tx_frame;
+    q->txhwb_frame = txhwb_frame;    
+    q->use_irq = interrupts;
+
+    // XXX:disable by default for now
+    q->use_txhwb = false;
+    q->use_rsc = false;
+
+    if (q->use_vf) {
+        err = e10k_vf_init_queue_hw(q);
+        if (err_is_fail(err)) {
+            return err;
+        }
+    } else {
+        struct capref regs;
+
+        // Inform card driver about new queue and get the registers/queue id
+        err = slot_alloc(&regs);
+        if (err_is_fail(err)) {
+            return err;
+        }
+
+        if (q->use_irq) {
+            /*
+            err = pci_setup_inthandler(interrupt_handler, NULL, &vector);
+            assert(err_is_ok(err));
+            core = disp_get_core_id();
+            */
+            // TODO setup MSI-X interrupts
+        }
+
+        int q_id;
+        err = q->binding->vtbl.create_queue(q->binding, tx_frame, txhwb_frame, 
+                                            rx_frame, 2048, q->msix_intvec, 
+                                            q->msix_intdest, false, false, &q_id,
+                                            &regs);   
+        if (err_is_fail(err)) {
+            return err;
+        }
+
+        assert(q_id >= 0);
+        q->id = (uint16_t)q_id;
+
+        err = map_device_memory(q, regs);
+        if (err_is_fail(err)) {
+            return err;
+        }
+    }
+
+    err = devq_init(&q->q, false);
+    if (err_is_fail(err)) {
+        return err;
+    }
+    
+    q->q.f.enq = e10k_enqueue;
+    q->q.f.deq = e10k_dequeue;
+    q->q.f.reg = e10k_register;
+    q->q.f.dereg = e10k_deregister;
+    q->q.f.ctrl = e10k_control;
+    q->q.f.notify = e10k_notify;
+
+
+    *queue = q;
+
+    return SYS_ERR_OK;
+}
+
diff --git a/lib/devif/backends/net/e10k/e10k_devif_vf.c b/lib/devif/backends/net/e10k/e10k_devif_vf.c
new file mode 100644 (file)
index 0000000..b28b5c6
--- /dev/null
@@ -0,0 +1,644 @@
+/*
+ * Copyright (c) 2013, 2014, University of Washington, 2017 ETH Zürich.
+ * 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, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include <net_device_manager/net_device_manager.h>
+#include <pci/pci.h>
+#include <barrelfish/nameservice_client.h>
+#include <barrelfish/debug.h>
+#include <ipv4/lwip/inet.h>
+#include <acpi_client/acpi_client.h>
+
+
+#include <if/e10k_defs.h>
+#include <if/e10k_vf_defs.h>
+#include <if/e10k_vf_rpcclient_defs.h>
+#include <dev/e10k_vf_dev.h>
+#include <dev/e10k_q_dev.h>
+
+#include "sleep.h"
+#include "helper.h"
+#include "e10k_devif_vf.h"
+#include "e10k_queue.h"
+#include "debug.h"
+
+
+#define POOL_SIZE 2
+
+
+#define E10K_PCI_DEVID 0x10ED
+
+
+#define QUEUE_INTRX 0
+#define QUEUE_INTTX 1
+
+
+struct vf_state {
+    uint8_t vf_num;
+    // if we have 64 vfs then pool
+    // size is only 2 but the maximum is 8
+    struct e10k_queue* qs[POOL_SIZE];
+    bool q_enabled[8];
+
+    uint64_t vf_mac;
+    
+    // device info
+    int initialized;
+    e10k_vf_t *d;
+    struct capref *regframe;
+    uint64_t d_mac;
+    uint32_t pci_function;
+
+
+    // interrupt related
+    bool msix;
+    struct bmallocator msix_alloc;
+    size_t vdriver_msix;
+    uint8_t vdriver_vector;
+    bool use_interrupts;
+
+    //
+    struct e10k_vf_rpc_client *binding;
+    bool bound;
+};
+
+static struct vf_state* vf;
+
+
+#define prnonz(x)                                               \
+    uint32_t x = e10k_vf_##x##_rd(vf->d);                           \
+    snprintf(str[cnt++], 32, #x "=%x \n", x);                      \
+
+static void stats_dump(void)
+{
+  char str[256][32];
+  int cnt = 0;
+  memset(str, 0, 256 * 32);
+
+    prnonz(vfctrl);
+    prnonz(vfstatus);
+    prnonz(vflinks);
+    prnonz(vfrxmemwrap);
+    prnonz(vfeicr);
+    prnonz(vfeics);
+    prnonz(vfeims);
+    prnonz(vfpsrtype);
+
+    if(cnt > 0) {
+      for(int i = 0; i < cnt; i++) {
+           printf("%s ", str[i]);
+      }
+      printf("\n");
+    }
+}
+
+static void setup_interrupt(size_t *msix_index, uint8_t core, uint8_t vector)
+{
+    bool res;
+    errval_t err;
+    uint8_t dest;
+
+    res = bmallocator_alloc(&vf->msix_alloc, msix_index);
+    assert(res);
+
+    err = get_apicid_from_core(core, &dest);
+    assert(err_is_ok(err));
+
+    err = pci_msix_vector_init(*msix_index, dest, vector);
+    assert(err_is_ok(err));
+
+    DEBUG_VF("e10k: MSI-X vector setup index=%"PRIx64", core=%d apic=%d swvec=%x\n",
+            *msix_index, core, dest, vector);
+}
+
+
+static void interrupt_handler_msix(void* arg)
+{
+    struct e10k_queue* q = (struct e10k_queue*)arg;
+    DEBUG_VF("MSI-X management interrupt\n");
+    e10k_vf_vfeicr_t eicr = e10k_vf_vfeicr_rd(vf->d);
+
+    eicr &= ~(1 << q->msix_index);
+
+    // Ensure management MSI-X vector is cleared
+    e10k_vf_vfeicr_wr(vf->d, 1 << q->msix_index);
+
+    // Reenable interrupt
+    e10k_vf_vfeimc_msix_wrf(vf->d, 1 << (q->msix_index % 32));
+    // TODO check for packets?
+}
+
+/** Stop whole device. */
+static void stop_device(struct vf_state* v)
+{
+    DEBUG_VF("Stopping device\n");
+
+    // Disable interrupts
+    e10k_vf_vfeimc_msix_wrf(v->d, 7);
+    e10k_vf_vfeicr_rd(v->d);
+
+    // Disable each RX and TX queue
+    for(int i = 0; i < 4; i++) {
+        e10k_vf_vftxdctl_wr(v->d, i, e10k_vf_vftxdctl_swflsh_insert(0x0, 1));
+    }
+    for(int i = 0; i < 8; i++) {
+        e10k_vf_vfrxdctl_wr(v->d, i, 0x0);
+    }
+
+    // From BSD driver (not in spec)
+    milli_sleep(2);
+}
+
+/**
+ * Initialize hardware registers.
+ * Is also called after a reset of the device.
+ */
+static void device_init(void)
+{
+    
+    int i;
+    errval_t err;
+    bool initialized_before = vf->initialized;
+
+    vf->initialized = 0;
+
+    stats_dump();
+
+    stop_device(vf);
+
+    assert(!initialized_before);
+
+    // Issue Global reset
+    e10k_vf_vfctrl_rst_wrf(vf->d, 1);
+    // Spec says 10, fbsd driver 50
+    milli_sleep(5000);
+    DEBUG_VF("Global reset done\n");
+
+    // Disable interrupts
+    e10k_vf_vfeimc_msix_wrf(vf->d, 7);
+    e10k_vf_vfeicr_rd(vf->d);
+
+    // Wait for link to come up
+    DEBUG_VF("Waiting for Link\n");
+    stats_dump();
+    //while (e10k_vf_vflinks_lnk_up_rdf(vf->d) == 0); // TODO: Timeout and Uncomment
+    DEBUG_VF("Link Up\n");
+    milli_sleep(50);
+
+    // Initialize interrupts
+    e10k_vf_vfeicr_wr(vf->d, 7);
+    e10k_vf_vfeitr_wr(vf->d, 0, 0x0);
+    e10k_vf_vfeitr_wr(vf->d, 1, 0x0);
+
+    if (vf->msix) {
+        // Allocate msix vector for cdriver and set up handler
+        if (vf->vdriver_msix == -1) {
+            err = pci_setup_inthandler(interrupt_handler_msix, NULL, 
+                                       &vf->vdriver_vector);
+            assert(err_is_ok(err));
+
+            setup_interrupt(&vf->vdriver_msix, disp_get_core_id(), 
+                            vf->vdriver_vector);
+        }
+
+        // Map management interrupts to our vector
+        e10k_vf_vfivar_misc_i_alloc0_wrf(vf->d, vf->vdriver_msix);
+        e10k_vf_vfivar_misc_i_allocval0_wrf(vf->d, 1);
+
+        // Enable interrupt
+        e10k_vf_vfeitr_wr(vf->d, vf->vdriver_msix / 32, (1 << (vf->vdriver_msix % 32)));
+    } else {
+        // Enable all interrupts
+        e10k_vf_vfeimc_wr(vf->d, e10k_vf_vfeims_rd(vf->d));
+        e10k_vf_vfeims_msix_wrf(vf->d, 7);
+    }
+
+    // Other stuff
+    e10k_vf_vfpsrtype_wr(vf->d, 0);
+
+    // disable relaxed ordering
+    for (i = 0; i < 8; i++) {
+        e10k_vf_vfdca_txctrl_txdesc_wbro_wrf(vf->d, i, 0);
+        e10k_vf_vfdca_rxctrl_rxhdr_ro_wrf(vf->d, i, 0);
+        e10k_vf_vfdca_rxctrl_rxdata_wrro_wrf(vf->d, i, 0);
+    }
+
+    // enable all queues
+    for (i = 0; i < POOL_SIZE; i++) {
+        e10k_vf_vftxdctl_enable_wrf(vf->d, i, 1);
+    }
+
+    vf->initialized = 1;
+}
+
+/** Initialize hardware queue n. */
+static void queue_hw_init(struct e10k_queue* q)
+{
+    errval_t r;
+    struct frame_identity frameid = { .base = 0, .bytes = 0 };
+    uint64_t tx_phys, txhwb_phys, rx_phys;
+    size_t tx_size, rx_size;
+    uint8_t n = q->id;
+    e10k_vf_t* d = q->d;
+
+    // Get physical addresses for rx/tx rings
+    r = invoke_frame_identify(q->tx_frame, &frameid);
+    assert(err_is_ok(r));
+    tx_phys = frameid.base;
+    tx_size = frameid.bytes;
+
+    r = invoke_frame_identify(q->rx_frame, &frameid);
+    assert(err_is_ok(r));
+    rx_phys = frameid.base;
+    rx_size = frameid.bytes;
+
+
+    DEBUG_VF("tx.phys=%"PRIx64" tx.size=%"PRIu64"\n", tx_phys, tx_size);
+    DEBUG_VF("rx.phys=%"PRIx64" rx.size=%"PRIu64"\n", rx_phys, rx_size);
+
+
+    // Initialize RX queue in HW
+    e10k_vf_vfrdbal_wr(d, n, rx_phys);
+    e10k_vf_vfrdbah_wr(d, n, rx_phys >> 32);
+    e10k_vf_vfrdlen_wr(d, n, rx_size);
+
+    e10k_vf_vfsrrctl_bsz_pkt_wrf(d, n, q->rxbufsz / 1024);
+    e10k_vf_vfsrrctl_bsz_hdr_wrf(d, n, 128 / 64); // TODO: Do 128 bytes suffice in
+                                               //       all cases?
+    e10k_vf_vfsrrctl_desctype_wrf(d, n, e10k_vf_adv_1buf);
+    e10k_vf_vfsrrctl_drop_en_wrf(d, n, 1);
+
+    // Initialize queue pointers (empty)
+    e10k_vf_vfrdt_wr(d, n, q->rx_head);
+    e10k_vf_vfrdh_wr(d, n, q->rx_head);
+
+    e10k_vf_vfrxdctl_enable_wrf(d, n, 1);
+    while (e10k_vf_vfrxdctl_enable_rdf(d, n) == 0); // TODO: Timeout
+    DEBUG_VF("[%x] RX queue enabled\n", n);
+
+    // Setup Interrupts for this queue
+    if (q->use_irq) {
+        DEBUG_VF("[%x] Setting up interrupts\n", n);
+        uint8_t rxv, txv;
+        // Look for interrupt vector
+        if (q->msix_intvec != 0) {
+            if (q->msix_index == -1) {
+                setup_interrupt(&q->msix_index, q->msix_intdest,
+                                q->msix_intvec);
+            }
+            rxv = txv = q->msix_index;
+        }
+
+        // XXX. Please double check, this is against the original intention
+        // Map rxv/txv to eicr bits that we can recognize
+        rxv = QUEUE_INTRX;
+        txv = QUEUE_INTTX;
+
+        // DEBUG_VF("rxv=%d txv=%d\n", rxv, txv);
+
+        // Setup mapping queue Rx/Tx -> interrupt
+        uint8_t i = n / 2;
+        if ((n % 2) == 0) {
+            e10k_vf_vfivar_i_alloc0_wrf(d, i, rxv);
+            e10k_vf_vfivar_i_allocval0_wrf(d, i, 1);
+            e10k_vf_vfivar_i_alloc1_wrf(d, i, txv);
+            e10k_vf_vfivar_i_allocval1_wrf(d, i, 1);
+        } else {
+            e10k_vf_vfivar_i_alloc2_wrf(d, i, rxv);
+            e10k_vf_vfivar_i_allocval2_wrf(d, i, 1);
+            e10k_vf_vfivar_i_alloc3_wrf(d, i, txv);
+            e10k_vf_vfivar_i_allocval3_wrf(d, i, 1);
+        }
+        if (q->msix_intvec != 0) {
+            // Enable interrupt
+            e10k_vf_vfeitr_wr(d, rxv / 32, (1 << (rxv % 32)));
+        }
+        if (rxv < 16) {
+            // Make sure interrupt is cleared
+            e10k_vf_vfeicr_wr(d, 1 << rxv);
+        }
+    }
+
+    // Initialize TX queue in HW
+    if (q->use_vtd) {
+        e10k_vf_vftdbal_wr(d, n, (lvaddr_t) q->tx_ring);
+        e10k_vf_vftdbah_wr(d, n, ((lvaddr_t)q->tx_ring) >> 32);
+    } else {
+        e10k_vf_vftdbal_wr(d, n, tx_phys);
+        e10k_vf_vftdbah_wr(d, n, tx_phys >> 32);
+    }
+
+    e10k_vf_vftdlen_wr(d, n, tx_size);
+
+    // Initialize TX head index write back
+    if (!capref_is_null(q->txhwb_frame)) {
+        r = invoke_frame_identify(q->txhwb_frame, &frameid);
+        assert(err_is_ok(r));
+        txhwb_phys = frameid.base;
+
+       if (q->use_vtd) {
+           e10k_vf_vftdwbal_headwb_low_wrf(d, n, ((lvaddr_t)q->tx_hwb) >> 2);
+           e10k_vf_vftdwbah_headwb_high_wrf(d, n, ((lvaddr_t)q->tx_hwb) >> 32);
+        } else {
+            e10k_vf_vftdwbal_headwb_low_wrf(d, n, txhwb_phys >> 2);
+            e10k_vf_vftdwbah_headwb_high_wrf(d, n, txhwb_phys >> 32);
+        }
+        e10k_vf_vftdwbal_headwb_en_wrf(d, n, 1);
+    }
+
+    // Initialized by queue driver to avoid race conditions
+    // Initialize queue pointers
+    assert(q->tx_head == 0);
+    e10k_vf_vftdh_wr(d, n, q->tx_head);
+    e10k_vf_vftdt_wr(d, n, q->tx_head);
+
+    // Configure prefetch and writeback threshhold
+    e10k_vf_vftxdctl_pthresh_wrf(d, n, 8); // FIXME: Figure out what the right number
+                                      //        is here.
+    e10k_vf_vftxdctl_hthresh_wrf(d, n, 0);
+    e10k_vf_vftxdctl_wthresh_wrf(d, n, 0);      // Needs to be 0 for TXHWB
+
+    e10k_vf_vftxdctl_enable_wrf(d, n, 1);
+
+    while (e10k_vf_vftxdctl_enable_rdf(d, n) == 0); // TODO: Timeout
+    DEBUG_VF("[%x] TX queue enabled\n", n);
+
+    // Some initialization stuff from BSD driver
+    e10k_vf_vfdca_txctrl_txdesc_wbro_wrf(d, n, 0);
+}
+
+#if 0
+/** Stop queue. */
+static void queue_hw_stop(uint8_t n)
+{
+#if 0
+    // This process is described in 4.6.7.1.2
+
+    // Disable TX for this queue
+    e10k_txdctl_enable_wrf(d, n, 0);
+
+    // TODO: Flush packet buffers
+    // TODO: Remove all filters
+    // TODO: With RSC we have to wait here (see spec), not used atm
+
+    // Disable RX for this queue
+    e10k_rxdctl_1_enable_wrf(d, n, 0);
+    while (e10k_rxdctl_1_enable_rdf(d, n) != 0); // TODO: Timeout
+
+    // A bit too much, but make sure memory is not used anymore
+    milli_sleep(1);
+#else
+    assert(!"NYI");
+#endif
+}
+#endif
+
+/** Here are the global interrupts handled. */
+static void interrupt_handler(void* arg)
+{
+    e10k_vf_vfeicr_t eicr = e10k_vf_vfeicr_rd(vf->d);
+
+    DEBUG_VF("interrupt vf (eicr=%x)\n", eicr);
+    
+    if (eicr & ((1 << QUEUE_INTRX) | (1 << QUEUE_INTTX))) {
+        e10k_vf_vfeicr_wr(vf->d, eicr);
+        /*qd_interrupt(!!(eicr & (1 << QUEUE_INTRX)),
+                     !!(eicr & (1 << QUEUE_INTTX)));*/
+    }
+}
+
+/******************************************************************************/
+/* Initialization code for driver */
+
+/** Callback from pci to initialize a specific PCI device. */
+static void pci_init_card(struct device_mem* bar_info, int bar_count)
+{
+
+    DEBUG_VF("pci init card\n");
+    errval_t err;
+    bool res;
+
+    assert(!vf->initialized);
+
+    vf->d = malloc(sizeof(*(vf->d)));
+
+    // Map first BAR for register access
+    assert(bar_count >= 1);
+    map_device(&bar_info[0]);
+    vf->regframe = bar_info[0].frame_cap;
+    DEBUG_VF("BAR[0] mapped (v=%llx p=%llx l=%llx)\n",
+            (unsigned long long) bar_info[0].vaddr,
+            (unsigned long long) bar_info[0].paddr,
+            (unsigned long long) bar_info[0].bytes);
+
+    // Initialize Mackerel binding
+    e10k_vf_initialize(vf->d, (void*) bar_info[0].vaddr);
+
+    // Initialize manager for MSI-X vectors
+    if (vf->msix) {
+        DEBUG_VF("Enabling MSI-X interrupts\n");
+        uint16_t msix_count = 0;
+        err = pci_msix_enable(&msix_count);
+        assert(err_is_ok(err));
+        assert(msix_count > 0);
+        DEBUG_VF("MSI-X #vecs=%d\n", msix_count);
+
+        res = bmallocator_init(&vf->msix_alloc, msix_count);
+        assert(res);
+    } else {
+        DEBUG_VF("Using legacy interrupts\n");
+    }
+
+    DEBUG_VF("STATUS = %x\n", e10k_vf_vfstatus_rd(vf->d));
+
+    // Initialize hardware registers etc.
+    DEBUG_VF("Initializing hardware\n");
+    device_init();
+
+    assert(vf->initialized);
+
+    // Tell PF driver
+    err = vf->binding->vtbl.init_done(vf->binding, vf->vf_num);
+    assert(err_is_ok(err));
+
+}
+
+
+/** Register with PCI */
+static errval_t pci_register(void)
+{
+    errval_t r;
+
+    r = pci_client_connect();
+    if (err_is_fail(r)) {
+        return r;
+    }
+
+    interrupt_handler_fn inthandler;
+
+    if(vf->use_interrupts) {
+        inthandler = interrupt_handler;
+    } else {
+        inthandler = NULL;
+    }
+
+    r = pci_register_driver_noirq(pci_init_card, PCI_CLASS_ETHERNET,
+                                PCI_DONT_CARE, PCI_DONT_CARE,
+                                PCI_VENDOR_INTEL, E10K_PCI_DEVID,
+                                7, 16, vf->pci_function);
+
+    DEBUG_VF("pci registered\n");
+    if (err_is_fail(r)) {
+        DEBUG_VF("err\n");
+        return r;
+    }
+    return SYS_ERR_OK;
+}
+
+
+static void vf_bind_cont(void *st, errval_t err, struct e10k_vf_binding *b)
+{
+    assert(err_is_ok(err));
+
+    struct e10k_vf_rpc_client *r = malloc(sizeof(*r));
+    assert(r != NULL);
+    err = e10k_vf_rpc_client_init(r, b);
+    if (err_is_ok(err)) {
+        vf->binding = r;
+    } else {
+        free(r);
+    }
+}
+
+static errval_t e10k_vf_client_connect(int pci_function)
+{
+    iref_t iref;
+    errval_t err, err2 = SYS_ERR_OK;
+    char name[256];
+
+    snprintf(name, 256, "e10k_vf%u", pci_function);
+
+    /* Connect to the pci server */
+    err = nameservice_blocking_lookup(name, &iref);
+    if (err_is_fail(err)) {
+        return err;
+    }
+
+    assert(iref != 0);
+
+    /* Setup flounder connection with pci server */
+    err = e10k_vf_bind(iref, vf_bind_cont, &err, get_default_waitset(),
+                   IDC_BIND_FLAG_RPC_CAP_TRANSFER);
+    if (err_is_fail(err)) {
+        return err;
+    }
+
+    /* XXX: Wait for connection establishment */
+    while (vf->binding == NULL && err2 == SYS_ERR_OK) {
+        messages_wait_and_handle_next();
+    }
+
+    return err2;
+}
+
+errval_t e10k_vf_init_queue_hw(struct e10k_queue* q)
+{
+    DEBUG_VF("VF queue init\n");
+    assert(vf->initialized);
+    assert(e10k_vf_can_create_queue());
+    assert(q != NULL);
+
+    uint8_t q_idx = 0;
+    // check which queue to initalize
+    for (int i = 0; i < 2; i++) {   
+        if (!vf->q_enabled[i]) {
+            q_idx = i;
+            vf->q_enabled[i] = true;
+            break;
+        }
+    }
+
+    q->d = vf->d;
+    DEBUG_VF("Enabled queue %d of VF in hardware\n", q_idx);
+    // initialize queue in hardware
+    queue_hw_init(q);
+
+    return SYS_ERR_OK;
+}
+
+errval_t e10k_init_vf_driver(uint8_t pci_function, 
+                             bool interrupts)
+{
+    
+    errval_t err, err2;
+    // crate vtd domain for VF driver
+    // XXX: might not be the best idea to do it here
+    err = connect_to_acpi();
+    assert(err_is_ok(err));
+    err = vtd_create_domain(cap_vroot);
+    assert(err_is_ok(err));
+    err = vtd_domain_add_device(0, 7, 16, 0, cap_vroot);
+    assert(err_is_ok(err));
+
+    DEBUG_VF("VF driver started\n");
+    vf = malloc(sizeof(struct vf_state));
+    vf->use_interrupts = interrupts;
+   
+    DEBUG_VF("Connecting to PF driver...\n");
+    err = e10k_vf_client_connect(pci_function);
+    if (err_is_fail(err)) {
+        return err;
+    }  
+
+    DEBUG_VF("Requesting VF number from PF...\n");
+    err = vf->binding->vtbl.request_vf_number(vf->binding, (uint8_t*) &vf->vf_num, 
+                                              &err2); 
+    if (err_is_fail(err) || err_is_fail(err2)) {
+        return err_is_fail(err) ? err: err2;
+    }
+    DEBUG_VF("Requesting MAC from PF...\n");
+    err = vf->binding->vtbl.get_mac_address(vf->binding, vf->vf_num, &vf->d_mac);
+    assert(err_is_ok(err));
+
+    DEBUG_VF("VF num %d initalize...\n", vf->vf_num);
+    err = pci_register();
+    if (err_is_fail(err)) {
+        return err;
+    }
+
+    while (!vf->initialized) {
+        event_dispatch(get_default_waitset());
+    }
+
+    DEBUG_VF("VF init done\n");
+    return SYS_ERR_OK;
+}
+
+bool e10k_vf_started(void) 
+{
+    return !(vf == NULL);
+}
+
+// Check if VF queue pool has still queues that it can enable
+bool e10k_vf_can_create_queue(void)
+{
+    for (int i = 0; i < POOL_SIZE; i++) {
+        if (!vf->q_enabled[i]) {
+            return true;
+        }            
+    }
+    return false;
+}
+
+
diff --git a/lib/devif/backends/net/e10k/e10k_devif_vf.h b/lib/devif/backends/net/e10k/e10k_devif_vf.h
new file mode 100644 (file)
index 0000000..273faa3
--- /dev/null
@@ -0,0 +1,66 @@
+/** \file
+ *  \brief Virtual Function management for e10k
+ */
+
+/*
+ * Copyright (c) 2017 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, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#ifndef __E10K_DEVIF_VF_H__
+#define __E10K_DEVIF_VF_H__
+
+struct vf_state;
+struct e10k_queue;
+
+
+ /*
+  * The library is not fully stateless. During the initalization
+  * call of a function there is a lot of state that can only
+  * stored in global variables
+  */ 
+
+ /**
+  * @brief Initializes a new virtual function of the 10k.
+  *
+  * @param pci_function     From which physical function this virtual
+  *                         function should be initalized
+  * @param interrupts       Enable interrupts
+  *
+  * @returns error on failure or SYS_ERR_OK on success
+  */
+errval_t e10k_init_vf_driver(uint8_t pci_function, 
+                             bool interrupts);
+
+ /**
+  * @brief Checks if the state of the library initalized
+  *
+  * @returns true if it is otherwise false
+  */
+bool e10k_vf_started(void);
+
+ /**
+  * @brief Checks if current active VF can still initalize another queue.
+  *        The general assumption is that each VF can allocate 2 queues.
+  *        Other configurations have to be specified in the PF driver.
+  *
+  * @returns true if it is otherwise false
+  */
+bool e10k_vf_can_create_queue(void);
+
+
+ /**
+  * @brief initalized queue on VF  in hardware
+  *
+  * @param queue     e10k queue struct with information about the mapped
+  *                  memory that is used for DMA
+  *
+  * @returns SYS_ERR_OK on success otherwise failter
+  */
+errval_t e10k_vf_init_queue_hw(struct e10k_queue* q);
+
+#endif
diff --git a/lib/devif/backends/net/e10k/e10k_queue.h b/lib/devif/backends/net/e10k/e10k_queue.h
new file mode 100644 (file)
index 0000000..760cac1
--- /dev/null
@@ -0,0 +1,424 @@
+/*
+ * Copyright (c) 2007-2011, 2017, 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, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#ifndef E10K_QUEUE_H_
+#define E10K_QUEUE_H_
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <arch/x86/barrelfish_kpi/asm_inlines_arch.h>
+#include <net_interfaces/flags.h>
+
+#include <devif/queue_interface.h>
+#include "../../../queue_interface_internal.h"
+#include <dev/e10k_q_dev.h>
+
+
+struct e10k_queue_ops {
+    errval_t (*update_txtail)(struct e10k_queue*, size_t);
+    errval_t (*update_rxtail)(struct e10k_queue*, size_t);
+};
+
+/**
+ * Context structure for RX descriptors. This is needed to implement RSC, since
+ * we need to be able to chain buffers together. */
+struct e10k_queue_rxctx {
+    struct devq_buf         buf;
+    struct e10k_queue_rxctx *previous;
+    bool                    used;
+};
+
+
+struct region_entry {
+    uint32_t rid;
+    lpaddr_t phys;
+    lvaddr_t virt;
+    size_t size;
+    struct capref cap;
+    struct region_entry* next;
+};
+
+struct e10k_queue {
+    struct devq q;
+
+    // queue info
+    void* d;
+    bool enabled;
+    uint16_t id;
+    uint32_t rsbufsz;
+    bool use_vf; // use VF for this queue
+    bool use_rsc; // Receive Side Coalescing
+    bool use_vtd; // Virtual addressing (required for VF)
+    bool use_rxctx; // 
+    bool use_txhwb; 
+    size_t rxbufsz;
+    uint8_t pci_function; 
+
+    // registered regions
+    struct region_entry* regions;   
+
+    // interrupt
+    bool use_irq;
+    uint8_t msix_intvec;
+    uint8_t msix_intdest;
+    size_t msix_index;
+    
+
+    // memory caps
+    struct capref                   rx_frame;
+    struct capref                   tx_frame;
+    struct capref                   txhwb_frame;
+    
+    struct capref                   regs;
+    // vf state
+    struct vf_state* vf;
+
+    // Communicatio to PF
+    struct e10k_vf_rpc_client *binding;
+    bool bound;
+
+    // FIXME: Look for appropriate type for the _head/tail/size fields
+    e10k_q_tdesc_adv_wb_array_t*    tx_ring;
+    struct devq_buf*                tx_bufs;
+    bool*                           tx_isctx;
+    size_t                          tx_head;
+    size_t                          tx_tail, tx_lasttail;
+    size_t                          tx_size;
+    void*                           tx_hwb;
+
+    e10k_q_rdesc_adv_wb_array_t*    rx_ring;
+    struct e10k_queue_rxctx*        rx_context;
+    size_t                          rx_head;
+    size_t                          rx_tail;
+    size_t                          rx_size;
+
+    struct e10k_queue_ops           ops;
+    void*                           opaque;
+    
+};
+
+typedef struct e10k_queue e10k_queue_t;
+
+// Does not initalize the queue struct itself
+static inline void e10k_queue_init(struct e10k_queue* q, void* tx, size_t tx_size,
+                                   uint32_t* tx_hwb, void* rx, size_t rx_size, 
+                                   struct e10k_queue_ops* ops)
+{
+    q->tx_ring = tx;
+    q->tx_bufs = calloc(tx_size, sizeof(struct devq_buf));
+    q->tx_isctx = calloc(tx_size, sizeof(bool));
+    q->tx_head = 0;
+    q->tx_tail = q->tx_lasttail = 0;
+    q->tx_size = tx_size;
+    q->tx_hwb = tx_hwb;
+
+    q->rx_ring = rx;
+    q->rx_context = calloc(rx_size, sizeof(*q->rx_context));
+    q->rx_head = 0;
+    q->rx_tail = 0;
+    q->rx_size = rx_size;
+
+    q->ops = *ops;
+
+    // Initialize ring memory with zero
+    memset(tx, 0, tx_size * e10k_q_tdesc_adv_wb_size);
+    memset(rx, 0, rx_size * e10k_q_rdesc_adv_wb_size);
+}
+
+static inline int e10k_queue_add_txcontext(e10k_queue_t* q, uint8_t idx,
+                                           uint8_t maclen, uint16_t iplen, 
+                                           uint8_t l4len, e10k_q_l4_type_t l4t)
+{
+    e10k_q_tdesc_adv_ctx_t d;
+    size_t tail = q->tx_tail;
+
+    memset(q->tx_ring[tail], 0, e10k_q_tdesc_adv_wb_size);
+
+    // TODO: Check if there is room in the queue
+    q->tx_isctx[tail] = true;
+    d = q->tx_ring[tail];
+
+    e10k_q_tdesc_adv_rd_dtyp_insert(d, e10k_q_adv_ctx);
+    e10k_q_tdesc_adv_rd_dext_insert(d, 1);
+
+    /* e10k_q_tdesc_adv_ctx_bcntlen_insert(d, 0x3f); */
+    e10k_q_tdesc_adv_ctx_idx_insert(d, idx);
+    e10k_q_tdesc_adv_ctx_maclen_insert(d, maclen);
+    e10k_q_tdesc_adv_ctx_iplen_insert(d, iplen);
+    e10k_q_tdesc_adv_ctx_ipv4_insert(d, 1);
+    e10k_q_tdesc_adv_ctx_l4len_insert(d, l4len);
+    e10k_q_tdesc_adv_ctx_l4t_insert(d, l4t);
+
+    q->tx_lasttail = q->tx_tail;
+    q->tx_tail = (tail + 1) % q->tx_size;
+    return 0;
+}
+
+
+static inline int e10k_queue_add_txbuf_ctx(e10k_queue_t* q, regionid_t rid,
+                                           bufferid_t bid, lpaddr_t base,
+                                           size_t totallen, uint64_t flags,
+                                           bool first, bool last,
+                                           size_t len, uint8_t ctx,
+                                           bool ixsm, bool txsm)
+{
+    e10k_q_tdesc_adv_rd_t d;
+    size_t tail = q->tx_tail;
+
+    memset(q->tx_ring[tail], 0, e10k_q_tdesc_adv_wb_size);
+
+    // TODO: Check if there is room in the queue
+    q->tx_isctx[tail] = false;
+    struct devq_buf* buf = &q->tx_bufs[tail];
+    buf->addr = base;
+    buf->bid = bid;
+    buf->rid = rid;
+    buf->flags = flags;  
+    buf->len = totallen;
+    d = q->tx_ring[tail];
+
+    e10k_q_tdesc_adv_rd_buffer_insert(d, base);
+    e10k_q_tdesc_adv_rd_dtalen_insert(d, len);
+    if (first) {
+        e10k_q_tdesc_adv_rd_paylen_insert(d, totallen);
+    }
+    e10k_q_tdesc_adv_rd_dtyp_insert(d, e10k_q_adv_data);
+    e10k_q_tdesc_adv_rd_dext_insert(d, 1);
+    e10k_q_tdesc_adv_rd_rs_insert(d, (last == 1));
+    e10k_q_tdesc_adv_rd_ifcs_insert(d, 1);
+    e10k_q_tdesc_adv_rd_eop_insert(d, last);
+
+    if (ctx != (uint8_t)-1) {
+        e10k_q_tdesc_adv_rd_idx_insert(d, ctx);
+        e10k_q_tdesc_adv_rd_cc_insert(d, 1);
+        e10k_q_tdesc_adv_rd_ixsm_insert(d, ixsm);
+        e10k_q_tdesc_adv_rd_txsm_insert(d, txsm);
+    }
+
+    q->tx_lasttail = q->tx_tail;
+    q->tx_tail = (tail + 1) % q->tx_size;
+    return 0;
+
+}
+
+static inline int e10k_queue_add_txbuf(e10k_queue_t* q, regionid_t rid,
+                                       bufferid_t bid, lpaddr_t base,
+                                       size_t totallen, uint64_t flags,
+                                       bool first, bool last,
+                                       size_t len)
+{
+    return e10k_queue_add_txbuf_ctx(q, rid, bid, base, totallen, 
+                                    flags, first, last, 
+                                    len, -1, false, false);
+}
+
+/*
+ * Reclaim 1 packet from the TX queue once it's handled by the
+ * card. Call multiple times to reclaim more packets.
+ *
+ * \param       q       Queue to check
+ * \param       opaque  Contains opaque data of reclaimed packet, if any
+ *
+ * \return true if packet can be reclaimed otherwise false
+ */
+static inline bool e10k_queue_get_txbuf(e10k_queue_t* q, regionid_t* rid,
+                                       bufferid_t* bid, lpaddr_t* base, 
+                                       size_t* len, uint64_t* flags)
+{
+    /* e10k_q_tdesc_adv_wb_t d; */
+    size_t head = q->tx_head;
+    bool result = false;
+
+    // If HWB is enabled, we can skip reading the descriptor if nothing happened
+    if (q->tx_hwb && *((uint32_t*)q->tx_hwb) == head) {
+        return false;
+    }
+
+    if(!q->tx_hwb) {
+        size_t idx = head;
+
+        // Skip over context and non-EOP descriptors
+        while(idx != q->tx_tail && q->tx_isctx[idx] && 
+              !e10k_q_tdesc_adv_wb_dd_extract(q->tx_ring[idx])) {
+            idx = (idx + 1) % q->tx_size;
+        }
+
+        if(idx == q->tx_tail) {
+            return false;
+        }
+    }
+
+    // That last packet got written out, now go reclaim from the head pointer.
+    if (!q->tx_isctx[head]) {
+        //*opaque = q->tx_opaque[head];
+        *rid = q->tx_bufs[head].rid;
+        *bid = q->tx_bufs[head].bid;
+        *base = q->tx_bufs[head].addr;
+        *len = q->tx_bufs[head].len;
+        *flags = q->tx_bufs[head].flags;
+
+        result = true;
+    }
+
+    /* memset(q->tx_ring[head], 0, e10k_q_tdesc_adv_wb_size); */
+    q->tx_head = (head + 1) % q->tx_size;
+    return result;
+}
+
+static inline errval_t e10k_queue_bump_txtail(e10k_queue_t* q)
+{
+    return q->ops.update_txtail(q, q->tx_tail);
+}
+
+static inline size_t e10k_queue_free_txslots(e10k_queue_t* q)
+{
+    size_t head = q->tx_head;
+    size_t tail = q->tx_tail;
+    size_t size = q->tx_size;
+
+    if (tail >= head) {
+        return size - (tail - head) - 1; // TODO: could this be off by 1?
+    } else {
+        return size - (tail + size - head) - 1; // TODO: off by 1?
+    }
+
+}
+
+static inline int e10k_queue_add_rxbuf(e10k_queue_t* q,
+                                       regionid_t rid,
+                                       bufferid_t bid,
+                                       lpaddr_t base,
+                                       size_t len,
+                                       uint64_t flags)
+{
+    e10k_q_rdesc_adv_rd_t d;
+    size_t tail = q->rx_tail;
+    struct e10k_queue_rxctx *ctx;
+
+    ctx = q->rx_context + tail;
+    if (ctx->used) {
+        printf("e10k: Already used!\n");
+        return 1;
+    }
+
+    // TODO: Check if there is room in the queue
+    ctx->buf.rid = rid;
+    ctx->buf.bid = bid;
+    ctx->buf.len = len;
+    ctx->buf.addr = base;
+    ctx->buf.flags = flags;
+    ctx->used = true;
+    d = (e10k_q_rdesc_adv_rd_t) q->rx_ring[tail];
+
+    e10k_q_rdesc_adv_rd_buffer_insert(d, base);
+    // TODO: Does this make sense for RSC?
+    e10k_q_rdesc_adv_rd_hdr_buffer_insert(d, 0);
+
+    q->rx_tail = (tail + 1) % q->rx_size;
+
+    return 0;
+}
+
+static inline uint64_t e10k_queue_convert_rxflags(e10k_q_rdesc_adv_wb_t d)
+{
+    uint64_t flags = 0;
+
+    // IP checksum
+    if (e10k_q_rdesc_adv_wb_ipcs_extract(d)) {
+        flags |= NETIF_RXFLAG_IPCHECKSUM;
+        if (!e10k_q_rdesc_adv_wb_ipe_extract(d)) {
+            flags |= NETIF_RXFLAG_IPCHECKSUM_GOOD;
+        }
+    }
+
+    // L4 checksum
+    if (e10k_q_rdesc_adv_wb_l4i_extract(d)) {
+        flags |= NETIF_RXFLAG_L4CHECKSUM;
+        if (!e10k_q_rdesc_adv_wb_l4e_extract(d)) {
+            flags |= NETIF_RXFLAG_L4CHECKSUM_GOOD;
+        }
+    }
+
+    // Packet type
+    if (e10k_q_rdesc_adv_wb_pt_ipv4_extract(d)) {
+        flags |= NETIF_RXFLAG_TYPE_IPV4;
+    }
+    if (e10k_q_rdesc_adv_wb_pt_tcp_extract(d)) {
+        flags |= NETIF_RXFLAG_TYPE_TCP;
+    }
+    if (e10k_q_rdesc_adv_wb_pt_udp_extract(d)) {
+        flags |= NETIF_RXFLAG_TYPE_UDP;
+    }
+
+    return flags;
+}
+
+static inline bool e10k_queue_get_rxbuf(e10k_queue_t* q, regionid_t* rid,
+                                        bufferid_t* bid, lpaddr_t* base,
+                                        size_t* len, uint64_t* flags, int* last)
+{
+    e10k_q_rdesc_adv_wb_t d;
+    size_t head = q->rx_head;
+    struct e10k_queue_rxctx *ctx;
+
+    d = q->rx_ring[head];
+    ctx = q->rx_context + head;
+
+    if (!e10k_q_rdesc_adv_wb_dd_extract(d)) {
+        return false;
+    }
+
+    // Barrier needed according to linux driver to make sure nothing else is
+    // read before the dd bit TODO: make sure
+    lfence();
+
+    // TODO add code for RSC
+
+    *flags = ctx->buf.flags;
+    // Set flags if it this is a descriptor with EOP
+    // TODO: with multi-part packets, we want these flags on the first packet
+    if (e10k_q_rdesc_adv_wb_eop_extract(d)) {
+        *flags = *flags | e10k_queue_convert_rxflags(d);
+    }
+
+    // TODO: Extract status (okay/error)
+    *last = e10k_q_rdesc_adv_wb_eop_extract(d);
+    *len = e10k_q_rdesc_adv_wb_pkt_len_extract(d);
+    *rid = ctx->buf.rid;
+    *bid = ctx->buf.bid;
+    *base = ctx->buf.addr;
+
+    ctx->used = false;
+    memset(d, 0, e10k_q_rdesc_adv_wb_size);
+
+    q->rx_head = (head + 1) % q->rx_size;
+    return true;
+}
+
+static inline errval_t e10k_queue_bump_rxtail(e10k_queue_t* q)
+{
+    return q->ops.update_rxtail(q, q->rx_tail);
+}
+
+static inline size_t e10k_queue_free_rxslots(e10k_queue_t* q)
+{
+    size_t head = q->rx_head;
+    size_t tail = q->rx_tail;
+    size_t size = q->rx_size;
+
+    if (tail >= head) {
+        return size - (tail - head) - 1; // TODO: could this be off by 1?
+    } else {
+        return size - (tail + size - head) - 1; // TODO: off by 1?
+    }
+}
+
+
+#endif // ndef E10K_QUEUE_H_
diff --git a/lib/devif/backends/net/e10k/helper.c b/lib/devif/backends/net/e10k/helper.c
new file mode 100644 (file)
index 0000000..627a42b
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2007-2011, 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, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#include "helper.h"
+
+#include <stdio.h>
+#include <stdint.h>
+
+#include <skb/skb.h>
+
+
+/* Dump bytes of memory region to stdout */
+void debug_dumpmem(void* buf, size_t len)
+{
+    uint8_t* b = buf;
+
+    while (len--)
+        printf("0x%02x ", *b++);
+    printf("\n");
+}
+
+/* allocate a single frame, mapping it into our vspace with given attributes */
+void* alloc_map_frame(vregion_flags_t attr, size_t size, struct capref *retcap)
+{
+    struct capref frame;
+    errval_t r;
+
+    r = frame_alloc(&frame, size, NULL);
+    assert(err_is_ok(r));
+    void *va;
+    r = vspace_map_one_frame_attr(&va, size, frame, attr,
+                                  NULL, NULL);
+    if (err_is_fail(r)) {
+        DEBUG_ERR(r, "vspace_map_one_frame failed");
+        return NULL;
+    }
+
+    if (retcap != NULL) {
+        *retcap = frame;
+    }
+
+    return va;
+}
+
+/* Get APIC id for specified core */
+errval_t get_apicid_from_core(coreid_t cid, uint8_t *apicid)
+{
+    static bool connected = false;
+    errval_t err;
+    unsigned int i;
+
+    if (!connected) {
+        err = skb_client_connect();
+        if (err_is_fail(err)) {
+            return err;
+        }
+        connected = true;
+    }
+
+    err = skb_execute_query("corename(%d,_,apic(ApicID)),write(ApicID).",
+                            cid);
+    if (err_is_fail(err)) {
+        return err;
+    }
+    err = skb_read_output("%u.", &i);
+    *apicid = i;
+    return err;
+}
+
+
+/*****************************************************************************/
+/* Bitmap based allocator */
+
+
+/** Init allocator for n objects. */
+bool bmallocator_init(struct bmallocator *alloc, size_t n)
+{
+    alloc->count = n;
+    alloc->bitmap = calloc((n + BMALLOCATOR_BITS - 1) / BMALLOCATOR_BITS,
+                           BMALLOCATOR_BITS / 8);
+    return alloc->bitmap != NULL;
+}
+
+/** Release memory associated with allocator. */
+void bmallocator_destroy(struct bmallocator *alloc)
+{
+    free(alloc->bitmap);
+    alloc->bitmap = NULL;
+}
+
+/** Allocate object, return index in *n if successful (return true). */
+bool bmallocator_alloc(struct bmallocator *alloc, size_t *n)
+{
+    size_t i;
+    BMALLOCATOR_TYPE bit;
+    size_t idx;
+
+    // This could be improved
+    for (i = 0; i < alloc->count; i++) {
+        bit = 1 << (i % BMALLOCATOR_BITS);
+        idx = i / BMALLOCATOR_BITS;
+
+        if (!(alloc->bitmap[idx] & bit)) {
+            alloc->bitmap[idx] |= bit;
+            *n = i;
+            return true;
+        }
+    }
+    return false;
+}
+
+/** Free object n, return value indicates if it was allocated before. */
+bool bmallocator_free(struct bmallocator *alloc, size_t n)
+{
+    bool result;
+    BMALLOCATOR_TYPE bit;
+    size_t idx;
+
+    if (n >= alloc->count) {
+        return false;
+    }
+
+    bit = (1 << (n % BMALLOCATOR_BITS));
+    idx = n / BMALLOCATOR_BITS;
+
+    result = alloc->bitmap[idx] & bit;
+    alloc->bitmap[idx] &= ~bit;
+
+    return result;
+}
+
diff --git a/lib/devif/backends/net/e10k/helper.h b/lib/devif/backends/net/e10k/helper.h
new file mode 100644 (file)
index 0000000..389100c
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2007-2011, 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, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#ifndef HELPER_H_
+#define HELPER_H_
+
+#include <errors/errno.h>
+#include <barrelfish/barrelfish.h>
+#include <barrelfish/vregion.h>
+
+void debug_dumpmem(void* buf, size_t len);
+void* alloc_map_frame(vregion_flags_t attr, size_t size, struct capref *retcap);
+errval_t get_apicid_from_core(coreid_t cid, uint8_t *apicid);
+
+/* Simple bitmap-based allocator */
+#define BMALLOCATOR_BITS 8
+#define BMALLOCATOR_TYPE uint8_t
+struct bmallocator {
+    BMALLOCATOR_TYPE *bitmap;
+    size_t count;
+};
+
+/** Init allocator for n objects. */
+bool bmallocator_init(struct bmallocator *alloc, size_t n);
+/** Release memory associated with allocator. */
+void bmallocator_destroy(struct bmallocator *alloc);
+/** Allocate object, return index in *n if successful (return true). */
+bool bmallocator_alloc(struct bmallocator *alloc, size_t *n);
+/** Free object n, return value indicates if it was allocated before. */
+bool bmallocator_free(struct bmallocator *alloc, size_t n);
+
+#endif // ndef HELPER_H_
diff --git a/lib/devif/backends/net/e10k/sleep.c b/lib/devif/backends/net/e10k/sleep.c
new file mode 100644 (file)
index 0000000..254e8a4
--- /dev/null
@@ -0,0 +1,67 @@
+/** \file
+ *  \brief Simple sleep call
+ */
+
+/*
+ * Copyright (c) 2010-2011, 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, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#include <barrelfish/barrelfish.h>
+#include <barrelfish/sys_debug.h>
+
+#include <bench/bench.h>
+
+#include "sleep.h"
+
+#if defined(__x86_64__)
+static uint64_t tscperms;
+#endif
+
+static bool initialised = false;
+
+void sleep_init(void)
+{
+    if (!initialised) {
+        bench_init();
+#if defined(__x86_64__)
+        errval_t err = sys_debug_get_tsc_per_ms(&tscperms);
+        assert(err_is_ok(err));
+#endif
+        initialised = true;
+    }
+}
+
+
+void cycle_sleep(uint64_t cycles)
+{
+    if (!initialised) {
+        sleep_init();
+    }
+
+    uint64_t start = bench_tsc();
+    uint64_t stop = bench_tsc();
+    while ((stop - start) < cycles) {
+        //        sys_yield(CPTR_NULL);
+        thread_yield_dispatcher(NULL_CAP);
+        stop = bench_tsc();
+    }
+}
+
+// FIXME: this should be determined at runtime from the kernel...
+// #define TSC_PER_MS 2513385
+
+void milli_sleep(uint64_t ms)
+{
+#if defined(__x86_64__)
+    uint64_t cycles = ms * tscperms;
+    cycle_sleep(cycles);
+#else
+    USER_PANIC("milli_sleep NYI for non-x86_64");
+#endif
+}
+
diff --git a/lib/devif/backends/net/e10k/sleep.h b/lib/devif/backends/net/e10k/sleep.h
new file mode 100644 (file)
index 0000000..74421f6
--- /dev/null
@@ -0,0 +1,21 @@
+/** \file
+ *  \brief Simple sleep call
+ */
+
+/*
+ * Copyright (c) 2010-2011, 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, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#ifndef __SLEEP_H__
+#define __SLEEP_H__
+
+void sleep_init(void);
+void cycle_sleep(uint64_t cycles);
+void milli_sleep(uint64_t ms);
+
+#endif