IOAT DMA Descriptor implementation
authorReto Achermann <acreto@student.ethz.ch>
Tue, 15 Jul 2014 13:59:33 +0000 (15:59 +0200)
committerStefan Kaestle <stefan.kaestle@inf.ethz.ch>
Wed, 20 Aug 2014 21:39:53 +0000 (23:39 +0200)
devices/ioat_dma.dev
usr/drivers/ioat_dma/ioat_dma_descriptors.c
usr/drivers/ioat_dma/ioat_dma_descriptors.h

index c5d5105..ac8bfa6 100644 (file)
@@ -1052,5 +1052,56 @@ device ioat_dma msbfirst ( addr cfg_base, addr bar ) "IOAT DMA (Crystal Beach) r
         chmsipend      1 rw "Pending Bit (when set) indicates that the DMA engine has a pending MSI-X";
     }; 
 
+    
+    
+    /*
+     * Descriptor types derrived from the Linux IOAT implementation
+     */
+    
+    /*
+     * Descriptor sizes
+     * 
+     * XXX: there are super extended descriptors which are longer in size...
+     */
+    constants desc_sizes "IOAT DMA Descriptor Sizes" {
+        descriptor_size = 64 "Size of the DMA descriptor";
+    };
+    
+    /*
+     * Possible opcodes
+     */
+    constants desc_opcodes "IOAT DMA Descriptor OP Codes" {
+        desc_op_copy    = 0x00 "Copy Operation";
+        desc_op_xor     = 0x87 "For Xor Descriptor";
+        desc_op_xor_val = 0x88 "For Xor descriptor";
+    };
+    
+    datatype desc_ctrl lsbfirst(32) "IOAT DMA Descriptor control field" {
+        int_en          1 "Interrupt enable";
+        src_snoop_dis   1 "Disable snooping of source address";
+        dest_snoop_dis  1 "Disable snooping of destination address";
+        compl_write     1 "";
+        fence           1 "";
+        null            1 "";
+        src_brk         1 "";
+        dest_brk        1 "";
+        bundle          1 "";
+        dest_dca        1 "";
+        hint            1 "";
+        _              13 "Reserved";
+        op              8 "OP Code to execute";
+    };
+    
+    datatype desc lsbfirst(32) "IOAT DMA Descriptor" {
+        size     32 "Size of the transfer";
+        ctrl     32 "Descriptor control field";
+        src      64 "Physical address of the source";
+        dst      64 "Physical address of the destination";
+        next     64 "Physical address of the next descriptor";
+        _        64 "Reserved";
+        _        64 "Reserved";
+        user1    64 "";
+        user2    64 "";
+    };
 };
 
index 301f19f..727a3b5 100644 (file)
 
 #include <barrelfish/barrelfish.h>
 
+#include <dev/ioat_dma_dev.h>
+
+#include "ioat_dma.h"
 #include "ioat_dma_descriptors.h"
 
