[Utils/Cuse] Implement run_input ioctl and connect libmrpsim
authorDongju Chae <dongju.chae@samsung.com>
Tue, 20 Apr 2021 06:26:05 +0000 (15:26 +0900)
committer파리차이카푸르/On-Device Lab(SR)/Engineer/삼성전자 <pk.kapoor@samsung.com>
Tue, 27 Apr 2021 09:40:27 +0000 (18:40 +0900)
This patch implements run_input ioctl, connecting libmrpsim APIs.

Signed-off-by: Dongju Chae <dongju.chae@samsung.com>
src/core/ne-hw-input-service.cc
src/core/npu/NPUdrvAPI_triv2.cc
tests/unittests/ne_core_npu_test.cc
utils/trinity_cuse/trinity-cuse-triv2.cc
utils/trinity_cuse/trinity-cuse.cc
utils/trinity_cuse/trinity-cuse.h

index 788b4e4..f0c50cd 100644 (file)
@@ -129,9 +129,9 @@ HwInputService::invoke (const DriverAPI *api, const Model *model,
 
   input_config.hw_rdev = devnode_stat.st_rdev;
 #else
-  input_config.req_id = req_id;
   input_config.hw_rdev = 0; /* don't care */
 #endif
+  input_config.req_id = req_id;
 
   /** run the inference with the input */
   ret = api->runInput (&input_config);
index 36394eb..143500c 100644 (file)
@@ -280,6 +280,7 @@ TrinityVision2API::dealloc (int dmabuf, bool contiguous) const {
 
   hwmem.type = contiguous ? HWMEM_DMA_CONT : HWMEM_DMA_IOMMU;
 
+  /* translate dmabuf FDs to cuse-compatible ones */
   if (is_cuse_) {
     CuseElement *elem = cuse_map.find (dmabuf);
     if (elem == nullptr)
@@ -402,9 +403,32 @@ TrinityVision2API::registerModel (model_config_t *model_config,
       return -EINVAL;
   } /** else: don't care */
 
+  /* translate dmabuf FDs to cuse-compatible ones */
+  int dbuf_fd = model_config->dbuf_fd;
+  int metadata_dbuf_fd = model_config->metadata_dbuf_fd;
+
+  if (is_cuse_) {
+    CuseElement *prog = cuse_map.find (dbuf_fd);
+    if (prog == nullptr)
+      return -ENOENT;
+
+    CuseElement *meta = cuse_map.find (metadata_dbuf_fd);
+    if (meta == nullptr)
+      return -ENOENT;
+
+    model_config->dbuf_fd = prog->getDbufFD ();
+    model_config->metadata_dbuf_fd = meta->getDbufFD ();
+  }
+
   ret =
       ioctl (this->getDeviceFD (), TRINITY_IOCTL_REGISTER_MODEL, model_config);
 
+  /* rollback FDs to prevent any side-effects */
+  if (is_cuse_) {
+    model_config->dbuf_fd = dbuf_fd;
+    model_config->metadata_dbuf_fd = metadata_dbuf_fd;
+  }
+
   if (ret != 0)
     return -errno;
 
@@ -465,6 +489,39 @@ TrinityVision2API::runInput (input_config_t *input_config) const {
     input_config->task_handle = val.task_handle;
   }
 
+  /* translate dmabuf FDs to cuse-compatible ones */
+  if (is_cuse_) {
+    CuseElement *elem;
+    void *segt;
+
+    elem = cuse_map.find (input_config->dbuf_fd);
+    if (elem == nullptr)
+      return -ENOENT;
+
+    segt = this->mmap (input_config->dbuf_fd, PAGE_SIZE);
+    if (segt == nullptr)
+      return -EINVAL;
+
+    input_config->dbuf_fd = elem->getDbufFD ();
+    for (uint32_t i = 0; i < input_config->num_segments; i++) {
+      int dbuf_fd = ((int *) segt)[i];
+      if (dbuf_fd < 0) {
+        this->munmap (segt, PAGE_SIZE);
+        return -EINVAL;
+      }
+
+      elem = cuse_map.find (dbuf_fd);
+      if (elem == nullptr) {
+        this->munmap (segt, PAGE_SIZE);
+        return -ENOENT;
+      }
+
+      ((int *) segt)[i] = elem->getDbufFD ();
+    }
+
+    this->munmap (segt, PAGE_SIZE);
+  }
+
   ret = ioctl (this->getDeviceFD (), TRINITY_IOCTL_RUN_INPUT, input_config);
   if (ret != 0)
     return -errno;
index f65c9f6..bb34422 100644 (file)
@@ -510,6 +510,7 @@ TEST (ne_core_npu_test, run_inference_triv2_n) {
   model.dbuf_fd = model_dmabuf;
   model.program_offset_addr = 0;
   model.program_size = 4096;
+  model.metadata_dbuf_fd = model_dmabuf; /* dummy */
 
   EXPECT_EQ (api->registerModel (&model, TEST_NPU_VERSION), 0);
 
index 674001c..1fe0466 100644 (file)
 #include <sys/types.h>
 
 #include "trinity-cuse.h"
+#include <triv2profile.h>
+
+#define ENV_PREFIX_SHARE "NE_PREFIX_SHARE"
+#define ENV_PREFIX_PROFILE "NE_PREFIX_PROFILE"
+#define DEFAULT_PREFIX_SHARE NE_PREFIX "/share"
+#define DEFAULT_PREFIX_PROFILE "/tmp"
+
+#define TRIV2_MAX_SEGMENTS 256
+
+static const char *prefix_share;
+static const char *prefix_profile;
 
 /**
  * @brief Emulated Dmabuf Impl.
  */
 class EmulDmabuf {
  public:
-  EmulDmabuf (int app_id) : fd_ (-1), shm_fd_ (-1), size_ (0) {
+  EmulDmabuf (int app_id) : fd_ (-1), shm_fd_ (-1), size_ (0), addr_ (nullptr) {
     std::string name;
 
     fd_ = global_fd_.fetch_add (1);
@@ -47,6 +58,9 @@ class EmulDmabuf {
     if (shm_fd_ < 0)
       return;
 
+    if (addr_ != nullptr)
+      munmap (addr_, size_);
+
     shm_unlink (shm_name_.c_str ());
     close (shm_fd_);
   }
@@ -63,9 +77,14 @@ class EmulDmabuf {
       return err;
 
     size_ = size;
+    addr_ = mmap (NULL, size_, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd_, 0);
+    if (addr_ == MAP_FAILED)
+      return -errno;
+
     return 0;
   }
 
+  void *getData () const { return addr_; }
   size_t getSize () const { return size_; }
   int getFD () const { return fd_; }
 
@@ -76,6 +95,7 @@ class EmulDmabuf {
   int shm_fd_;
   std::string shm_name_;
   size_t size_;
+  void *addr_;
 };
 
 /**
@@ -114,6 +134,11 @@ class EmulModel {
   }
 
   uint64_t getID () const { return id_; }
+  uint32_t getVersion () const { return version_; }
+  int getDbufFD () const { return dbuf_fd_; }
+  int getMetaDbufFD () const { return metadata_dbuf_fd_; }
+  uint64_t getProgramSize () const { return program_size_; }
+  uint64_t getProgramOffset () const { return program_offset_addr_; }
 
   friend bool operator== (EmulModel &l, EmulModel &r) {
     std::unique_lock<std::mutex> lock_l (l.mutex_);
@@ -157,12 +182,11 @@ class EmulModel {
   uint64_t metadata_ext_size_;
 };
 
-/** @brief Global file descriptor */
-std::atomic<int> EmulDmabuf::global_fd_ (0);
+std::atomic<int> EmulDmabuf::global_fd_ (1);
 /** @brief Global mapping of dmabuf */
 ThreadSafeMap<int, EmulDmabuf> global_dmabuf_map;
 /** @brief Global model id */
-std::atomic<uint64_t> EmulModel::global_id_ (0);
+std::atomic<uint64_t> EmulModel::global_id_ (1);
 /** @brief Global mapping of models */
 ThreadSafeMap<uint64_t, EmulModel> global_model_map;
 
@@ -296,6 +320,164 @@ triv2_deregister_model (trinity_cuse_context *ctx, const uint64_t model_id) {
   return 0;
 }
 
+/**
+ * @brief TRIV2 impl. of run_input ioctl()
+ */
+static int
+triv2_run_input (trinity_cuse_context *ctx,
+                 const struct trinity_ioctl_input *in,
+                 struct trinity_ioctl_input *out) {
+  EmulDmabuf *dbuf_input = global_dmabuf_map.find (in->dbuf_fd);
+  if (dbuf_input == nullptr)
+    return -ENOENT;
+
+  EmulModel *model = global_model_map.find (in->model_id);
+  if (model == nullptr)
+    return -ENOENT;
+
+  if (model->getVersion () != 3)
+    return -EINVAL;
+
+  EmulDmabuf *dbuf_model = global_dmabuf_map.find (model->getDbufFD ());
+  if (dbuf_model == nullptr)
+    return -ENOENT;
+
+  EmulDmabuf *dbuf_meta = global_dmabuf_map.find (model->getMetaDbufFD ());
+  if (dbuf_meta == nullptr)
+    return -ENOENT;
+
+  memcpy (out, in, sizeof (*in));
+
+  size_t prog_size = model->getProgramSize ();
+  uint32_t num_segs = in->num_segments;
+
+  /* skip */
+  if (prog_size == 0)
+    return 0;
+
+  if (num_segs == 0)
+    return -EINVAL;
+
+  if (num_segs > TRIV2_MAX_SEGMENTS)
+    return -ERANGE;
+
+  void *input = dbuf_input->getData ();
+  char *prog = ((char *) dbuf_model->getData ()) + model->getProgramOffset ();
+  char *meta = (char *) dbuf_meta->getData ();
+  char **segt = new char *[num_segs];
+  uint32_t tops;
+
+  for (uint32_t i = 0; i < num_segs; i++) {
+    int dbuf_fd = reinterpret_cast<int *> (input)[i];
+    uint32_t offset =
+        reinterpret_cast<uint32_t *> ((char *) input + PAGE_SIZE / 2)[i];
+
+    EmulDmabuf *dbuf_seg = global_dmabuf_map.find (dbuf_fd);
+    if (dbuf_seg == nullptr) {
+      delete[] segt;
+      return -EINVAL;
+    }
+
+    segt[i] = static_cast<char *> (dbuf_seg->getData ()) + offset;
+  }
+
+  triv2_get_tops (ctx, &tops);
+
+  std::string cmd_path = NE_PREFIX "/share";
+  if (tops == 2)
+    cmd_path += "/mRPsim/triv2_2tops.cmd";
+  else
+    cmd_path += "/mRPsim/triv2.cmd";
+
+  std::string prof_path = "/tmp";
+  prof_path += "/ne_profile." + std::to_string (in->req_id);
+
+  run_triv2_emul (prog, segt, meta, cmd_path.c_str (), prof_path.c_str ());
+
+  delete[] segt;
+  return 0;
+}
+
+/**
+ * @brief TRIV2 impl. of stop_requests ioctl()
+ */
+static int
+triv2_stop_requests (trinity_cuse_context *ctx) {
+  /* this does not support scheduling. So, do nothing */
+  return 0;
+}
+
+/**
+ * @brief TRIV2 impl. of stop_request ioctl()
+ */
+static int
+triv2_stop_request (trinity_cuse_context *ctx, const int id) {
+  /* NYI, let's check again when reserved apps are running here */
+  return 0;
+}
+
+/**
+ * @brief TRIV2 impl. of stat_current_app ioctl()
+ */
+static int
+triv2_stat_current_app (trinity_cuse_context *ctx,
+                        struct trinity_ioctl_stat_app *stat) {
+  /* NYI */
+  memset (stat, '\x00', sizeof (*stat));
+  stat->app_id = ctx->app_id;
+  return 0;
+}
+
+/**
+ * @brief TRIV2 impl. of stat_apps ioctl()
+ */
+static int
+triv2_stat_apps (trinity_cuse_context *ctx,
+                 struct trinity_ioctl_stat_apps *stat) {
+  /* NYI */
+  memset (stat, '\x00', sizeof (*stat));
+  stat->num_apps = 0;
+  return 0;
+}
+
+/**
+ * @brief TRIV2 impl. of stat_reqs ioctl()
+ */
+static int
+triv2_stat_reqs (trinity_cuse_context *ctx,
+                 struct trinity_ioctl_stat_reqs *stat) {
+  /* NYI */
+  memset (stat, '\x00', sizeof (*stat));
+  stat->app_id = ctx->app_id;
+  stat->num_reqs = 0;
+  return 0;
+}
+
+/**
+ * @brief TRIV2 impl. of get_profile_meta ioctl()
+ */
+static int
+triv2_get_profile_meta (trinity_cuse_context *ctx,
+                        const struct trinity_ioctl_profile *in,
+                        struct trinity_ioctl_profile *out) {
+  /* NYI */
+  *out = *in;
+  out->total_ops = 0;
+  return 0;
+}
+
+/**
+ * @brief TRIV2 impl. of get_profile_buff ioctl()
+ */
+static int
+triv2_get_profile_buff (trinity_cuse_context *ctx,
+                        const struct trinity_ioctl_profile *in,
+                        struct trinity_ioctl_profile *out) {
+  /* NYI */
+  *out = *in;
+  return 0;
+}
+
 /** @brief The ioctl vtable for TRIV2 devices */
 static trinity_cuse_ioctl_vtable triv2_vtable{
     /* Device Info. */
@@ -309,14 +491,38 @@ static trinity_cuse_ioctl_vtable triv2_vtable{
     .hwmem_dealloc = triv2_hwmem_dealloc,
     .register_model = triv2_register_model,
     .deregister_model = triv2_deregister_model,
+    .run_input = triv2_run_input,
+    .stop_requests = triv2_stop_requests,
+    .stop_request = triv2_stop_request,
+    /* Device Statistics/Profile */
+    .stat_current_app = triv2_stat_current_app,
+    .stat_apps = triv2_stat_apps,
+    .stat_reqs = triv2_stat_reqs,
+    .get_profile_meta = triv2_get_profile_meta,
+    .get_profile_buff = triv2_get_profile_buff,
 };
 
 /** @brief Setting the ioctl vtable */
 bool
 set_ioctl_vtable_triv2 (trinity_cuse_ioctl_vtable **vtable) {
+  static char default_prefix_share[] = DEFAULT_PREFIX_SHARE;
+  static char default_prefix_profile[] = DEFAULT_PREFIX_PROFILE;
+
   if (vtable == nullptr)
     return false;
 
   *vtable = &triv2_vtable;
+
+  prefix_share = getenv (ENV_PREFIX_SHARE);
+  if (prefix_share == nullptr) {
+    prefix_share = default_prefix_share;
+    setenv (ENV_PREFIX_SHARE, default_prefix_share, 1);
+  }
+  prefix_profile = getenv (ENV_PREFIX_PROFILE);
+  if (prefix_profile == nullptr) {
+    prefix_profile = default_prefix_profile;
+    setenv (ENV_PREFIX_PROFILE, default_prefix_profile, 1);
+  }
+
   return true;
 }
index ef50153..9c92b69 100644 (file)
@@ -49,12 +49,7 @@ static void
 trinity_cuse_open (fuse_req_t req, struct fuse_file_info *fi) {
   trinity_cuse_context *ctx;
 
-  ctx = (trinity_cuse_context *) malloc (sizeof (*ctx));
-  if (ctx == NULL) {
-    fuse_reply_err (req, ENOMEM);
-    return;
-  }
-
+  ctx = new trinity_cuse_context;
   ctx->app_id = fuse_req_ctx (req)->pid;
 
   fi->fh = (uint64_t) ctx;
@@ -69,7 +64,7 @@ trinity_cuse_release (fuse_req_t req, struct fuse_file_info *fi) {
   trinity_cuse_context *ctx = (trinity_cuse_context *) fi->fh;
 
   if (ctx)
-    free (ctx);
+    delete ctx;
 
   fi->fh = 0;
   fuse_reply_err (req, 0);
@@ -94,6 +89,8 @@ trinity_cuse_ioctl (fuse_req_t req, int cmd, void *arg,
     return;
   }
 
+  std::unique_lock<std::mutex> lock (ctx->mutex);
+
   switch (cmd) {
     case TRINITY_IOCTL_GET_VERSION:
       if (!out_size) {
@@ -186,6 +183,84 @@ trinity_cuse_ioctl (fuse_req_t req, int cmd, void *arg,
         fuse_reply_ioctl (req, vtable->deregister_model (ctx, *val), NULL, 0);
       }
       break;
+    case TRINITY_IOCTL_RUN_INPUT:
+      if (!in_size || !out_size) {
+        struct iovec iov = {arg, sizeof (struct trinity_ioctl_input)};
+        fuse_reply_ioctl_retry (req, &iov, 1, &iov, 1);
+      } else {
+        const struct trinity_ioctl_input *in_val =
+            static_cast<const struct trinity_ioctl_input *> (in_buf);
+        struct trinity_ioctl_input out_val;
+        fuse_reply_ioctl (req, vtable->run_input (ctx, in_val, &out_val),
+                          &out_val, sizeof (out_val));
+      }
+      break;
+    case TRINITY_IOCTL_STOP_REQUESTS:
+      fuse_reply_ioctl (req, vtable->stop_requests (ctx), NULL, 0);
+      break;
+    case TRINITY_IOCTL_STOP_REQUEST:
+      if (!in_size) {
+        struct iovec iov = {arg, sizeof (int)};
+        fuse_reply_ioctl_retry (req, &iov, 1, NULL, 0);
+      } else {
+        const int *val = static_cast<const int *> (in_buf);
+        fuse_reply_ioctl (req, vtable->stop_request (ctx, *val), NULL, 0);
+      }
+      break;
+    case TRINITY_IOCTL_STAT_CURRENT_APP:
+      if (!out_size) {
+        struct iovec iov = {arg, sizeof (struct trinity_ioctl_stat_app)};
+        fuse_reply_ioctl_retry (req, NULL, 0, &iov, 1);
+      } else {
+        struct trinity_ioctl_stat_app val;
+        fuse_reply_ioctl (req, vtable->stat_current_app (ctx, &val), &val,
+                          sizeof (val));
+      }
+      break;
+    case TRINITY_IOCTL_STAT_APPS:
+      if (!out_size) {
+        struct iovec iov = {arg, sizeof (struct trinity_ioctl_stat_apps)};
+        fuse_reply_ioctl_retry (req, NULL, 0, &iov, 1);
+      } else {
+        struct trinity_ioctl_stat_apps val;
+        fuse_reply_ioctl (req, vtable->stat_apps (ctx, &val), &val,
+                          sizeof (val));
+      }
+      break;
+    case TRINITY_IOCTL_STAT_REQS:
+      if (!out_size) {
+        struct iovec iov = {arg, sizeof (struct trinity_ioctl_stat_reqs)};
+        fuse_reply_ioctl_retry (req, NULL, 0, &iov, 1);
+      } else {
+        struct trinity_ioctl_stat_reqs val;
+        fuse_reply_ioctl (req, vtable->stat_reqs (ctx, &val), &val,
+                          sizeof (val));
+      }
+      break;
+    case TRINITY_IOCTL_GET_PROFILE_META:
+      if (!in_size || !out_size) {
+        struct iovec iov = {arg, sizeof (struct trinity_ioctl_profile)};
+        fuse_reply_ioctl_retry (req, &iov, 1, &iov, 1);
+      } else {
+        const struct trinity_ioctl_profile *in_val =
+            static_cast<const struct trinity_ioctl_profile *> (in_buf);
+        struct trinity_ioctl_profile out_val;
+        fuse_reply_ioctl (req, vtable->get_profile_meta (ctx, in_val, &out_val),
+                          &out_val, sizeof (out_val));
+      }
+      break;
+    case TRINITY_IOCTL_GET_PROFILE_BUFF:
+      if (!in_size || !out_size) {
+        struct iovec iov = {arg, sizeof (struct trinity_ioctl_profile)};
+        fuse_reply_ioctl_retry (req, &iov, 1, &iov, 1);
+      } else {
+        const struct trinity_ioctl_profile *in_val =
+            static_cast<const struct trinity_ioctl_profile *> (in_buf);
+        struct trinity_ioctl_profile out_val;
+        fuse_reply_ioctl (req, vtable->get_profile_buff (ctx, in_val, &out_val),
+                          &out_val, sizeof (out_val));
+      }
+      break;
     /* NYI */
     default:
       fuse_reply_err (req, ENOTTY);
@@ -284,7 +359,7 @@ main (int argc, char **argv) {
     return 0;
 
   if (param.dev_name.empty ()) {
-    std::cerr << "[Error] Missing device name (e.g., --name=triv2)\n";
+    std::cerr << "[Error] Missing device name (e.g., triv2)\n";
     print_help_message ();
     return -EINVAL;
   }
index c211c64..b68618d 100644 (file)
 #define __TRINITY_FUSE_H__
 
 #include <misc/trinity.h>
+#include <mutex>
 
 /**
  * @brief The trinity context
  */
 typedef struct {
   int app_id;
+  std::mutex mutex;
   /* NYI */
 } trinity_cuse_context;
 
@@ -43,6 +45,21 @@ typedef struct {
                          const struct trinity_ioctl_model *,
                          struct trinity_ioctl_model *);
   int (*deregister_model) (trinity_cuse_context *, const uint64_t);
+  int (*run_input) (trinity_cuse_context *, const struct trinity_ioctl_input *,
+                    struct trinity_ioctl_input *);
+  int (*stop_requests) (trinity_cuse_context *);
+  int (*stop_request) (trinity_cuse_context *, const int);
+  /* Device Statistics/Profile */
+  int (*stat_current_app) (trinity_cuse_context *,
+                           struct trinity_ioctl_stat_app *);
+  int (*stat_apps) (trinity_cuse_context *, struct trinity_ioctl_stat_apps *);
+  int (*stat_reqs) (trinity_cuse_context *, struct trinity_ioctl_stat_reqs *);
+  int (*get_profile_meta) (trinity_cuse_context *,
+                           const struct trinity_ioctl_profile *,
+                           struct trinity_ioctl_profile *);
+  int (*get_profile_buff) (trinity_cuse_context *,
+                           const struct trinity_ioctl_profile *,
+                           struct trinity_ioctl_profile *);
 } trinity_cuse_ioctl_vtable;
 
 #endif