[Utils/Cuse] Implement hwmem alloc/dealloc via Cuse driver
authorDongju Chae <dongju.chae@samsung.com>
Tue, 20 Apr 2021 02:58:41 +0000 (11:58 +0900)
committer파리차이카푸르/On-Device Lab(SR)/Engineer/삼성전자 <pk.kapoor@samsung.com>
Tue, 20 Apr 2021 11:01:54 +0000 (20:01 +0900)
This patch implements hwmem alloc/dealloc via Cuse driver.

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

index a5fe299..131e975 100644 (file)
@@ -12,6 +12,8 @@
  * @note this input service does not use a thread pool.
  */
 
+#include <sys/types.h>
+#include <sys/stat.h>
 #include <unistd.h>
 
 #include "ne-inputservice.h"
index 1707df0..9b3fec4 100644 (file)
 #include <memory>
 #include <thread>
 
-#include <sys/user.h> /* PAGE_SIZE */
-/** the size of each allocation is aligned to PAGE_SIZE */
-#ifndef PAGE_SHIFT
-#define PAGE_SHIFT (12)
-#endif
-#ifndef PAGE_SIZE
-#define PAGE_SIZE (1UL << PAGE_SHIFT)
-#endif
-#ifndef PFN_UP
-#define PFN_UP(x) (((x) + PAGE_SIZE - 1) >> PAGE_SHIFT)
-#endif
-#define ALIGNED_SIZE(x) (PFN_UP(x) * PAGE_SIZE)
-
-typedef struct trinity_model model_config_t;
-typedef struct trinity_input input_config_t;
+typedef struct trinity_ioctl_model model_config_t;
+typedef struct trinity_ioctl_input input_config_t;
 typedef enum trinity_state device_state_t;
 typedef enum trinity_dev_type device_dev_t;
 
@@ -126,6 +113,8 @@ class DriverAPI {
     static uint32_t api_level_; /**< Trinity API level */
 };
 
