[HWmemExternal] Support external hwmem (dmabuf)
authorDongju Chae <dongju.chae@samsung.com>
Thu, 7 May 2020 10:58:59 +0000 (19:58 +0900)
committer채동주/On-Device Lab(SR)/Staff Engineer/삼성전자 <dongju.chae@samsung.com>
Thu, 14 May 2020 03:52:30 +0000 (12:52 +0900)
This patch supports external hwmem and revives dmabuf-related apptests.

Signed-off-by: Dongju Chae <dongju.chae@samsung.com>
15 files changed:
src/core/comm/plugin-comm-ip.cc
src/core/ne-handler.cc
src/core/ne-handler.h
src/core/ne-hwmem.cc
src/core/ne-hwmem.h
src/core/ne-mem.cc
src/core/ne-mem.h
src/core/ne-segment-table.cc
src/core/ne-segment-table.h
tests/apptests/dmabuf_buffers.c
tests/apptests/dmabuf_model.c
tests/apptests/example_visa.c
tests/apptests/meson.build
tests/unittests/ne_core_mem_test.cc
tests/unittests/ne_core_segment_table_test.cc

index 5b27ea5..b143210 100644 (file)
@@ -76,11 +76,8 @@ public:
         memcpy (data, buf->addr, buf->size);
 
       return 0;
-    } else if (buf->type == BUFFER_DMABUF) {
-      if (buf->dmabuf < 0)
-        return -EINVAL;
-      /** TODO */
     }
+    /** BUFFER_DMABUF enables zero-copy, which means it's impossible to manipulate data */
 
     return -EINVAL;
   }
index 2e0596b..149556d 100644 (file)
@@ -1006,20 +1006,26 @@ TrinityVision::prepareInputBuffers (const Metadata *meta, const input_buffers *i
     return nullptr;
   }
 
