allocator_ion.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. /*
  2. * Copyright 2015 Rockchip Electronics Co. LTD
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #define MODULE_TAG "mpp_ion"
  17. #include <unistd.h>
  18. #include <string.h>
  19. #include <errno.h>
  20. #include <sys/ioctl.h>
  21. #include <sys/mman.h>
  22. #if defined(ARMLINUX)
  23. #include <ion.h>
  24. #else
  25. #include "ion.h"
  26. #endif
  27. #include "os_mem.h"
  28. #include "allocator_ion.h"
  29. #include "mpp_env.h"
  30. #include "mpp_mem.h"
  31. #include "mpp_debug.h"
  32. #include "mpp_common.h"
  33. #include "mpp_thread.h"
  34. static RK_U32 ion_debug = 0;
  35. static pthread_once_t once = PTHREAD_ONCE_INIT;
  36. static pthread_mutex_t scandir_lock;
  37. #define ION_FUNCTION (0x00000001)
  38. #define ION_DEVICE (0x00000002)
  39. #define ION_CLINET (0x00000004)
  40. #define ION_IOCTL (0x00000008)
  41. #define ION_DETECT_IOMMU_DISABLE (0x0) /* use ION_HEAP_TYPE_DMA */
  42. #define ION_DETECT_IOMMU_ENABLE (0x1) /* use ION_HEAP_TYPE_SYSTEM */
  43. #define ION_DETECT_NO_DTS (0x2) /* use ION_HEAP_TYPE_CARVEOUT */
  44. #define ion_dbg(flag, fmt, ...) _mpp_dbg(ion_debug, flag, fmt, ## __VA_ARGS__)
  45. #define ion_dbg_f(flag, fmt, ...) _mpp_dbg_f(ion_debug, flag, fmt, ## __VA_ARGS__)
  46. #define ion_dbg_func(fmt, ...) ion_dbg_f(ION_FUNCTION, fmt, ## __VA_ARGS__)
  47. static int ion_ioctl(int fd, int req, void *arg)
  48. {
  49. int ret = ioctl(fd, req, arg);
  50. if (ret < 0) {
  51. mpp_err("ion_ioctl %x failed with code %d: %s\n", req,
  52. ret, strerror(errno));
  53. return -errno;
  54. }
  55. return ret;
  56. }
  57. static int ion_alloc(int fd, size_t len, size_t align, unsigned int heap_mask,
  58. unsigned int flags, ion_user_handle_t *handle)
  59. {
  60. int ret = -EINVAL;
  61. struct ion_allocation_data data = {
  62. .len = len,
  63. .align = align,
  64. .heap_id_mask = heap_mask,
  65. .flags = flags,
  66. };
  67. ion_dbg_func("enter: fd %d len %d align %d heap_mask %x flags %x",
  68. fd, len, align, heap_mask, flags);
  69. if (handle) {
  70. ret = ion_ioctl(fd, ION_IOC_ALLOC, &data);
  71. if (ret >= 0)
  72. *handle = data.handle;
  73. }
  74. ion_dbg_func("leave: ret %d\n", ret);
  75. return ret;
  76. }
  77. static int ion_free(int fd, ion_user_handle_t handle)
  78. {
  79. int ret;
  80. struct ion_handle_data data = {
  81. .handle = handle,
  82. };
  83. ion_dbg_func("enter: fd %d\n", fd);
  84. ret = ion_ioctl(fd, ION_IOC_FREE, &data);
  85. ion_dbg_func("leave: ret %d\n", ret);
  86. return ret;
  87. }
  88. static int ion_map_fd(int fd, ion_user_handle_t handle, int *map_fd)
  89. {
  90. int ret;
  91. struct ion_fd_data data = {
  92. .handle = handle,
  93. };
  94. if (map_fd == NULL)
  95. return -EINVAL;
  96. ret = ion_ioctl(fd, ION_IOC_MAP, &data);
  97. if (ret < 0)
  98. return ret;
  99. *map_fd = data.fd;
  100. if (*map_fd < 0) {
  101. mpp_err("map ioctl returned negative fd\n");
  102. return -EINVAL;
  103. }
  104. return 0;
  105. }
  106. static int ion_mmap(int fd, size_t length, int prot, int flags, off_t offset,
  107. void **ptr)
  108. {
  109. static unsigned long pagesize_mask = 0;
  110. if (ptr == NULL)
  111. return -EINVAL;
  112. if (!pagesize_mask)
  113. pagesize_mask = sysconf(_SC_PAGESIZE) - 1;
  114. offset = offset & (~pagesize_mask);
  115. *ptr = mmap(NULL, length, prot, flags, fd, offset);
  116. if (*ptr == MAP_FAILED) {
  117. mpp_err("mmap failed: %s\n", strerror(errno));
  118. *ptr = NULL;
  119. return -errno;
  120. }
  121. return 0;
  122. }
  123. #include <dirent.h>
  124. static const char *search_name = NULL;
  125. static int _compare_name(const struct dirent *dir)
  126. {
  127. if (search_name && strstr(dir->d_name, search_name))
  128. return 1;
  129. return 0;
  130. }
  131. static void scandir_lock_init(void)
  132. {
  133. pthread_mutexattr_t attr;
  134. pthread_mutexattr_init(&attr);
  135. pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
  136. pthread_mutex_init(&scandir_lock, &attr);
  137. pthread_mutexattr_destroy(&attr);
  138. }
  139. /*
  140. * directory search function:
  141. * search directory with dir_name on path.
  142. * if found match dir append name on path and return
  143. *
  144. * return 0 for failure
  145. * return positive value for length of new path
  146. */
  147. static RK_S32 find_dir_in_path(char *path, const char *dir_name,
  148. size_t max_length)
  149. {
  150. struct dirent **dir;
  151. RK_S32 path_len = strnlen(path, max_length);
  152. RK_S32 new_path_len = 0;
  153. RK_S32 n;
  154. pthread_once(&once, scandir_lock_init);
  155. pthread_mutex_lock(&scandir_lock);
  156. search_name = dir_name;
  157. n = scandir(path, &dir, _compare_name, alphasort);
  158. if (n <= 0) {
  159. mpp_log("scan %s for %s return %d\n", path, dir_name, n);
  160. } else {
  161. mpp_assert(n == 1);
  162. new_path_len = path_len;
  163. new_path_len += snprintf(path + path_len, max_length - path_len - 1,
  164. "/%s", dir[0]->d_name);
  165. free(dir[0]);
  166. free(dir);
  167. }
  168. search_name = NULL;
  169. pthread_mutex_unlock(&scandir_lock);
  170. return new_path_len;
  171. }
  172. #define MAX_PATH_NAME_SIZE 256
  173. static RK_S32 check_sysfs_iommu()
  174. {
  175. RK_U32 i = 0;
  176. RK_U32 dts_info_found = 0;
  177. RK_U32 ion_info_found = 0;
  178. RK_S32 ret = ION_DETECT_IOMMU_DISABLE;
  179. char path[MAX_PATH_NAME_SIZE];
  180. static char *dts_devices[] = {
  181. "vpu_service",
  182. "hevc_service",
  183. "rkvdec",
  184. "rkvenc",
  185. "vpu_combo",
  186. };
  187. static char *system_heaps[] = {
  188. "vmalloc",
  189. "system-heap",
  190. };
  191. mpp_env_get_u32("ion_debug", &ion_debug, 0);
  192. #ifdef SOFIA_3GR_LINUX
  193. return ret;
  194. #endif
  195. for (i = 0; i < MPP_ARRAY_ELEMS(dts_devices); i++) {
  196. snprintf(path, sizeof(path), "/proc/device-tree");
  197. if (find_dir_in_path(path, dts_devices[i], sizeof(path))) {
  198. if (find_dir_in_path(path, "iommu_enabled", sizeof(path))) {
  199. FILE *iommu_fp = fopen(path, "rb");
  200. if (iommu_fp) {
  201. RK_U32 iommu_enabled = 0;
  202. if (fread(&iommu_enabled, sizeof(RK_U32), 1, iommu_fp))
  203. mpp_log("%s iommu_enabled %d\n", dts_devices[i],
  204. (iommu_enabled > 0));
  205. fclose(iommu_fp);
  206. if (iommu_enabled)
  207. ret = ION_DETECT_IOMMU_ENABLE;
  208. }
  209. dts_info_found = 1;
  210. break;
  211. }
  212. }
  213. }
  214. if (!dts_info_found) {
  215. for (i = 0; i < MPP_ARRAY_ELEMS(system_heaps); i++) {
  216. snprintf(path, sizeof(path), "/sys/kernel/debug/ion/heaps");
  217. if (find_dir_in_path(path, system_heaps[i], sizeof(path))) {
  218. mpp_log("%s found\n", system_heaps[i]);
  219. ret = ION_DETECT_IOMMU_ENABLE;
  220. ion_info_found = 1;
  221. break;
  222. }
  223. }
  224. }
  225. if (!dts_info_found && !ion_info_found) {
  226. mpp_err("can not find any hint from all possible devices\n");
  227. ret = ION_DETECT_NO_DTS;
  228. }
  229. return ret;
  230. }
  231. typedef struct {
  232. size_t alignment;
  233. RK_S32 ion_device;
  234. MppAllocFlagType flags;
  235. } allocator_ctx_ion;
  236. static const char *dev_ion = "/dev/ion";
  237. static RK_S32 ion_heap_id = -1;
  238. static RK_U32 ion_heap_mask = (1 << ION_HEAP_TYPE_SYSTEM);
  239. static pthread_mutex_t lock;
  240. static MPP_RET allocator_ion_open(void **ctx, size_t alignment, MppAllocFlagType flags)
  241. {
  242. RK_S32 fd;
  243. allocator_ctx_ion *p;
  244. if (NULL == ctx) {
  245. mpp_err("os_allocator_open Android do not accept NULL input\n");
  246. return MPP_ERR_NULL_PTR;
  247. }
  248. *ctx = NULL;
  249. fd = open(dev_ion, O_RDWR | O_CLOEXEC);
  250. if (fd < 0) {
  251. mpp_err("open %s failed!\n", dev_ion);
  252. return MPP_ERR_UNKNOW;
  253. }
  254. ion_dbg(ION_DEVICE, "open ion dev fd %d\n", fd);
  255. p = mpp_malloc(allocator_ctx_ion, 1);
  256. if (NULL == p) {
  257. close(fd);
  258. mpp_err("os_allocator_open Android failed to allocate context\n");
  259. return MPP_ERR_MALLOC;
  260. } else {
  261. /*
  262. * do heap id detection here:
  263. * if there is no vpu_service use default ION_HEAP_TYPE_SYSTEM_CONTIG
  264. * if there is vpu_service then check the iommu_enable status
  265. */
  266. pthread_mutex_lock(&lock);
  267. if (ion_heap_id < 0) {
  268. int detect_result = check_sysfs_iommu();
  269. const char *heap_name = NULL;
  270. switch (detect_result) {
  271. case ION_DETECT_IOMMU_DISABLE : {
  272. ion_heap_mask = (1 << ION_HEAP_TYPE_DMA);
  273. ion_heap_id = ION_HEAP_TYPE_DMA;
  274. heap_name = "ION_HEAP_TYPE_DMA";
  275. } break;
  276. case ION_DETECT_IOMMU_ENABLE : {
  277. ion_heap_mask = (1 << ION_HEAP_TYPE_SYSTEM);
  278. ion_heap_id = ION_HEAP_TYPE_SYSTEM;
  279. heap_name = "ION_HEAP_TYPE_SYSTEM";
  280. } break;
  281. case ION_DETECT_NO_DTS : {
  282. ion_heap_mask = (1 << ION_HEAP_TYPE_CARVEOUT);
  283. ion_heap_id = ION_HEAP_TYPE_CARVEOUT;
  284. heap_name = "ION_HEAP_TYPE_CARVEOUT";
  285. } break;
  286. default : {
  287. mpp_err("invalid detect result %d\n", detect_result);
  288. ion_heap_mask = (1 << ION_HEAP_TYPE_DMA);
  289. ion_heap_id = ION_HEAP_TYPE_DMA;
  290. heap_name = "ION_HEAP_TYPE_DMA";
  291. } break;
  292. }
  293. mpp_log("using ion heap %s\n", heap_name);
  294. }
  295. pthread_mutex_unlock(&lock);
  296. p->alignment = alignment;
  297. p->flags = flags;
  298. p->ion_device = fd;
  299. *ctx = p;
  300. }
  301. return MPP_OK;
  302. }
  303. static MPP_RET allocator_ion_alloc(void *ctx, MppBufferInfo *info)
  304. {
  305. MPP_RET ret = MPP_OK;
  306. int fd = -1;
  307. ion_user_handle_t hnd = -1;
  308. allocator_ctx_ion *p = NULL;
  309. if (NULL == ctx) {
  310. mpp_err("os_allocator_close Android do not accept NULL input\n");
  311. return MPP_ERR_NULL_PTR;
  312. }
  313. ion_dbg_func("enter: ctx %p size %d\n", ctx, info->size);
  314. p = (allocator_ctx_ion *)ctx;
  315. ret = ion_alloc(p->ion_device, info->size, p->alignment, ion_heap_mask,
  316. 0, &hnd);
  317. if (ret)
  318. mpp_err_f("ion_alloc failed ret %d\n", ret);
  319. else {
  320. ret = ion_map_fd(p->ion_device, hnd, &fd);
  321. if (ret)
  322. mpp_err_f("ion_map_fd failed ret %d\n", ret);
  323. }
  324. info->fd = fd;
  325. info->ptr = NULL;
  326. info->hnd = (void *)(intptr_t)hnd;
  327. ion_dbg_func("leave: ret %d handle %d fd %d\n", ret, hnd, fd);
  328. return ret;
  329. }
  330. static MPP_RET allocator_ion_import(void *ctx, MppBufferInfo *data)
  331. {
  332. MPP_RET ret = MPP_NOK;
  333. allocator_ctx_ion *p = (allocator_ctx_ion *)ctx;
  334. struct ion_fd_data fd_data;
  335. ion_dbg_func("enter: ctx %p dev %d fd %d size %d\n",
  336. ctx, p->ion_device, data->fd, data->size);
  337. fd_data.fd = data->fd;
  338. ret = ion_ioctl(p->ion_device, ION_IOC_IMPORT, &fd_data);
  339. if (0 > fd_data.handle) {
  340. mpp_err_f("fd %d import failed for %s\n", data->fd, strerror(errno));
  341. goto RET;
  342. }
  343. data->hnd = (void *)(intptr_t)fd_data.handle;
  344. ret = ion_map_fd(p->ion_device, fd_data.handle, &data->fd);
  345. data->ptr = NULL;
  346. RET:
  347. ion_dbg_func("leave: ret %d handle %d\n", ret, data->hnd);
  348. return ret;
  349. }
  350. static MPP_RET allocator_ion_mmap(void *ctx, MppBufferInfo *data)
  351. {
  352. MPP_RET ret = MPP_OK;
  353. if (NULL == ctx) {
  354. mpp_err_f("do not accept NULL input\n");
  355. return MPP_ERR_NULL_PTR;
  356. }
  357. ion_dbg_func("enter: ctx %p fd %d size %d\n", ctx, data->fd, data->size);
  358. if (NULL == data->ptr)
  359. ret = ion_mmap(data->fd, data->size, PROT_READ | PROT_WRITE,
  360. MAP_SHARED, 0, &data->ptr);
  361. ion_dbg_func("leave: ret %d ptr %p\n", ret, data->ptr);
  362. return ret;
  363. }
  364. static MPP_RET allocator_ion_free(void *ctx, MppBufferInfo *data)
  365. {
  366. allocator_ctx_ion *p = NULL;
  367. if (NULL == ctx) {
  368. mpp_err_f("do not accept NULL input\n");
  369. return MPP_ERR_NULL_PTR;
  370. }
  371. ion_dbg_func("enter: ctx %p fd %d ptr %p size %d\n",
  372. ctx, data->fd, data->ptr, data->size);
  373. p = (allocator_ctx_ion *)ctx;
  374. if (data->ptr) {
  375. munmap(data->ptr, data->size);
  376. data->ptr = NULL;
  377. }
  378. if (data->fd > 0) {
  379. close(data->fd);
  380. data->fd = -1;
  381. }
  382. if (data->hnd) {
  383. ion_free(p->ion_device, (ion_user_handle_t)((intptr_t)data->hnd));
  384. data->hnd = NULL;
  385. }
  386. ion_dbg_func("leave\n");
  387. return MPP_OK;
  388. }
  389. static MPP_RET allocator_ion_close(void *ctx)
  390. {
  391. int ret;
  392. allocator_ctx_ion *p;
  393. if (NULL == ctx) {
  394. mpp_err("os_allocator_close Android do not accept NULL input\n");
  395. return MPP_ERR_NULL_PTR;
  396. }
  397. ion_dbg_func("enter: ctx\n", ctx);
  398. p = (allocator_ctx_ion *)ctx;
  399. ret = close(p->ion_device);
  400. mpp_free(p);
  401. if (ret < 0)
  402. ret = (MPP_RET) - errno;
  403. ion_dbg_func("leave: ret %d\n", ret);
  404. return ret;
  405. }
  406. static MppAllocFlagType os_allocator_ion_flags(void *ctx)
  407. {
  408. allocator_ctx_ion *p = (allocator_ctx_ion *)ctx;
  409. return p ? p->flags : MPP_ALLOC_FLAG_NONE;
  410. }
  411. os_allocator allocator_ion = {
  412. .type = MPP_BUFFER_TYPE_ION,
  413. .open = allocator_ion_open,
  414. .close = allocator_ion_close,
  415. .alloc = allocator_ion_alloc,
  416. .free = allocator_ion_free,
  417. .import = allocator_ion_import,
  418. .release = allocator_ion_free,
  419. .mmap = allocator_ion_mmap,
  420. .flags = os_allocator_ion_flags,
  421. };