+class CuseElement;
+
 /** @brief Driver APIs for TRIV2 */
 class TrinityVision2API : public DriverAPI {
   public:
@@ -171,7 +160,10 @@ class TrinityVision2API : public DriverAPI {
     static const std::string dev_node_base;
     static std::bitset<CHAR_BIT> dev_bitset;
 
+    static ThreadSafeMap<int, CuseElement> cuse_map;
+
     int sched_dev_fd_;
+    bool is_cuse_;
 
     std::fstream dev_ios_;
 };
index ce20b96..ea58f93 100644 (file)
@@ -20,12 +20,35 @@ constexpr size_t default_buf_size = (256 * PAGE_SIZE);
 const std::string TrinityVision2API::dev_node_base = "triv2";
 std::bitset<CHAR_BIT> TrinityVision2API::dev_bitset = 0;
 
+/** @brief Cuse-hwmem element */
+class CuseElement {
+  public:
+    CuseElement (int dbuf_fd, int shm_fd, const std::string & shm_name) :
+      dbuf_fd_ (dbuf_fd), shm_fd_ (shm_fd), shm_name_ (shm_name) {}
+
+    ~CuseElement () {
+      close (shm_fd_);
+      shm_unlink (shm_name_.c_str ());
+    }
+
+    int getDbufFD () { return dbuf_fd_; }
+    int getShmFD () { return shm_fd_; }
+    const std::string & getShmName () { return shm_name_; }
+
+  private:
+    int dbuf_fd_;
+    int shm_fd_;
+    const std::string shm_name_;
+};
+
+ThreadSafeMap<int, CuseElement> TrinityVision2API::cuse_map;
+
 /**
  * @brief constructor of the API instance for Trinity Vision driver
  * @param[in] dev_id device id
  */
 TrinityVision2API::TrinityVision2API (int dev_id) :
-  DriverAPI (dev_id), sched_dev_fd_ (-1)
+  DriverAPI (dev_id), sched_dev_fd_ (-1), is_cuse_ (false)
 {
   int num_devs;
 
@@ -80,18 +103,25 @@ TrinityVision2API::open ()
 int
 TrinityVision2API::checkSanity ()
 {
-  unsigned int lib_ver = trinity_gen_ver (TRINITY_DEV_VISION_2, VER_NE_MAJOR,
-      VER_NE_MINOR, VER_NE_EXTRA);
-  unsigned int min_ver = this->getDrvVersion();
+  int lib_ver = trinity_gen_ver (0, VER_NE_MAJOR, VER_NE_MINOR, VER_NE_EXTRA);
+  int min_ver = this->getDrvVersion();
+  enum trinity_dev_type type =
+         static_cast<enum trinity_dev_type> ((min_ver & TRINITY_MASK_DEV) >> TRINITY_SHIFT_DEV);
 
-  // Check if this device is for TRIV1
-  if ((lib_ver & TRINITY_MASK_DEV) != (min_ver & TRINITY_MASK_DEV))
+  /* Check if this device is a TRIV2-compatible one */
+  if (type != TRINITY_DEV_VISION2 && type != TRINITY_DEV_VISION2_CUSE)
     return -ENODEV;
 
-  // Check if the major version numbers are same
+  /* Check if this device is using a cuse-based driver */
+  if (type == TRINITY_DEV_VISION2_CUSE)
+    this->is_cuse_ = true;
+
+  min_ver &= ~TRINITY_MASK_DEV;
+  /* Check if the major version numbers are same */
   if ((lib_ver & TRINITY_MASK_MAJOR_VER) != (min_ver & TRINITY_MASK_MAJOR_VER))
     return -ENOTSUP;
 
+  /* Check if the library version is lower than the driver one */
   if (lib_ver < min_ver)
     return -ENOTSUP;
 
@@ -184,6 +214,9 @@ TrinityVision2API::alloc (size_t size, bool contiguous) const
   if (!this->initialized())
     return -EPERM;
 
+  if (size == 0 || size > UINT32_MAX)
+    return -EINVAL;
+
   hwmem.type = contiguous ? HWMEM_DMA_CONT : HWMEM_DMA_IOMMU;
   hwmem.size = size;
 
@@ -191,6 +224,36 @@ TrinityVision2API::alloc (size_t size, bool contiguous) const
   if (ret < 0)
     return -errno;
 
+  if (!is_cuse_)
+    return ret;
+
+  CuseElement *elem;
+  std::string shm_name;
+  int dbuf_fd = ret;
+  struct stat statbuf;
+
+  /* Let's use shared memory for emulated dmabuf */
+  shm_name = "/triv2_" + std::to_string (getpid ()) + "_" + std::to_string (dbuf_fd);
+  ret = shm_open (shm_name.c_str (), O_RDWR, 0666);
+  if (ret < 0)
+    return ret;
+
+  elem = new CuseElement (dbuf_fd, ret, shm_name);
+  if (fstat (ret, &statbuf) < 0) {
+    delete elem;
+    return -errno;
+  }
+
+  if (size != statbuf.st_size) {
+    delete elem;
+    return -EINVAL;
+  }
+
+  if (cuse_map.insert (ret, elem) < 0) {
+    delete elem;
+    return -EINVAL;
+  }
+
   return ret;
 }
 
@@ -210,7 +273,18 @@ TrinityVision2API::dealloc (int dmabuf, bool contiguous) const
     return -EPERM;
 
   hwmem.type = contiguous ? HWMEM_DMA_CONT : HWMEM_DMA_IOMMU;
-  hwmem.dbuf_fd = dmabuf;
+
+  if (is_cuse_) {
+    CuseElement *elem = cuse_map.find (dmabuf);
+    if (elem == nullptr)
+      return -ENOENT;
+
+    hwmem.dbuf_fd = elem->getDbufFD ();
+
+    cuse_map.remove (dmabuf);
+  } else {
+    hwmem.dbuf_fd = dmabuf;
+  }
 
   ret = ioctl (this->getDeviceFD (), TRINITY_IOCTL_HWMEM_DEALLOC, &hwmem);
   if (ret < 0)
@@ -259,10 +333,13 @@ TrinityVision2API::mmap (int dmabuf, size_t size) const
 {
   void *ret;
 
-  if (!this->initialized())
+  if (!this->initialized ())
+    return nullptr;
+
+  if (size != ALIGNED_SIZE (size))
     return nullptr;
 
-  ret = ::mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, dmabuf, 0);
+  ret = ::mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, dmabuf, 0);
   if (ret == MAP_FAILED)
     return nullptr;
 
@@ -472,7 +549,7 @@ TrinityVision2API::fpga_memcpy (int dmabuf, uint32_t offset, void *addr, size_t
 int
 TrinityVision2API::getProfile (int req_id, npu_profile *profile) const
 {
-  struct trinity_profile t_profile;
+  struct trinity_ioctl_profile t_profile;
   int ret = 0;
 
   if (profile == nullptr)
index 336052e..52ca000 100644 (file)
@@ -22,6 +22,7 @@ else
   ne_core_npu_dep = declare_dependency(
     sources : ne_core_npu_src,
     dependencies : [ne_core_utils_dep],
+    link_args : '-lrt',
     include_directories : [ne_common_inc, ne_core_npu_inc])
 endif
 
index 2a0edff..253aa6e 100644 (file)
 #include <condition_variable>
 #include <functional>
 
+#include <sys/user.h> /* PAGE_SIZE */
+
+#ifndef PAGE_SHIFT
+#define PAGE_SHIFT (12)
+#endif
+#ifndef PAGE_SIZE
+#define PAGE_SIZE (1UL << PAGE_SHIFT)
+#endif
+#ifndef PFN_UP
+#define PFN_UP(x) (((x) + PAGE_SIZE - 1) >> PAGE_SHIFT)
+#endif
+#define ALIGNED_SIZE(x) (PFN_UP(x) * PAGE_SIZE)
+
 /********************************************************************
  * Logging utilities                                                *
  ********************************************************************/
index da34450..459b6b6 100644 (file)
 
 #include <triv2profile.h>
 #include <ne-conf.h>
+#include <ne-utils.h>
+#include <atomic>
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/types.h>
 
 #include "trinity-cuse.h"
 
 /**
+ * @brief Emulated Dmabuf Impl.
+ */
+class EmulDmabuf {
+  public:
+    EmulDmabuf (int app_id) : fd_ (-1), shm_fd_ (-1), size_ (0) {
+      std::string name;
+
+      fd_ = global_fd_.fetch_add (1);
+
+      /* create a shared memory */
+      mode_t old_umask = umask (0);
+      shm_name_ = "/triv2_" + std::to_string (app_id) + "_" + std::to_string (fd_);
+      shm_fd_ = shm_open (shm_name_.c_str (), O_RDWR | O_CREAT, 0666);
+      umask (old_umask);
+    }
+
+    ~EmulDmabuf () {
+      if (shm_fd_ < 0)
+        return;
+
+      shm_unlink (shm_name_.c_str ());
+      close (shm_fd_);
+    }
+
+    int setSize (size_t size) {
+      int err;
+
+      if (shm_fd_ < 0)
+        return -EINVAL;
+
+      /* change file size */
+      err = ftruncate (shm_fd_, size);
+      if (err != 0)
+        return err;
+
+      size_ = size;
+      return 0;
+    }
+
+    size_t getSize () const { return size_; }
+    int getFD () const { return fd_; }
+
+  private:
+    static std::atomic<int> global_fd_;
+
+    int fd_;
+    int shm_fd_;
+    std::string shm_name_;
+    size_t size_;
+};
+
+/** @brief Global mapping of dmabuf */
+ThreadSafeMap<int, EmulDmabuf> global_dmabuf_map;
+/** @brief Global file descriptor */
+std::atomic<int> EmulDmabuf::global_fd_ (0);
+
+/**
  * @brief TRIV2 impl. of get_version ioctl()
  */
 static int
-triv2_get_version (uint32_t *version)
+triv2_get_version (trinity_cuse_context *ctx, uint32_t *version)
 {
-  *version = trinity_gen_ver (2, 2, 0, 0);
-  fprintf (stderr, "version: %x\n", *version);
+  *version = trinity_gen_ver (TRINITY_DEV_VISION2_CUSE, 2, 0, 0);
   return 0;
 }
 
@@ -31,7 +96,7 @@ triv2_get_version (uint32_t *version)
  * @brief TRIV2 impl. of get_api_level ioctl()
  */
 static int
-triv2_get_api_level (uint32_t *api_level)
+triv2_get_api_level (trinity_cuse_context *ctx, uint32_t *api_level)
 {
   *api_level = TRINITY_API_LEVEL;
   return 0;
@@ -41,7 +106,7 @@ triv2_get_api_level (uint32_t *api_level)
  * @brief TRIV2 impl. of get_state ioctl()
  */
 static int
-triv2_get_state (uint32_t *state)
+triv2_get_state (trinity_cuse_context *ctx, uint32_t *state)
 {
   *state = STATE_READY;
   return 0;
@@ -51,7 +116,7 @@ triv2_get_state (uint32_t *state)
  * @brief TRIV2 impl. of get_tops ioctl()
  */
 static int
-triv2_get_tops (uint32_t *tops)
+triv2_get_tops (trinity_cuse_context *ctx, uint32_t *tops)
 {
   *tops = 2;
   return 0;
@@ -61,20 +126,63 @@ triv2_get_tops (uint32_t *tops)
  * @brief TRIV2 impl. of get_dspm ioctl()
  */
 static int
-triv2_get_dspm (uint32_t *dspm)
+triv2_get_dspm (trinity_cuse_context *ctx, uint32_t *dspm)
 {
   *dspm = UINT32_MAX;
   return 0;
 }
 
+/**
+ * @brief TRIV2 impl. of hwmem_alloc ioctl()
+ */
+static int
+triv2_hwmem_alloc (trinity_cuse_context *ctx, const struct trinity_ioctl_hwmem * hwmem)
+{
+  EmulDmabuf *dmabuf;
+  size_t size;
+
+  size = ALIGNED_SIZE (hwmem->size);
+  if (size == 0)
+    return -EINVAL;
+
+  switch (hwmem->type) {
+    case HWMEM_DMA_CONT:
+    case HWMEM_DMA_IOMMU:
+      dmabuf = new EmulDmabuf (ctx->app_id);
+      if (dmabuf->setSize (size) != 0)
+        return -EINVAL;
+
+      if (global_dmabuf_map.insert (dmabuf->getFD (), dmabuf) != 0) {
+        delete dmabuf;
+        return -EINVAL;
+      }
+
+      return dmabuf->getFD ();
+    default:
+      return -ENOTTY;
+  }
+}
+
+/**
+ * @brief TRIV2 impl. of hwmem_dealloc ioctl()
+ */
+static int
+triv2_hwmem_dealloc (trinity_cuse_context *ctx, const struct trinity_ioctl_hwmem * hwmem)
+{
+  return global_dmabuf_map.remove (hwmem->dbuf_fd);
+}
+
 /** @brief The ioctl vtable for TRIV2 devices */
 static trinity_cuse_ioctl_vtable triv2_vtable {
+  /* Device Info. */
   .get_version = triv2_get_version,
   .get_api_level = triv2_get_api_level,
   .get_state = triv2_get_state,
   .get_tops = triv2_get_tops,
   .get_dspm = triv2_get_dspm,
-  /* NYI */
+  /* Device Control */
+  .hwmem_alloc = triv2_hwmem_alloc,
+  .hwmem_dealloc = triv2_hwmem_dealloc,
 };
 
 /** @brief Setting the ioctl vtable */
index 033cd21..84b3ee5 100644 (file)
@@ -57,7 +57,7 @@ trinity_cuse_open (fuse_req_t req, struct fuse_file_info *fi)
     return;
   }
 
-  std::cerr << "open!!\n";
+  ctx->app_id = fuse_req_ctx (req)->pid;
 
   fi->fh = (uint64_t) ctx;
   fuse_reply_open(req, fi);
@@ -86,7 +86,7 @@ trinity_cuse_ioctl (fuse_req_t req, int cmd, void *arg,
     struct fuse_file_info *fi, unsigned int flags,
     const void *in_buf, size_t in_size, size_t out_size)
 {
-  (void) fi;
+  trinity_cuse_context *ctx = (trinity_cuse_context *) fi->fh;
 
   if (flags & FUSE_IOCTL_COMPAT) {
     fuse_reply_err (req, ENOSYS);
@@ -105,7 +105,7 @@ trinity_cuse_ioctl (fuse_req_t req, int cmd, void *arg,
         fuse_reply_ioctl_retry (req, NULL, 0, &iov, 1);
       } else {
         uint32_t val;
-        vtable->get_version (&val);
+        vtable->get_version (ctx, &val);
         fuse_reply_ioctl (req, 0, &val, sizeof (val));
       }
       break;
@@ -115,7 +115,7 @@ trinity_cuse_ioctl (fuse_req_t req, int cmd, void *arg,
         fuse_reply_ioctl_retry (req, NULL, 0, &iov, 1);
       } else {
         uint32_t val;
-        vtable->get_api_level (&val);
+        vtable->get_api_level (ctx, &val);
         fuse_reply_ioctl (req, 0, &val, sizeof (val));
       }
       break;
@@ -125,7 +125,7 @@ trinity_cuse_ioctl (fuse_req_t req, int cmd, void *arg,
         fuse_reply_ioctl_retry (req, NULL, 0, &iov, 1);
       } else {
         uint32_t val;
-        vtable->get_state (&val);
+        vtable->get_state (ctx, &val);
         fuse_reply_ioctl (req, 0, &val, sizeof (val));
       }
       break;
@@ -135,7 +135,7 @@ trinity_cuse_ioctl (fuse_req_t req, int cmd, void *arg,
         fuse_reply_ioctl_retry (req, NULL, 0, &iov, 1);
       } else {
         uint32_t val;
-        vtable->get_tops (&val);
+        vtable->get_tops (ctx, &val);
         fuse_reply_ioctl (req, 0, &val, sizeof (val));
       }
       break;
@@ -145,17 +145,34 @@ trinity_cuse_ioctl (fuse_req_t req, int cmd, void *arg,
         fuse_reply_ioctl_retry (req, NULL, 0, &iov, 1);
       } else {
         uint32_t val;
-        vtable->get_dspm (&val);
+        vtable->get_dspm (ctx, &val);
         fuse_reply_ioctl (req, 0, &val, sizeof (val));
       }
       break;
+    case TRINITY_IOCTL_HWMEM_ALLOC:
+      if (!in_size) {
+        struct iovec iov = { arg, sizeof (struct trinity_ioctl_hwmem) };
+        fuse_reply_ioctl_retry (req, &iov, 1, NULL, 0);
+      } else {
+        const struct trinity_ioctl_hwmem *val =
+          static_cast<const struct trinity_ioctl_hwmem *> (in_buf);
+        fuse_reply_ioctl (req, vtable->hwmem_alloc (ctx, val), NULL, 0);
+      }
+      break;
+    case TRINITY_IOCTL_HWMEM_DEALLOC:
+      if (!in_size) {
+        struct iovec iov = { arg, sizeof (struct trinity_ioctl_hwmem) };
+        fuse_reply_ioctl_retry (req, &iov, 1, NULL, 0);
+      } else {
+        const struct trinity_ioctl_hwmem *val =
+          static_cast<const struct trinity_ioctl_hwmem *> (in_buf);
+        fuse_reply_ioctl (req, vtable->hwmem_dealloc (ctx, val), NULL, 0);
+      }
+      break;
     /* NYI */
     default:
       fuse_reply_err (req, ENOTTY);
-      break;
   }
-
-  fuse_reply_err (req, 0);
 }
 
 /** @brief Structure to describe parameters */
index 4272b04..51569a1 100644 (file)
@@ -28,12 +28,15 @@ typedef struct {
  * @brief The ioctl vtable of each device ioctl
  */
 typedef struct {
-  int (*get_version) (uint32_t *);
-  int (*get_api_level) (uint32_t *);
-  int (*get_state) (uint32_t *);
-  int (*get_tops) (uint32_t *);
-  int (*get_dspm) (uint32_t *);
-  /* NYI */
+  /* Device Info. */
+  int (*get_version) (trinity_cuse_context *, uint32_t *);
+  int (*get_api_level) (trinity_cuse_context *, uint32_t *);
+  int (*get_state) (trinity_cuse_context *, uint32_t *);
+  int (*get_tops) (trinity_cuse_context *, uint32_t *);
+  int (*get_dspm) (trinity_cuse_context *, uint32_t *);
+  /* Device Control */
+  int (*hwmem_alloc) (trinity_cuse_context *, const struct trinity_ioctl_hwmem *);
+  int (*hwmem_dealloc) (trinity_cuse_context *, const struct trinity_ioctl_hwmem *);
 } trinity_cuse_ioctl_vtable;
 
 #endif