-errval_t ioat_dma_desc_alloc_init(size_t element_size,
-                                  size_t align)
+#include "debug.h"
+
+#define ALIGN(val, align) (((val) + (align)-1) & ~((align)-1))
+
+
+#define IOAT_DMA_DESC_FLAG_VALID     0x01
+#define IOAT_DMA_DESC_FLAG_USED      0x02
+#define IOAT_DMA_DESC_FLAG_FIRST     0x03
+#define IOAT_DMA_DESC_FLAG_LAST      0x08
+#define IOAT_DMA_DESC_FLAG_PROGRESS  0x10
+#define IOAT_DMA_DESC_FLAG_DONE      0x40
+#define IOAT_DMA_DESC_FLAG_ERR       0x80
+
+/**
+ * IOAT DMA Descriptor Meta structure. This wrapps around the hardware descriptor
+ * and is used to keep track of the descriptors.
+ */
+struct ioat_dma_descriptor
+{
+    lpaddr_t paddr;                     ///< physical address of the descriptor
+    ioat_dma_desc_t desc;               ///< virtual address of the descriptor
+    uint32_t flags;                     ///< descriptor flags
+    struct ioat_dma_descriptor *next;   ///< next descriptor in the list
+    struct ioat_dma_request *req;       ///< pointer to the DMA request
+    struct ioat_dma_desc_alloc *alloc;  ///< allocator this descriptor belongs to
+};
+
+/**
+ *
+ */
+struct frame_list
+{
+    struct capref frame;     ///< the frame backing the descriptors
+    void *desc;              ///< pointer to the descriptor data structure
+    struct frame_list *next;  ///< next memory in list
+};
+
+struct ioat_dma_desc_alloc
+{
+    uint16_t elem_size;                     ///< the size of the elements
+    uint16_t elem_align;                    ///< alignment of the elements
+    uint32_t elem_count;                    ///< number of elements when growing
+    struct ioat_dma_descriptor *free_list;  ///< free list
+    uint32_t free_count;                    ///< the number of elements in free list
+    struct frame_list *frame_list;          ///< keep track of allocated frames
+};
+
+static inline struct ioat_dma_descriptor *desc_deq(struct ioat_dma_desc_alloc *alloc)
+{
+    struct ioat_dma_descriptor *ret = alloc->free_list;
+    alloc->free_list = ret->next;
+    alloc->free_count--;
+    return ret;
+}
+
+static inline void desc_enq(struct ioat_dma_descriptor *desc)
 {
-    assert(!"NYI");
+    struct ioat_dma_desc_alloc *alloc = desc->alloc;
+    desc->next = alloc->free_list;
+    alloc->free_list = desc;
+    alloc->free_count++;
+}
+
+/**
+ *
+ */
+static errval_t alloc_grow(struct ioat_dma_desc_alloc *alloc)
+{
+    errval_t err;
+
+    struct frame_list *flist = malloc(sizeof(struct frame_list));
+    if (flist == NULL) {
+        return LIB_ERR_MALLOC_FAIL;
+    }
+
+    size_t frame_size = alloc->elem_count * alloc->elem_size;
+    err = frame_alloc(&flist->frame, frame_size, &frame_size);
+    if (err_is_fail(err)) {
+        free(flist);
+        return err;
+    }
+
+    struct frame_identity id;
+    err = invoke_frame_identify(flist->frame, &id);
+    if (err_is_fail(err)) {
+        cap_destroy(flist->frame);
+        free(flist);
+        return err;
+    }
+
+    uint8_t *descr_vaddr;
+    err = vspace_map_one_frame_attr((void **) &descr_vaddr, frame_size, flist->frame,
+    IOAT_DMA_DESC_MAP_FLAGS,
+                                    NULL, NULL);
+    if (err_is_fail(err)) {
+        cap_destroy(flist->frame);
+        free(flist);
+        return err;
+    }
+
+    IODESC_DEBUG("Allocated frame of size %lu bytes @ [%016lx]\n",
+                 (uint64_t ) frame_size, id.base);
+
+    uint32_t num_desc = frame_size / alloc->elem_size;
+
+    struct ioat_dma_descriptor *desc = calloc(num_desc, sizeof(*desc));
+    if (desc == NULL) {
+        vspace_unmap(descr_vaddr);
+        free(flist);
+        return LIB_ERR_MALLOC_FAIL;
+    }
+
+    lpaddr_t descr_paddr = id.base;
+
+    for (uint32_t i = 0; i < num_desc; ++i) {
+        /* initialize the fields */
+        desc->desc = descr_vaddr;
+        desc->paddr = descr_paddr;
+        desc->req = NULL;
+        desc->alloc = alloc;
+        desc->flags = IOAT_DMA_DESC_FLAG_VALID;
+
+        /* put it onto the free list */
+        desc_enq(desc);
+
+        descr_vaddr += alloc->elem_size;
+        descr_paddr += alloc->elem_size;
+    }
+
+    flist->next = alloc->frame_list;
+    alloc->frame_list = flist;
+
+    IODESC_DEBUG("Allocated %u desc of size %u\n", num_desc, alloc->elem_size);
+
+    return SYS_ERR_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ * Public interface
+ * ----------------------------------------------------------------------------
+ */
+
+/**
+ * \brief allocates and initializes a new descriptor allocator for descriptors
+ *        of a give size.
+ *
+ * \param size      size of the descriptors in bytes
+ * \param align     alignment constraint for the descriptors
+ * \param count     number of initial descriptors to allocate
+ * \param ret_alloc returns a pointer to the allocator
+ *
+ * \returns SYS_ERR_OK on success
+ *          errval on failure
+ */
+errval_t ioat_dma_desc_alloc_init(uint16_t size,
+                                  uint16_t align,
+                                  uint32_t count,
+                                  struct ioat_dma_desc_alloc **ret_alloc)
+{
+    errval_t err;
+
+    assert(ret_alloc);
+
+    struct ioat_dma_desc_alloc *alloc = calloc(1, sizeof(*alloc));
+
+    assert((align & (IOAT_DMA_DESC_ALIGN -1)) == 0);
+
+    alloc->elem_size = ALIGN(size, align);
+    alloc->elem_align = align;
+    alloc->elem_count = count;
+
+    IODESC_DEBUG("Initialized allocator from elements of size %u\n",
+                 alloc->elem_size);
+
+    alloc->free_count = 0;
+    alloc->free_list = NULL;
+    alloc->frame_list = NULL;
+
+    err = alloc_grow(alloc);
+    if (err_is_fail(err)) {
+        free(alloc);
+        *ret_alloc = NULL;
+        return err;
+    }
+
+    *ret_alloc = alloc;
+
     return SYS_ERR_OK;
 }
+
+/**
+ * \brief allocates a new descriptor from the allocator
+ *
+ * \param alloc the allocator to get the descriptors
+ *
+ * \returns pointer to the memory region of the descriptor
+ *          NULL on failure
+ *
+ * Note: this function will automatically grow the allocator if there are
+ *       no descriptors free
+ */
+struct ioat_dma_descriptor *ioat_dma_desc_alloc(struct ioat_dma_desc_alloc *alloc)
+{
+    errval_t err;
+
+    if (alloc->free_count == 0) {
+        assert(alloc->free_list == NULL);
+        err = alloc_grow(alloc);
+        if (err_is_fail(err)) {
+            return NULL;
+        }
+    }
+
+    struct ioat_dma_descriptor *ret = desc_deq(alloc);
+    ret->flags |= IOAT_DMA_DESC_FLAG_USED;
+    ret->flags |= IOAT_DMA_DESC_FLAG_FIRST;
+    ret->flags |= IOAT_DMA_DESC_FLAG_LAST;
+    assert(ret->flags & IOAT_DMA_DESC_FLAG_VALID);
+    return ret;
+}
+
+/**
+ * \brief frees a previously allocated DMA descriptor
+ *
+ * \param desc the descriptor to be returned
+ */
+void ioat_dma_desc_free(struct ioat_dma_descriptor *desc)
+{
+    assert(desc->flags & IOAT_DMA_DESC_FLAG_VALID);
+    desc->flags = IOAT_DMA_DESC_FLAG_VALID;
+    desc->req = NULL;
+    desc_enq(desc);
+}
+
+/**
+ * \brief allocates a a descriptor chain from the allocator
+ *
+ * \param alloc the allocator to get the descriptors
+ * \param count number of descriptors in the chain
+ *
+ * \returns pointer to the head of the descriptor chain
+ *          NULL on failure
+ *
+ * Note: this function will automatically grow the allocator if there are
+ *       no descriptors free
+ */
+struct ioat_dma_descriptor *ioat_dma_desc_chain_alloc(struct ioat_dma_desc_alloc *alloc,
+                                                      uint32_t count)
+{
+    errval_t err;
+
+    if (alloc->free_count < count) {
+        err = alloc_grow(alloc);
+        if (err_is_fail(err)) {
+            return NULL;
+        }
+    }
+    assert(alloc->free_count > count);
+
+    struct ioat_dma_descriptor *desc = NULL, *next = NULL;
+    for (uint32_t i = 0; i < count; ++i) {
+        desc = desc_deq(alloc);
+        assert(desc);
+        assert(desc->flags & IOAT_DMA_DESC_FLAG_VALID);
+
+        if (!next) {
+            desc->flags |= IOAT_DMA_DESC_FLAG_LAST;
+            ioat_dma_desc_next_insert(desc->desc, 0);
+        } else {
+            ioat_dma_desc_next_insert(desc->desc, next->paddr);
+        }
+        desc->flags |= IOAT_DMA_DESC_FLAG_USED;
+        desc->next = next;
+
+        next = desc;
+    }
+    desc->flags |= IOAT_DMA_DESC_FLAG_FIRST;
+
+    return desc;
+}
+
+/**
+ * \brief frees a previously allocated DMA descriptor chain
+ *
+ * \param desc head of the descriptor chain to be freed
+ */
+void ioat_dma_desc_chain_free(struct ioat_dma_descriptor *head)
+{
+    struct ioat_dma_descriptor *desc;
+    while (head) {
+        assert(head->flags & IOAT_DMA_DESC_FLAG_VALID);
+        head->flags = IOAT_DMA_DESC_FLAG_VALID;
+        head->req = NULL;
+        desc = head;
+        head = head->next;
+        desc_enq(desc);
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ * Descriptor getters / setters
+ * ----------------------------------------------------------------------------
+ */
+
+/**
+ * \brief returns the physical address of the descriptor
+ *
+ * \param desc IOAT DMA descriptor
+ *
+ * \returns physical address of the descriptor
+ */
+lpaddr_t ioat_dma_desc_get_paddr(struct ioat_dma_descriptor *desc)
+{
+    return desc->paddr;
+}
+
+/**
+ * \brief returns a virtual address pointer to the location where the descriptor
+ *        is mapped
+ *
+ * \param desc IOAT DMA descriptor
+ */
+ioat_dma_desc_t ioat_dma_desc_get_desc(struct ioat_dma_descriptor *desc)
+{
+    return desc->desc;
+}
+
+/**
+ * \brief sets the corresponding request
+ *
+ * \param desc IOAT DMA descriptor
+ */
+void ioat_dma_desc_set_request(struct ioat_dma_descriptor *desc,
+                               struct ioat_dma_request *req)
+{
+    desc->req = req;
+}
+
+/**
+ * \brief returns the corresponding IOAT DMA request this descriptor belongs
+ *
+ * \param desc IOAT DMA descriptor
+ *
+ * \brief pointer to the request
+ *        NULL if there is none
+ */
+struct ioat_dma_request *ioat_dma_desc_get_request(struct ioat_dma_descriptor *desc)
+{
+    return desc->req;
+}
+
+/**
+ * \brief returns a pointer to the next descriptor in a chain
+ *
+ * \param desc IOAT DMA descriptor
+ *
+ * \returns next descriptor
+ *          NULL if the end of chain
+ */
+struct ioat_dma_descriptor *ioat_dma_desc_get_next(struct ioat_dma_descriptor *desc)
+{
+    return desc->next;
+}
+
+/**
+ * \brief sets the next descriptor pointer in the descriptor
+ *
+ * \param desc IOAT DMA descriptor
+ * \param next IOAT DMA descriptor
+ *
+ * XXX: This does not check for cycles
+ */
+void ioat_dma_desc_set_next(struct ioat_dma_descriptor *desc,
+                            struct ioat_dma_descriptor *next)
+{
+    if (desc->flags & IOAT_DMA_DESC_FLAG_LAST) {
+        desc->flags &= ~IOAT_DMA_DESC_FLAG_LAST;
+        next->flags |= IOAT_DMA_DESC_FLAG_LAST;
+    }
+    ioat_dma_desc_next_insert(desc->desc, next->paddr);
+    desc->next = next;
+}
+
+/**
+ * \brief initializes the hardware specific part of the descriptor
+ *
+ * \param desc  IOAT DMA descriptor
+ * \param src   Source address of the transfer
+ * \param dst   destination address of the transfer
+ * \param size  number of bytes to copy
+ * \param ctrl  control flags
+ *
+ * XXX: this function assumes that the size of the descriptor has already been
+ *      checked and must match the maximum transfer size of the channel
+ */
+void ioat_dma_desc_init(struct ioat_dma_descriptor *desc,
+                        lpaddr_t src,
+                        lpaddr_t dst,
+                        uint32_t size,
+                        ioat_dma_desc_ctrl_t ctrl)
+{
+    ioat_dma_desc_size_insert(desc->desc, size);
+    ioat_dma_desc_ctrl_insert(desc->desc, *((uint32_t *)ctrl));
+    ioat_dma_desc_src_insert(desc->desc, src);
+    ioat_dma_desc_dst_insert(desc->desc, dst);
+}
+
index 5eb1621..c47d3d3 100644 (file)
 #ifndef IOAT_DMA_DESCRIPTORS_H
 #define IOAT_DMA_DESCRIPTORS_H
 
-struct ioat_dma_desc_pool;
+#include <dev/ioat_dma_dev.h>
+
+struct ioat_dma_desc_alloc;
+struct ioat_dma_request;
+
+#define IOAT_DMA_DESC_MAP_FLAGS VREGION_FLAGS_READ_WRITE
 
 /// the minimum amount of DMA descriptors to allocate
-#define IOAT_DMA_DESC_COUNT
+#define IOAT_DMA_DESC_COUNT 256
 
-#define IOAT_DMA_DESC_SIZE 0
+/// the size of the basic descriptor
+#define IOAT_DMA_DESC_SIZE 64
 
+/// minimum alignment constraint for the descriptors
 #define IOAT_DMA_DESC_ALIGN 64
 
+
+/*
+ * ----------------------------------------------------------------------------
+ * Descriptor allocation / free
+ * ----------------------------------------------------------------------------
+ */
+
 /**
+ * \brief allocates and initializes a new descriptor allocator for descriptors
+ *        of a give size.
  *
+ * \param size      size of the descriptors in bytes
+ * \param align     alignment constraint for the descriptors
+ * \param count     number of initial descriptors to allocate
+ * \param ret_alloc returns a pointer to the allocator
+ *
+ * \returns SYS_ERR_OK on success
+ *          errval on failure
+ */
+errval_t ioat_dma_desc_alloc_init(uint16_t size,
+                                  uint16_t align,
+                                  uint32_t count,
+                                  struct ioat_dma_desc_alloc **ret_alloc);
+
+
+/**
+ * \brief allocates a new descriptor from the allocator
+ *
+ * \param alloc the allocator to get the descriptors
+ *
+ * \returns pointer to the memory region of the descriptor
+ *          NULL on failure
+ *
+ * Note: this function will automatically grow the allocator if there are
+ *       no descriptors free
+ */
+struct ioat_dma_descriptor *ioat_dma_desc_alloc(struct ioat_dma_desc_alloc *alloc);
+
+/**
+ * \brief frees a previously allocated DMA descriptor
+ *
+ * \param desc the descriptor to be returned
+ */
+void ioat_dma_desc_free(struct ioat_dma_descriptor *desc);
+
+/**
+ * \brief allocates a a descriptor chain from the allocator
+ *
+ * \param alloc the allocator to get the descriptors
+ * \param count number of descriptors in the chain
+ *
+ * \returns pointer to the head of the descriptor chain
+ *          NULL on failure
+ *
+ * Note: this function will automatically grow the allocator if there are
+ *       no descriptors free
+ */
+struct ioat_dma_descriptor *ioat_dma_desc_chain_alloc(struct ioat_dma_desc_alloc *alloc,
+                                                      uint32_t count);
+
+/**
+ * \brief frees a previously allocated DMA descriptor chain
+ *
+ * \param desc head of the descriptor chain to be freed
+ */
+void ioat_dma_desc_chain_free(struct ioat_dma_descriptor *head);
+
+
+/*
+ * ----------------------------------------------------------------------------
+ * Descriptor getters / setters
+ * ----------------------------------------------------------------------------
  */
-errval_t ioat_dma_desc_alloc_init(size_t element_size,
-                                  size_t align);
 
+/**
+ * \brief returns the physical address of the descriptor
+ *
+ * \param desc IOAT DMA descriptor
+ *
+ * \returns physical address of the descriptor
+ */
+lpaddr_t ioat_dma_desc_get_paddr(struct ioat_dma_descriptor *desc);
 
+/**
+ * \brief returns a virtual address pointer to the location where the descriptor
+ *        is mapped
+ *
+ * \param desc IOAT DMA descriptor
+ */
+ioat_dma_desc_t ioat_dma_desc_get_desc(struct ioat_dma_descriptor *desc);
 
-void *ioat_dma_desc_alloc(void );
+/**
+ * \brief sets the corresponding request
+ *
+ * \param desc IOAT DMA descriptor
+ */
+void ioat_dma_desc_set_request(struct ioat_dma_descriptor *desc,
+                               struct ioat_dma_request *req);
 
-errval_t ioat_dma_desc_free(void);
+/**
+ * \brief returns the corresponding IOAT DMA request this descriptor belongs
+ *
+ * \param desc IOAT DMA descriptor
+ *
+ * \brief pointer to the request
+ *        NULL if there is none
+ */
+struct ioat_dma_request *ioat_dma_desc_get_request(struct ioat_dma_descriptor *desc);
 
-errval_t ioat_dma_desc_chain_alloc(void);
+/**
+ * \brief returns a pointer to the next descriptor in a chain
+ *
+ * \param desc IOAT DMA descriptor
+ *
+ * \returns next descriptor
+ *          NULL if the end of chain
+ */
+struct ioat_dma_descriptor *ioat_dma_desc_get_next(struct ioat_dma_descriptor *desc);
 
-errval_t ioat_dma_desc_chain_free(void);
+/**
+ * \brief sets the next descriptor pointer in the descriptor
+ *
+ * \param desc IOAT DMA descriptor
+ * \param next IOAT DMA descriptor
+ *
+ * XXX: This does not check for cycles
+ */
+void ioat_dma_desc_set_next(struct ioat_dma_descriptor *desc,
+                            struct ioat_dma_descriptor *next);
 
 
+/**
+ * \brief initializes the hardware specific part of the descriptor
+ *
+ * \param desc  IOAT DMA descriptor
+ * \param src   Source address of the transfer
+ * \param dst   destination address of the transfer
+ * \param size  number of bytes to copy
+ * \param ctrl  control flags
+ *
+ * XXX: this function assumes that the size of the descriptor has already been
+ *      checked and must match the maximum transfer size of the channel
+ */
+void ioat_dma_desc_init(struct ioat_dma_descriptor *desc,
+                        lpaddr_t src,
+                        lpaddr_t dst,
+                        uint32_t size,
+                        ioat_dma_desc_ctrl_t ctrl);
 
 #endif /* IOAT_DMA_CHANNEL_H */