-  Buffer * buffer = mem_->allocBuffer ();
-  if (buffer != nullptr) {
-    const generic_buffer *first = &input->bufs[0];
-    if (first->type == BUFFER_DMABUF) {
-      buffer->setDmabuf (first->dmabuf);
-      buffer->setOffset (first->offset);
-      buffer->setSize (meta->getBufferSize());
-    } else {
-      int status = buffer->alloc (meta->getBufferSize ());
-      if (status != 0) {
-        logerr (TAG, "Failed to allocate buffer: %d\n", status);
-        delete buffer;
-        return nullptr;
-      }
+  Buffer * buffer;
+  const generic_buffer *first = &input->bufs[0];
+  if (first->type == BUFFER_DMABUF) {
+    buffer = mem_->allocBuffer (new HWmemExternal);
+    if (buffer == nullptr)
+      return nullptr;
+
+    buffer->setDmabuf (first->dmabuf);
+    buffer->setOffset (first->offset);
+    buffer->setSize (meta->getBufferSize());
+  } else {
+    buffer = mem_->allocBuffer (new HWmemDevice);
+    if (buffer == nullptr)
+      return nullptr;
+
+    int status = buffer->alloc (meta->getBufferSize ());
+    if (status != 0) {
+      logerr (TAG, "Failed to allocate buffer: %d\n", status);
+      delete buffer;
+      return nullptr;
     }
   }
 
@@ -1050,21 +1056,29 @@ TrinityVision::setModel (const generic_buffer *model_buf, Model ** model_ptr)
   if (model_buf == nullptr || model_ptr == nullptr)
     return -EINVAL;
 
-  Model *model = mem_->allocModel ();
-  if (model == nullptr) {
-    logerr (TAG, "Failed to allocate model\n");
-    return -ENOMEM;
-  }
+  Model *model;
+  int status;
 
-  int status = 0;
   switch (model_buf->type) {
   case BUFFER_DMABUF:
+    model = mem_->allocModel (new HWmemExternal);
+    if (model == nullptr) {
+      logerr (TAG, "Failed to allocate model\n");
+      return -ENOMEM;
+    }
+
     model->setDmabuf (model_buf->dmabuf);
     model->setOffset (model_buf->offset);
     model->setSize (model_buf->size);
     break;
   case BUFFER_FILE:
   case BUFFER_MAPPED:
+    model = mem_->allocModel (new HWmemDevice);
+    if (model == nullptr) {
+      logerr (TAG, "Failed to allocate model\n");
+      return -ENOMEM;
+    }
+
     status = model->alloc (model_buf->size);
     if (status != 0) {
       logerr (TAG, "Failed to allocate model: %d\n", status);
@@ -1078,8 +1092,7 @@ TrinityVision::setModel (const generic_buffer *model_buf, Model ** model_ptr)
     }
     break;
   default:
-    status = -EINVAL;
-    goto delete_exit;
+    return -EINVAL;
   }
 
   status = model->setMetadata (model->getData());
@@ -1140,14 +1153,16 @@ TrinityVision::run (npu_input_opmode opmode, const Model *model,
     return -EINVAL;
   }
 
-  for (uint32_t idx = 0; idx < input->num_buffers; idx++) {
-    auto func = std::bind (TrinityVision::manipulateData, model, idx, true,
-        std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
-    int status = comm_.extractGenericBuffer (&input->bufs[idx],
-        buffer->getInputTensor(idx)->getData(), func);
-    if (status != 0) {
-      logerr (TAG, "Failed to feed input buffer: %d\n", status);
-      return status;
+  if (!buffer->isExternal ()) {
+    for (uint32_t idx = 0; idx < input->num_buffers; idx++) {
+      auto func = std::bind (TrinityVision::manipulateData, model, idx, true,
+          std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
+      int status = comm_.extractGenericBuffer (&input->bufs[idx],
+          buffer->getInputTensor(idx)->getData(), func);
+      if (status != 0) {
+        logerr (TAG, "Failed to feed input buffer: %d\n", status);
+        return status;
+      }
     }
   }
 
@@ -1186,17 +1201,23 @@ TrinityVision::callback (Request *req, npuOutputNotify cb, void *cb_data)
   for (uint32_t idx = 0; idx < output.num_buffers; idx++) {
     uint32_t output_tensor_size = model->getOutputTensorSize (idx);
 
-    output.bufs[idx].type = BUFFER_MAPPED;
-    output.bufs[idx].size = output_tensor_size;
-    /** user needs to free this */
-    output.bufs[idx].addr = malloc (output_tensor_size);
-
-    auto func = std::bind (TrinityVision::manipulateData, model, idx, false,
-        std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
-    int status = comm_.insertGenericBuffer (buffer->getOutputTensor(idx)->getData(),
-        &output.bufs[idx], func);
-    if (status != 0) {
-      logerr (TAG, "Failed to return output buffer: %d\n", status);
+    if (buffer->isExternal ()) {
+      output.bufs[idx].type = BUFFER_DMABUF;
+      output.bufs[idx].size = output_tensor_size;
+      output.bufs[idx].addr = buffer->getOutputTensor(idx)->getData();
+    } else {
+      output.bufs[idx].type = BUFFER_MAPPED;
+      output.bufs[idx].size = output_tensor_size;
+      /** user needs to free this */
+      output.bufs[idx].addr = malloc (output_tensor_size);
+
+      auto func = std::bind (TrinityVision::manipulateData, model, idx, false,
+          std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
+      int status = comm_.insertGenericBuffer (buffer->getOutputTensor(idx)->getData(),
+          &output.bufs[idx], func);
+      if (status != 0) {
+        logerr (TAG, "Failed to return output buffer: %d\n", status);
+      }
     }
   }
 
@@ -1207,44 +1228,43 @@ TrinityVision::callback (Request *req, npuOutputNotify cb, void *cb_data)
 
 /**
  * @brief extract the segment table instance from input generic buffers
- * @param[in] meta the model metadata
+ * @param[in] model the model instance
  * @param[in] input the input generic buffers
  * @return the segment table instance
  */
 SegmentTable *
-TrinityVision2::prepareSegmentTable (const Metadata *meta, const input_buffers *input)
+TrinityVision2::prepareSegmentTable (const Model *model, const input_buffers *input)
 {
-  if (meta == nullptr || input == nullptr ||
+  if (model == nullptr || input == nullptr) {
+    logerr (TAG, "Invalid arguments provided\n");
+    return nullptr;
+  }
+
+  const Metadata *meta = model->getMetadata ();
+  if (meta == nullptr ||
       meta->getInputNum() != input->num_buffers) {
     logerr (TAG, "Invalid metadata info provided\n");
     return nullptr;
   }
 
-  SegmentTable * segt = mem_->allocSegmentTable ();
-  if (segt != nullptr) {
-    const generic_buffer *first = &input->bufs[0];
-    if (first->type == BUFFER_DMABUF) { /** WIP */
-      segt->setDmabuf (first->dmabuf);
-      segt->setOffset (first->offset);
-      segt->setSize (meta->getBufferSize());
-    } else {
-      int status = segt->alloc ();
-      if (status != 0) {
-        logerr (TAG, "Failed to allocate segment table: %d\n", status);
-        delete segt;
-        return nullptr;
-      }
-    }
+  SegmentTable * segt = mem_->allocSegmentTable (new HWmemDevice);
+  int status = segt->alloc ();
+  if (status != 0) {
+    logerr (TAG, "Failed to allocate segment table: %d\n", status);
+    goto delete_segt;
   }
 
-  int status = segt->createSegments (meta);
+  status = segt->createSegments (model, input);
   if (status != 0) {
     logerr (TAG, "Failed to create segments: %d\n", status);
-    delete segt;
-    segt = nullptr;
+    goto delete_segt;
   }
 
   return segt;
+
+delete_segt:
+  delete segt;
+  return nullptr;
 }
 
 /** @brief implementation of TRIV2's setModel () */
@@ -1259,21 +1279,29 @@ TrinityVision2::setModel (const generic_buffer *model_buf, Model ** model_ptr)
        if (model_buf == nullptr || model_ptr == nullptr)
                return -EINVAL;
 
-  Model *model = mem_->allocModel ();
-  if (model == nullptr) {
-    logerr (TAG, "Failed to allocate model\n");
-    return -ENOMEM;
-  }
+  Model *model;
+  int status;
 
-  int status = 0;
   switch (model_buf->type) {
   case BUFFER_DMABUF:
+    model = mem_->allocModel (new HWmemExternal);
+    if (model == nullptr) {
+      logerr (TAG, "Failed to allocate model\n");
+      return -ENOMEM;
+    }
+
     model->setDmabuf (model_buf->dmabuf);
     model->setOffset (model_buf->offset);
     model->setSize (model_buf->size);
     break;
   case BUFFER_FILE:
   case BUFFER_MAPPED:
+    model = mem_->allocModel (new HWmemDevice);
+    if (model == nullptr) {
+      logerr (TAG, "Failed to allocate model\n");
+      return -ENOMEM;
+    }
+
     status = model->alloc (model_buf->size);
     if (status != 0) {
       logerr (TAG, "Failed to allocate model: %d\n", status);
@@ -1287,8 +1315,7 @@ TrinityVision2::setModel (const generic_buffer *model_buf, Model ** model_ptr)
     }
     break;
   default:
-    status = -EINVAL;
-    goto delete_exit;
+    return -EINVAL;
   }
 
   status = model->setMetadata (model->getData());
@@ -1330,30 +1357,35 @@ TrinityVision2::run (npu_input_opmode opmode, const Model *model,
     return -EINVAL;
 
   /** this device uses segment table */
-  const Metadata * meta = model->getMetadata ();
-  SegmentTable * segt = prepareSegmentTable (meta, input);
+  SegmentTable * segt = prepareSegmentTable (model, input);
   if (segt == nullptr) {
     logerr (TAG, "Failed to create segment table instance\n");
     return -EINVAL;
   }
 
-  /** set weight data */
-  HWmem * weight = segt->getWeightSegment ();
-  if (weight == nullptr) {
-    logerr (TAG, "Failed to get a weight segment\n");
-    return -ENOENT;
+  if (!model->isExternal ()) {
+    /** set weight data */
+    HWmem * weight = segt->getWeightSegment ();
+    if (weight == nullptr) {
+      logerr (TAG, "Failed to get a weight segment\n");
+      return -ENOENT;
+    }
+
+    const Metadata * meta = model->getMetadata ();
+    memcpy (weight->getData (), model->getWeightData (), meta->getWeightSize ());
   }
-  memcpy (weight->getData (), model->getWeightData (), meta->getWeightSize ());
 
-  /** extract input data */
-  for (uint32_t idx = 0; idx < input->num_buffers; idx++) {
-    auto func = std::bind (TrinityVision2::manipulateData, model, idx, true,
-        std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
-    int status = comm_.extractGenericBuffer (&input->bufs[idx],
-        segt->getInputSegment(idx)->getData(), func);
-    if (status != 0) {
-      logerr (TAG, "Failed to feed input segment: %d\n", status);
-      return status;
+  if (!segt->isExternal ()) {
+    /** extract input data */
+    for (uint32_t idx = 0; idx < input->num_buffers; idx++) {
+      auto func = std::bind (TrinityVision2::manipulateData, model, idx, true,
+          std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
+      int status = comm_.extractGenericBuffer (&input->bufs[idx],
+          segt->getInputSegment(idx)->getData(), func);
+      if (status != 0) {
+        logerr (TAG, "Failed to feed input segment: %d\n", status);
+        return status;
+      }
     }
   }
 
@@ -1414,15 +1446,23 @@ TrinityAsr::run (npu_input_opmode opmode, const Model *model,
   if (opmode != NPUINPUT_HOST)
     return -EINVAL;
 
+  Buffer * buffer;
+  int status;
   /** ASR does not require model and support only a single tensor */
   const generic_buffer *first_buf = &input->bufs[0];
-  Buffer * buffer = mem_->allocBuffer ();
-  int status;
   if (first_buf->type == BUFFER_DMABUF) {
+    buffer = mem_->allocBuffer (new HWmemExternal);
+    if (buffer == nullptr)
+      return -ENOMEM;
+
     buffer->setDmabuf (first_buf->dmabuf);
     buffer->setOffset (first_buf->offset);
     buffer->setSize (first_buf->size);
   } else {
+    buffer = mem_->allocBuffer (new HWmemDevice);
+    if (buffer == nullptr)
+      return -ENOMEM;
+
     status = buffer->alloc (first_buf->size);
     if (status != 0) {
       delete buffer;
@@ -1437,10 +1477,12 @@ TrinityAsr::run (npu_input_opmode opmode, const Model *model,
     return status;
   }
 
-  status = comm_.extractGenericBuffer (first_buf,
-      buffer->getInputTensor(0)->getData(), nullptr);
-  if (status != 0)
-    return status;
+  if (!buffer->isExternal ()) {
+    status = comm_.extractGenericBuffer (first_buf,
+        buffer->getInputTensor(0)->getData(), nullptr);
+    if (status != 0)
+      return status;
+  }
 
   Request *req = new Request (opmode);
   req->setBuffer (buffer);
index 91319f5..c17a43c 100644 (file)
@@ -161,7 +161,7 @@ class TrinityVision2 : public Device {
     static size_t manipulateData (const Model *model, uint32_t idx, bool is_input,
         void *dst, void *src, size_t size);
 
-    SegmentTable * prepareSegmentTable (const Metadata *meta, const input_buffers *input);
+    SegmentTable * prepareSegmentTable (const Model *model, const input_buffers *input);
 
     int setModel (const generic_buffer *model_buf, Model ** model_ptr);
     int run (npu_input_opmode opmode, const Model *model,
index 5e43f21..2bb17a1 100644 (file)
@@ -150,19 +150,25 @@ HWmemDevice::dealloc (HWmem *hwmem) const
 char *
 HWmemDevice::getData (HWmem *hwmem) const
 {
-  if (hwmem->getDriverAPI() == nullptr)
+  const DriverAPI * api = hwmem->getDriverAPI();
+  if (api == nullptr)
     return nullptr;
 
-  if (!(hwmem->getDmabuf() >= 0 && hwmem->getSize() > 0))
+  int dmabuf = hwmem->getDmabuf();
+  size_t size = hwmem->getSize();
+  size_t offset = hwmem->getOffset();
+  if (!(dmabuf >= 0 && size > 0))
     return nullptr;
 
   if (hwmem->getBaseAddr () == nullptr) {
-    void *addr = hwmem->getDriverAPI()->mmap (hwmem->getDmabuf(), ALIGNED_SIZE (hwmem->getSize()));
+    void *addr = api->mmap (dmabuf, ALIGNED_SIZE (offset + size));
     if (addr != nullptr)
       hwmem->setBaseAddr (addr);
+    else
+      return nullptr;
   }
 
-  return static_cast <char*> (hwmem->getBaseAddr ()) + hwmem->getOffset ();
+  return static_cast <char*> (hwmem->getBaseAddr ()) + offset;
 }
 
 /** Impl. of HWmemChunk */
@@ -194,8 +200,12 @@ HWmemChunk::getData (HWmem *hwmem) const
   if (parent->getDriverAPI() == nullptr)
     return nullptr;
 
+  char * data = parent->getData();
+  if (data == nullptr)
+    return nullptr;
+
   /** its base address is parent's getData() */
-  return parent->getData() + hwmem->getOffset ();
+  return data + hwmem->getOffset ();
 }
 
 /**
@@ -249,4 +259,37 @@ HWmemChunk::dealloc (HWmem *hwmem) const
   return 0;
 }
 
-/** Impl. of HWmemExternal: TODO */
+/** Impl. of HWmemExternal */
+
+/**
+ * @brief get user-accessible data address
+ * @param[in] hwmem the hwmem instance
+ * @return mapped address
+ */
+char *
+HWmemExternal::getData (HWmem *hwmem) const
+{
+  /**
+   * In fact, a driver API does not have a responsibility to support mmap()
+   * for an external dmabuf FD. But, let's just rely on the API for uniformity.
+   */
+  const DriverAPI * api = hwmem->getDriverAPI();
+  if (api == nullptr)
+    return nullptr;
+
+  int dmabuf = hwmem->getDmabuf();
+  size_t size = hwmem->getSize();
+  size_t offset = hwmem->getOffset();
+  if (!(dmabuf >= 0 && size > 0))
+    return nullptr;
+
+  if (hwmem->getBaseAddr () == nullptr) {
+    void *addr = api->mmap (dmabuf, ALIGNED_SIZE (offset + size));
+    if (addr != nullptr)
+      hwmem->setBaseAddr (addr);
+    else
+      return nullptr;
+  }
+
+  return static_cast <char*> (hwmem->getBaseAddr ()) + offset;
+}
index bc8d19b..a45b34c 100644 (file)
@@ -61,7 +61,8 @@ class HWmemChunk : public HWmemImpl {
 /** HWmem shared by external dmabuf */
 class HWmemExternal : public HWmemImpl {
   public:
-    /** TODO */
+    /** only support getData() */
+    char *getData (HWmem *hwmem) const;
 };
 
 /** HWmem base class */
@@ -77,6 +78,7 @@ class HWmem {
     size_t getOffset () const { return offset_; }
     void *getBaseAddr () const { return base_addr_; }
     HWmem *getParent () const { return parent_; }
+    bool isExternal () const { return nullptr != dynamic_cast<const HWmemExternal *>(impl_); }
 
     /** set private member variables */
     void setDriverAPI (const DriverAPI* api) { api_ = api; }
index 4c996c9..e3d7ac6 100644 (file)
@@ -26,7 +26,7 @@ class MemDefault : public MemAllocator {
   public:
     MemDefault (const DriverAPI *api) : MemAllocator (api) {}
 
-    /** For library users */
+    /** For library users. its backing storage is always a device driver */
     HWmem* allocMemory (size_t size) {
       HWmem * hwmem = new HWmem (new HWmemDevice);
       hwmem->setDriverAPI (api_);
@@ -42,24 +42,30 @@ class MemDefault : public MemAllocator {
     }
 
     /** For internal npu-engine components */
-    Model* allocModel () {
-      Model * model = new Model (new HWmemDevice);
+    Model* allocModel (HWmemImpl *impl) {
+      if (impl == nullptr)
+        return nullptr;
+
+      Model * model = new Model (impl);
       model->setDriverAPI (api_);
-      /** lazy allocation */
       return model;
     }
 
-    Buffer *allocBuffer () {
-      Buffer * buffer = new Buffer (new HWmemDevice);
+    Buffer *allocBuffer (HWmemImpl *impl) {
+      if (impl == nullptr)
+        return nullptr;
+
+      Buffer * buffer = new Buffer (impl);
       buffer->setDriverAPI (api_);
-      /** lazy allocation */
       return buffer;
     }
 
-    SegmentTable * allocSegmentTable () {
-      SegmentTable * segt = new SegmentTable (new HWmemDevice);
+    SegmentTable * allocSegmentTable (HWmemImpl *impl) {
+      if (impl == nullptr)
+        return nullptr;
+
+      SegmentTable * segt = new SegmentTable (impl);
       segt->setDriverAPI (api_);
-      /** lazy allocation */
       return segt;
     }
 };
index 52dda6d..a6201e6 100644 (file)
@@ -51,9 +51,9 @@ class MemAllocator {
      * Below does not allocate actual memory as we don't know the type of generic buffer
      * from users yet. So, the caller should explictly call alloc() when required.
      */
-    virtual Model * allocModel () { return nullptr; }
-    virtual Buffer * allocBuffer () { return nullptr; }
-    virtual SegmentTable * allocSegmentTable () { return nullptr; }
+    virtual Model * allocModel (HWmemImpl *impl) { return nullptr; }
+    virtual Buffer * allocBuffer (HWmemImpl *impl) { return nullptr; }
+    virtual SegmentTable * allocSegmentTable (HWmemImpl *impl) { return nullptr; }
 
   protected:
     const DriverAPI *api_;  /**< driver API that this allocator is supposed to access */
index 25c7bd1..03a4a64 100644 (file)
@@ -44,19 +44,41 @@ SegmentTable::~SegmentTable ()
 }
 
 /**
+ * @brief set dmabuf fd to each segment slot
+ * @param[in] hwmem hwmem instance
+ * @param[in] slot segment slot
+ */
+void
+SegmentTable::setSegmentSlot (HWmem *hwmem, int slot)
+{
+  segments_.push_back (hwmem);
+
+  /**
+   * Set its dmabuf FD to each slot in the segment table. The kernel driver has
+   * a responsibility to convert these dmabuf FDs to actual physical addresses,
+   * accessed by NPU hardware, before invoking an inference. Note that each entry
+   * in segmen table has 64-bits slot.
+   */
+  unsigned int unsigned_dmabuf = hwmem->getDmabuf ();
+  reinterpret_cast<uint64_t *>(getData())[slot] = unsigned_dmabuf;
+}
+
+/**
  * @brief create segments according to on metadata info
- * @param[in] meta the metadata
+ * @param[in] model the model instance
+ * @param[in] input user-provided input buffers
  * @return 0 if no error, otherwise a negative errno
  * @note we assume that # weight segments is always 1. (fix impl when it's changed)
  */
 int
-SegmentTable::createSegments (const Metadata *meta)
+SegmentTable::createSegments (const Model *model, const input_buffers *input)
 {
-  if (meta == nullptr) {
-    logerr (TAG, "No metadata provided\n");
+  if (model == nullptr || input == nullptr) {
+    logerr (TAG, "No model/input provided\n");
     return -EINVAL;
   }
 
+  const Metadata *meta = model->getMetadata ();
   /** check npubinfmt version */
   if (meta->getVersion () != SUPPORTED_VERSION) {
     logerr (TAG, "Suppory only npubinfmt version %d\n",
@@ -74,52 +96,78 @@ SegmentTable::createSegments (const Metadata *meta)
     return -EINVAL;
   }
 
-  HWmem * hwmem;
-  int status;
-  for (uint32_t i = 0; i < meta->getSegmentsNum (); i++) {
-    uint32_t size = meta->getSegmentSize (i);
-
-    hwmem = new HWmem (new HWmemDevice);
-    hwmem->setDriverAPI (getDriverAPI ());
-
-    status = hwmem->alloc (size);
-    if (status != 0) {
-      logerr (TAG, "Failed to allocate %uth segment with size %u: %d\n",
-          i, size, status);
-      segments_.clear ();
-      return -EINVAL;
-    }
-    segments_.push_back (hwmem);
-
-    /** should be successful otherwise alloc() was failed */
-    assert (hwmem->getDmabuf () >= 0);
-
-    /**
-     * Set its dmabuf FD to each slot in the segment table. The kernel driver has
-     * a responsibility to convert these dmabuf FDs to actual physical addresses,
-     * accessed by NPU hardware, before invoking an inference. Note that each entry
-     * in segmen table has 64-bits slot.
-     */
-    unsigned int unsigned_dmabuf = hwmem->getDmabuf ();
-    reinterpret_cast<uint64_t *>(getData())[i] = unsigned_dmabuf;
-  }
-
-  weight_seg_idx_ = new uint32_t [num_weight_segments_];
-  input_seg_idx_ = new uint32_t [num_input_segments_];
-  output_seg_idx_ = new uint32_t [num_output_segments_];
-
   /** segment index validity is already checked in Metadata's checkSanity () */
   num_total_segments_ = meta->getSegmentsNum ();
   num_weight_segments_ = 1;
   num_input_segments_ = meta->getInputNum ();
   num_output_segments_ = meta->getOutputNum ();
 
+  weight_seg_idx_ = new uint32_t [num_weight_segments_];
+  input_seg_idx_ = new uint32_t [num_input_segments_];
+  output_seg_idx_ = new uint32_t [num_output_segments_];
+
   weight_seg_idx_[0] = meta->getWeightSegmentIndex ();
   for (uint32_t i = 0; i < num_input_segments_; i++)
     input_seg_idx_[i] = meta->getInputSegmentIndex (i);
   for (uint32_t i = 0; i < num_output_segments_; i++)
     output_seg_idx_[i] = meta->getOutputSegmentIndex (i);
 
+  /** we need to consider the scenario that users provide input/weight as external dmabuf */
+  for (uint32_t i = 0; i < meta->getSegmentsNum (); i++) {
+    uint32_t size = meta->getSegmentSize (i);
+    HWmem * hwmem;
+
+    /** check model weight */
+    if (i == weight_seg_idx_[0] && model->isExternal ()) {
+      hwmem = new HWmem (new HWmemExternal);
+      hwmem->setDriverAPI (getDriverAPI ());
+      hwmem->setDmabuf (model->getDmabuf ());
+      /** offset of weight base addr */
+      hwmem->setOffset (model->getOffset () + meta->getMetaSize () + meta->getProgramSize ());
+      hwmem->setSize (meta->getWeightSize ());
+
+      setSegmentSlot (hwmem, i);
+      continue;
+    }
+
+    /** check input */
+    bool is_set = false;
+    for (uint32_t j = 0; j < num_input_segments_; j++) {
+      /** user provides a external segment */
+      if (i == input_seg_idx_[j]) {
+        if (input->bufs[j].type == BUFFER_DMABUF) {
+          hwmem = new HWmem (new HWmemExternal);
+          hwmem->setDriverAPI (getDriverAPI ());
+          hwmem->setDmabuf (input->bufs[j].dmabuf);
+          hwmem->setOffset (input->bufs[j].offset);
+          hwmem->setSize (input->bufs[j].size);
+
+          setSegmentSlot (hwmem, i);
+          is_set = true;
+          break;
+        }
+      }
+    }
+
+    if (is_set == false) {
+      hwmem = new HWmem (new HWmemDevice);
+      hwmem->setDriverAPI (getDriverAPI ());
+
+      int status = hwmem->alloc (size);
+      if (status != 0) {
+        logerr (TAG, "Failed to allocate %uth segment with size %u: %d\n",
+            i, size, status);
+        segments_.clear ();
+        delete [] weight_seg_idx_;
+        delete [] input_seg_idx_;
+        delete [] output_seg_idx_;
+        return -EINVAL;
+      }
+
+      setSegmentSlot (hwmem, i);
+    }
+  }
+
   return 0;
 }
 
index ab2df52..8d7401c 100644 (file)
@@ -28,7 +28,7 @@ class SegmentTable : public HWmem {
 
     /** Override alloc () and DO NOT support a variable-sized segment table */
     int alloc (size_t = 0) { return static_cast<HWmem *>(this)->alloc (PAGE_SIZE); }
-    int createSegments (const Metadata *meta);
+    int createSegments (const Model *model, const input_buffers *input);
 
     HWmem *getWeightSegment (uint32_t idx = 0); /** maybe, # weight segments is 1 */
     HWmem *getInputSegment (uint32_t idx);
@@ -40,6 +40,8 @@ class SegmentTable : public HWmem {
     uint32_t getNumOutputSegments () const { return num_output_segments_; }
 
   private:
+    void setSegmentSlot (HWmem *hwmem, int slot);
+
     std::vector <HWmem *> segments_;  /**< segments */
 
     uint32_t * weight_seg_idx_;       /**< segment index for weight */
index e507e17..90abdf2 100644 (file)
@@ -22,8 +22,6 @@
 #include <npubinfmt.h>
 #include <ne_test_utils.h>
 
-#include "../../src/core/ne-conf.h"
-
 /** @brief compare output result with the golden data */
 static int
 compare_result (output_buffers *output)
@@ -78,7 +76,7 @@ run_inference (npudev_h dev)
   };
 
   /** make model */
-  model = make_model (&meta, BUFFER_DMABUF);
+  model = make_model (dev, &meta, BUFFER_DMABUF);
   if (!model)
     goto out;
 
@@ -86,16 +84,15 @@ run_inference (npudev_h dev)
   input.num_buffers = 1;
   input.bufs[0].size = buffer_size;
   input.bufs[0].type = BUFFER_DMABUF;
-  if ((err = allocInputBuffers (&input)) != 0)
+  if ((err = allocNPU_inputBuffers (dev, &input)) != 0)
     goto out_free_model;
 
   /** register the model to NPU Engine */
-  if ((err = registerNPUmodel(dev, model, &model_id)) != 0)
+  if ((err = registerNPUmodel (dev, model, &model_id)) != 0)
     goto out_free_all;
 
   /** run NPU inference */
   fill_input_data (&input.bufs[0], meta.input_offset, meta.input_size);
-
   if ((err = runNPU_sync (dev, model_id, &input, &output)) != 0)
     goto out_unregister;
 
@@ -103,11 +100,11 @@ run_inference (npudev_h dev)
   err = compare_result (&output);
 
 out_unregister:
-  unregisterNPUmodel(dev, model_id);
+  unregisterNPUmodel (dev, model_id);
 out_free_all:
-  cleanInputBuffers (&input);
+  cleanNPU_inputBuffers (dev, &input);
 out_free_model:
-  destroy_model (model);
+  destroy_model (dev, model);
 out:
   return err;
 }
@@ -116,19 +113,20 @@ out:
 int
 main (int argc, char **argv)
 {
+  dev_type type = NPUCOND_TRIV_CONN_SOCIP;
   npudev_h dev;
-  int num_devices = getnumNPUdevice();
+  int num_devices = getnumNPUdeviceByType (type);
   int result = -1;
 
   if (num_devices) {
-    if (getNPUdevice (&dev, num_devices - 1) == 0) {
+    if (getNPUdeviceByType (&dev, type, num_devices - 1) == 0) {
       result = run_inference (dev);
       if (result == 0) {
         fprintf(stderr, "[APPTEST] %s: PASSED\n", argv[0]);
       } else {
         fprintf(stderr, "[APPTEST] %s: FAILED (%d)\n", argv[0], result);
       }
-      free (dev);
+      putNPUdevice (dev);
     }
   }
 
index 752a9c3..0c7e9e8 100644 (file)
@@ -22,8 +22,6 @@
 #include <npubinfmt.h>
 #include <ne_test_utils.h>
 
-#include "../../src/core/ne-conf.h"
-
 /** @brief compare output result with the golden data */
 static int
 compare_result (output_buffers *output)
@@ -81,7 +79,7 @@ run_inference (npudev_h dev)
   };
 
   /** make model */
-  model = make_model (&meta, BUFFER_DMABUF);
+  model = make_model (dev, &meta, BUFFER_DMABUF);
   if (!model)
     goto out;
 
@@ -89,16 +87,15 @@ run_inference (npudev_h dev)
   input.num_buffers = 1;
   input.bufs[0].size = buffer_size;
   input.bufs[0].type = BUFFER_MAPPED;
-  if ((err = allocInputBuffers (&input)) != 0)
+  if ((err = allocNPU_inputBuffers (dev, &input)) != 0)
     goto out_free_model;
 
   /** register the model to NPU Engine */
-  if ((err = registerNPUmodel(dev, model, &model_id)) != 0)
+  if ((err = registerNPUmodel (dev, model, &model_id)) != 0)
     goto out_free_all;
 
   /** run NPU inference */
   fill_input_data (&input.bufs[0], meta.input_offset, meta.input_size);
-
   if ((err = runNPU_sync (dev, model_id, &input, &output)) != 0)
     goto out_unregister;
 
@@ -106,11 +103,11 @@ run_inference (npudev_h dev)
   err = compare_result (&output);
 
 out_unregister:
-  unregisterNPUmodel(dev, model_id);
+  unregisterNPUmodel (dev, model_id);
 out_free_all:
-  cleanInputBuffers (&input);
+  cleanNPU_inputBuffers (dev, &input);
 out_free_model:
-  destroy_model (model);
+  destroy_model (dev, model);
 out:
   return err;
 }
@@ -119,19 +116,20 @@ out:
 int
 main (int argc, char **argv)
 {
+  dev_type type = NPUCOND_TRIV_CONN_SOCIP;
   npudev_h dev;
-  int num_devices = getnumNPUdevice();
+  int num_devices = getnumNPUdeviceByType (type);
   int result = -1;
 
   if (num_devices) {
-    if (getNPUdevice (&dev, num_devices - 1) == 0) {
+    if (getNPUdeviceByType (&dev, type, num_devices - 1) == 0) {
       result = run_inference (dev);
       if (result == 0) {
         fprintf(stderr, "[APPTEST] %s: PASSED\n", argv[0]);
       } else {
         fprintf(stderr, "[APPTEST] %s: FAILED (%d)\n", argv[0], result);
       }
-      free (dev);
+      putNPUdevice (dev);
     }
   }
 
index 6c7151a..f2fd0c6 100644 (file)
@@ -292,6 +292,7 @@ out_free_meta:
 int
 main (int argc, char **argv)
 {
+  dev_type type = NPUCOND_TRIV_CONN_SOCIP;
   npudev_h dev;
 
   if (argc != 2) {
@@ -299,7 +300,7 @@ main (int argc, char **argv)
     return 0;
   }
 
-  if (!(getnumNPUdevice() > 0 && getNPUdevice (&dev, 0) == 0)) {
+  if (!(getnumNPUdeviceByType (type) > 0 && getNPUdeviceByType (&dev, type, 0) == 0)) {
     fprintf(stderr, "No available NPU device\n");
     return -1;
   }
index 82ee8c8..e79789a 100644 (file)
@@ -23,26 +23,26 @@ executable ('apptest_async_callbacks',
   install_dir : join_paths(ne_bindir, 'apptests')
 )
 
-#executable ('apptest_dmabuf_model',
-#  'dmabuf_model.c',
-#  include_directories : ne_apptest_inc,
-#  dependencies : ne_test_utils_dep,
-#  link_with : ne_library_shared,
-#  install : true,
-#  install_rpath : ne_libdir,
-#  install_dir : join_paths(ne_bindir, 'apptests')
-#)
-#
-#executable ('apptest_dmabuf_buffers',
-#  'dmabuf_buffers.c',
-#  include_directories : ne_apptest_inc,
-#  dependencies : ne_test_utils_dep,
-#  link_with : ne_library_shared,
-#  install : true,
-#  install_rpath : ne_libdir,
-#  install_dir : join_paths(ne_bindir, 'apptests')
-#)
-#
+executable ('apptest_dmabuf_model',
+  'dmabuf_model.c',
+  include_directories : ne_apptest_inc,
+  dependencies : ne_test_utils_dep,
+  link_with : ne_library_shared,
+  install : true,
+  install_rpath : ne_libdir,
+  install_dir : join_paths(ne_bindir, 'apptests')
+)
+
+executable ('apptest_dmabuf_buffers',
+  'dmabuf_buffers.c',
+  include_directories : ne_apptest_inc,
+  dependencies : ne_test_utils_dep,
+  link_with : ne_library_shared,
+  install : true,
+  install_rpath : ne_libdir,
+  install_dir : join_paths(ne_bindir, 'apptests')
+)
+
 executable ('apptest_example_visa',
   'example_visa.c',
   include_directories : ne_apptest_inc,
index d8a3458..02ee87a 100644 (file)
@@ -43,11 +43,11 @@ TEST (ne_core_mem_test, mem_default_primitives)
   EXPECT_EQ (mem->deallocMemory (hwmem->getDmabuf ()), 0);
   delete hwmem;
 
-  Model * model = mem->allocModel ();
+  Model * model = mem->allocModel (new HWmemDevice);
   EXPECT_NE (model, nullptr);
   delete model;
 
-  Buffer * buffer = mem->allocBuffer ();
+  Buffer * buffer = mem->allocBuffer (new HWmemDevice);
   EXPECT_NE (buffer, nullptr);
   delete buffer;
 }
@@ -69,8 +69,13 @@ TEST (ne_core_mem_test, mem_pool_primitives_n)
 
   EXPECT_EQ (mem->allocMemory (4096), nullptr);
   EXPECT_NE (mem->deallocMemory (0), 0);
-  EXPECT_EQ (mem->allocModel (), nullptr);
-  EXPECT_EQ (mem->allocBuffer (), nullptr);
+  EXPECT_EQ (mem->allocModel (nullptr), nullptr);
+  EXPECT_EQ (mem->allocBuffer (nullptr), nullptr);
+
+  HWmemImpl *impl = new HWmemDevice;
+  EXPECT_EQ (mem->allocModel (impl), nullptr);
+  EXPECT_EQ (mem->allocBuffer (impl), nullptr);
+  delete impl;
 }
 
 /**
index 8b7e72b..db6f809 100644 (file)
@@ -56,6 +56,16 @@ create_metadata_v3 (npubin_meta & meta)
   meta.output_seg_idx[2] = 3;
 }
 
+/** @brief set dummy input buffer info */
+static void
+set_input_buffers (input_buffers &input)
+{
+  input.num_buffers = 2;
+  input.bufs[0].type = BUFFER_MAPPED;
+  input.bufs[0].size = 0x1000;
+  input.bufs[1].type = BUFFER_MAPPED;
+  input.bufs[1].size = 0x1000;
+}
 /**
  * @brief test createSegments() of SegmentTable
  */
@@ -74,8 +84,11 @@ TEST (ne_core_segment_table_test, create_segments)
   std::unique_ptr<SegmentTable> table (new SegmentTable (new HWmemDevice));
   table->setDriverAPI (api.get ());
 
+  input_buffers input;
+  set_input_buffers (input);
+
   EXPECT_EQ (table->alloc (), 0);
-  EXPECT_EQ (table->createSegments (model->getMetadata ()), 0);
+  EXPECT_EQ (table->createSegments (model.get(), &input), 0);
 
   EXPECT_EQ (table->getNumTotalSegments (), meta.segment_num);
   EXPECT_EQ (table->getNumWeightSegments (), 1);
@@ -107,8 +120,11 @@ TEST (ne_core_segment_table_test, primitives_n)
   std::unique_ptr<SegmentTable> table (new SegmentTable (new HWmemDevice));
   table->setDriverAPI (api.get ());
 
+  input_buffers input;
+  set_input_buffers (input);
+
   ASSERT_EQ (table->alloc (), 0);
-  ASSERT_EQ (table->createSegments (model->getMetadata ()), 0);
+  ASSERT_EQ (table->createSegments (model.get(), &input), 0);
 
   /** get segment with invalid index */
   EXPECT_EQ (table->getWeightSegment (1), nullptr);
@@ -133,28 +149,31 @@ TEST (ne_core_segment_table_test, create_segments_n)
 
   std::unique_ptr<SegmentTable> table (new SegmentTable (new HWmemDevice));
 
-  /** without metadata */
-  EXPECT_NE (table->createSegments (nullptr), 0);
+  input_buffers input;
+  set_input_buffers (input);
+
+  /** without model or input */
+  EXPECT_NE (table->createSegments (nullptr, nullptr), 0);
+  EXPECT_NE (table->createSegments (nullptr, &input), 0);
+  EXPECT_NE (table->createSegments (model.get(), nullptr), 0);
 
   /** before setting driver API */
-  EXPECT_NE (table->createSegments (model->getMetadata ()), 0);
+  EXPECT_NE (table->createSegments (model.get(), &input), 0);
 
   table->setDriverAPI (api.get ());
   /** before allocating segment table */
-  EXPECT_NE (table->createSegments (model->getMetadata ()), 0);
+  EXPECT_NE (table->createSegments (model.get(), &input), 0);
 
   ASSERT_EQ (table->alloc (), 0);
   /** invalid metadata (only support v3) */
   meta.magiccode = NPUBIN_MAGICCODE | 0x2;  /* v2 */
-  std::unique_ptr<Metadata> metadata = Metadata::extractMetadata (&meta);
-  ASSERT_NE (metadata.get(), nullptr);
-  EXPECT_NE (table->createSegments (metadata.get()), 0);
+  ASSERT_EQ (model->setMetadata (&meta), 0);
+  EXPECT_NE (table->createSegments (model.get(), &input), 0);
   /** invalid metadata (zero size) */
   meta.magiccode = NPUBIN_MAGICCODE | 0x3;  /* v3 */
   meta.segment_size[1] = 0;
-  metadata = Metadata::extractMetadata (&meta);
-  ASSERT_NE (metadata.get(), nullptr);
-  EXPECT_NE (table->createSegments (metadata.get()), 0);
+  ASSERT_EQ (model->setMetadata (&meta), 0);
+  EXPECT_NE (table->createSegments (model.get(), &input), 0);
 }
 
 /**