본문 바로가기

Linux

Sharing a buffer between kernel-space and user-space

Sharing a buffer between kernel-space and user-space


ION memory allocator 관련 글은 LWN 를 번역한 The Android ION memory allocator 참조.


이번에는 user-space 에서 할당한 메모리를 kernel driver 에서 공유하여 write 를 하고 user에서 write 한 데이터가 잘 들어가 있는지 확인하는 수준(?)으로 진행한다.


% 모바일이나 특정 브라우저에서는 syntex highlight 가 지원이 안되어 textbox 에 소스가 나올 수도 있음.


Scenario

1. User 에서 ion("/dev/ion")을 open ==> alloc ==>  share 순으로 진행하여 공유할 ion buffer 를 생성 및 공유 준비

2. User application 에서 공유하려는 kernel driver("/dev/vion_dev")를 open : 여기서 vion_dev 는 내가 만든 device driver 이다. 

3. User 에서 만들어진 shared_fd 를 vion_dev driver 로 전달.

4. Kernel driver 에서 받은 shared_fd 를 이용해 import 하여 메모리를 공유할 준비.

5. User 영역에서 특정 string("ion buffer test magic string") 을 vion_dev driver 에 write 를 요청

6. Kernel driver 에서 shared buffer 에 magic string을 write

7. User 에서 mmap 으로 shared buffer 의 내용 확인.


먼저 User application을 살펴 보면,

아래의 ion_XXX interface 들은 https://android.googlesource.com/platform/system/core/+/android-4.1.2_r1/libion/ion.c 를 살펴 보면 내용을 볼 수 있다. 


  - User-space application

  1. #include <stdio.h>  
  2. #include <linux/ioctl.h>  
  3. #include <fcntl.h>  
  4. #include <errno.h>  
  5. #include <stdlib.h>  
  6. #include <sys/mman.h>  
  7. #include <string.h>  
  8.   
  9. #include <linux/ion.h>  
  10. #include <ion/ion.h>  
  11. #include <cutils/log.h>  
  12.   
  13. #include "ion_test_module.h"  
  14.   
  15. #define TEST_MODULE_NAME    "/dev/vion_dev"  
  16. #define DEFAULT_BUF_SIZE    128  
  17.   
  18.   
  19. int share_with_testdrv(int shared_fd)  
  20. {  
  21.     int test_fd;  
  22.     char buff[DEFAULT_BUF_SIZE] = "ion buffer test magic string";  
  23.     int size = 0;  
  24.     // TEST_MODULE_NAME : vion_dev 를 open  
  25.     test_fd = open(TEST_MODULE_NAME, O_RDWR);  
  26.   
  27.     if (test_fd < 0) {  
  28.         printf("Failed to open test driver(vion)\n");  
  29.         return 0;  
  30.     }     
  31.     // register ion client in driver with shared file descriptor  
  32.     ioctl(test_fd, VION_REGISTER_ION, &shared_fd);  
  33.     // write a string for confirm.  
  34.     size = write(test_fd, (char*)buff, DEFAULT_BUF_SIZE - 1);   
  35.   
  36.     if (size > 0) {  
  37.         printf("write \"%s\" string to ion test driver complete\n", buff);  
  38.     }     
  39.     // for sync??   
  40.     printf("sleep 1sec...\n");  
  41.     sleep(1);  
  42.   
  43.     close(test_fd);  
  44.   
  45.     return size;  
  46. }  
  47.   
  48. int main(int argc, char ** argv)  
  49. {  
  50.     struct ion_handle *ion_hnd;  
  51.     size_t size = DEFAULT_BUF_SIZE;  
  52.     int ion_fd, shared_fd, test_fd;  
  53.     int ret = 0;  
  54.     int write_size = 0;  
  55.     unsigned char *confirm_vaddr = NULL;  
  56.     // open ion device driver  
  57.     ion_fd = ion_open();  
  58.   
  59.     if (ion_fd < 0) {  
  60.         printf("Failed to open ion device driver\n");  
  61.         ret = ion_fd;  
  62.         goto out;  
  63.     }  
  64.     // allocation ion buffer with ION_HEAP_SYSTEM_CONTIG type.  
  65.     // ion driver 의 physical address 를 얻어오는 interface(ion_phys()) 는  
  66.     // ION_HEAP_SYSTEM 의 memory type은 지원하지 않음.  
  67.     ret = ion_alloc(ion_fd, size, 0, ION_HEAP_SYSTEM_CONTIG_MASK,  
  68.                     ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC,  
  69.                     &ion_hnd );  
  70.   
  71.     if (ret) {  
  72.         printf("ion_alloc failed\n");  
  73.         goto close_fd;  
  74.     }  
  75.     // get shared file descriptor  
  76.     ret = ion_share(ion_fd, ion_hnd, &shared_fd);  
  77.   
  78.     if (ret) {  
  79.         printf("ion_share failed %d\n", ret);  
  80.         goto close_ion;  
  81.     }  
  82.     // open test driver(vion_dev) and write test string.  
  83.     write_size = share_with_testdrv(shared_fd);  
  84.   
  85.     if (write_size <= 0) {  
  86.         printf("Failed to share with test drv\n");  
  87.         goto close_ion;  
  88.     }  
  89.     // mmap memory with shared file descriptor.  
  90.     confirm_vaddr = (unsigned char*)mmap( NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, shared_fd, 0 );  
  91.   
  92.     if (confirm_vaddr != NULL)  
  93.         // check a string which is written in ion test kernel driver.  
  94.         printf("confirm_vaddr is not NULL, confirm data string \"%s\"\n", confirm_vaddr);  
  95.     else  
  96.         printf("mmap failed\n");  
  97.   
  98.     if (confirm_vaddr)  
  99.         munmap((void*)confirm_vaddr, size);  
  100.   
  101.     printf("\nion test application terminated.\n");  
  102.   
  103. close_ion:  
  104.     ion_free(ion_fd, ion_hnd);  
  105. close_fd:  
  106.     close(ion_fd);  
  107. out:  
  108.     return ret;  
  109. }  


주석을 참조하고 Android ION 에 대한 내용의 글을 보면 이해가 쉬울 듯 한데..


  - Kernel-space driver

  1. #include <linux/device.h>  
  2. #include <linux/module.h>  
  3. #include <linux/kernel.h>  
  4. #include <linux/init.h>  
  5. #include <linux/cdev.h>  
  6.   
  7. #include <linux/fs.h>  
  8. #include <asm/uaccess.h>  
  9. #include <linux/export.h>  
  10. #include <asm/io.h>  
  11.   
  12. #include <linux/ion.h>  
  13.   
  14. #include "ion_test_module.h"  
  15.   
  16. #define DEVICE_NAME     "vion"  
  17.   
  18. static int major = 0, minor = 0;  
  19. static struct cdev vion_cdev;  
  20. static struct class *cl;  
  21.   
  22. static struct ion_client *m_client;  
  23. static struct ion_handle *m_handle;  
  24.   
  25. static int device_opened = 0;  
  26. extern struct ion_device *lg115x_dev;  
  27.   
  28. void *kernel_vaddr = NULL;  
  29.   
  30. static int vion_open(struct inode *inode, struct file *filp)  
  31. {  
  32. #ifdef DEBUG  
  33.     printk(KERN_INFO "vion device open\n");  
  34. #endif  
  35.   
  36.     if (device_opened > 0)  
  37.         return -EBUSY;  
  38.   
  39.     device_opened++;  
  40.   
  41.     return 0;  
  42. }  
  43.   
  44. static int vion_release(struct inode *inode, struct file *filp)  
  45. {  
  46.     // Already open?  
  47.     if (device_opened <= 0)  
  48.         return 0;  
  49.   
  50.     device_opened--;  
  51.   
  52.     return 0;  
  53. }  
  54.   
  55. static long vion_ioctl(struct file *f, unsigned int cmd, unsigned long arg)  
  56. {  
  57.     int result = 0;  
  58.   
  59.     switch(cmd) {  
  60.         case VION_REGISTER_ION: {  
  61.             int ion_register;  
  62.   
  63.             if (copy_from_user(&ion_register,  
  64.                         (int *)arg,  
  65.                         sizeof(int))) {  
  66.                 printk(KERN_ERR "cmd %d : Error get data from ion\n", cmd);  
  67.                 result = -EFAULT;  
  68.                 goto exit;  
  69.             }  
  70.   
  71.             if (m_client == NULL) {  
  72.                 // lg115x_dev is exported from driver/gpu/ion/lg115x/lg115x_ion.c  
  73.                 m_client = ion_client_create(lg115x_dev,  
  74.                             ION_HEAP_TYPE_SYSTEM_CONTIG,  
  75.                             "vion");  
  76.             }  
  77.   
  78.             if (m_client == NULL) {  
  79.                 printk(KERN_ERR "Failed to create ion client with lg115x interface\n");  
  80.                 result = -EFAULT;  
  81.                 goto exit;  
  82.             }  
  83.             // get handle by ion_import_dma_buf() call.  
  84.             m_handle = ion_import_dma_buf(m_client, ion_register);  
  85.             return 0;  
  86.         }  
  87.         // 아직 필요하진 않지만(ion buffer는 user level에서 free해주니까) register와 pair로   
  88.         // 작성됨.     
  89.         case VION_UNREGISTER_ION: {  
  90.             int ion_register;  
  91.   
  92.             if (copy_from_user(&ion_register,  
  93.                         (int *)arg,  
  94.                         sizeof(int))) {  
  95.                 printk(KERN_ERR "cmd %d : Error get data from ion\n", cmd);  
  96.                 result = -EFAULT;  
  97.                 goto exit;  
  98.             }  
  99.   
  100.             if (m_client == NULL) {  
  101.                 printk(KERN_ERR "Failed to free with ion client\n");  
  102.                 result = -EFAULT;  
  103.                 goto exit;  
  104.             }  
  105.   
  106.             ion_free(m_client, m_handle);  
  107.             return 0;  
  108.         }  
  109.     }  
  110.   
  111. exit:  
  112.     return result;  
  113. }  
  114.   
  115. size_t get_vaddr_from_ion_buffer(void)  
  116. {  
  117.     ion_phys_addr_t phyaddr = 0;  
  118.     size_t len = 0;  
  119.     int ret_val = 0;  
  120.   
  121.     if (m_client == NULL || m_handle == NULL) {  
  122.         printk(KERN_ERR "Can't get client or handle in read operation\n");  
  123.         return 0;  
  124.     }  
  125.     // get physical address and length from handle.  
  126.     ret_val = ion_phys(m_client, m_handle, &phyaddr, &len);  
  127.   
  128.     if (ret_val || !phyaddr) {  
  129.         printk(KERN_ERR "Error get physical address from ion\n");  
  130.         return 0;  
  131.     } else {  
  132.         printk(KERN_INFO "Read : physaddr 0x%x, len 0x%x\n", (unsigned long)phyaddr, (unsigned int)len);  
  133.     }  
  134. // ION_HEAP_TYPE_SYSTEM_CONTIG 은 map 할 필요가 없을 듯.   
  135. // virtual address를 바로 얻어와 copy 하도록 함.  
  136. //  kernel_vaddr = ion_map_kernel(m_client, m_handle);  
  137.     kernel_vaddr = phys_to_virt(phyaddr);  
  138.   
  139.     printk(KERN_INFO "phys to virt 0x%x\n", kernel_vaddr);  
  140.     return len;  
  141. }  
  142. // read 도 작성되었지만 사용할 필요는 없습니다. 테스트 용도로 만들어짐.  
  143. static ssize_t vion_read(struct file *f, char __user *buf,  
  144.         size_t len, loff_t *off) {  
  145.     size_t size = 0;  
  146.   
  147.     if ((size = get_vaddr_from_ion_buffer()) < 1) {  
  148.         printk(KERN_ERR "Need to ready buffer for read\n");  
  149.         return 0;  
  150.     }  
  151.   
  152.     if (size < len)  
  153.         len = size;  
  154.   
  155.     if (copy_to_user((char*)kernel_vaddr, buf, len) < 0)  
  156.         return 0;  
  157.   
  158.     return len;  
  159. }  
  160.   
  161. static ssize_t vion_write(struct file *f, const char __user *buf,  
  162.           size_t len, loff_t *off) {  
  163.   
  164.     size_t size = 0;  
  165.   
  166. #ifdef DEBUG  
  167.     printk(KERN_INFO "write data %s\n", buf);  
  168. #endif  
  169.   
  170.     if ((size = get_vaddr_from_ion_buffer()) < 1) {  
  171.         printk(KERN_ERR "Need to ready buffer for read\n");  
  172.         return 0;  
  173.     }  
  174.   
  175.     if (size < len) {  
  176.         printk(KERN_ERR "Not enough buffer, set to MAX 0x%x\n", size);  
  177.         len = size;  
  178.     }  
  179.   
  180.     if (kernel_vaddr != NULL)  
  181.         strncpy((char*)kernel_vaddr, buf, len);  
  182.     else  
  183.         printk(KERN_ERR "virtual address is NULL \n");  
  184.   
  185.     return len;  
  186. }  
  187.   
  188. static struct file_operations vion_fops = {  
  189.     .open = vion_open,  
  190.     .release = vion_release,  
  191.     .read = vion_read,  
  192.     .write = vion_write,  
  193.     .unlocked_ioctl = vion_ioctl  
  194. };  
  195.   
  196. static int __init verify_iondev_init(void)  
  197. {  
  198.     int ret_val;  
  199.     dev_t dev;  
  200.   
  201.     // Get a number of device node dynamically.  
  202.     ret_val = alloc_chrdev_region(&dev, minor, 1, DEVICE_NAME);  
  203.     major = MAJOR(dev);  
  204.   
  205.     if (ret_val < 0) {  
  206.         printk(KERN_ERR "Error allocating charater device region\n");  
  207.         ret_val = -ENODEV;  
  208.         return ret_val;  
  209.     }  
  210.   
  211.     // Register the character device.  
  212.     if ((cl = class_create(THIS_MODULE, "chardrv")) == NULL) {  
  213.         printk(KERN_ERR "class_create failed\n");  
  214.         unregister_chrdev_region(dev, 1);  
  215.         return -1;  
  216.     }  
  217.   
  218.     if (device_create(cl, NULL, dev, NULL, "vion_dev") == NULL) {  
  219.         printk(KERN_ERR "device_create failed\n");  
  220.         class_destroy(cl);  
  221.         unregister_chrdev_region(dev, 1);  
  222.         return -1;  
  223.     }  
  224.   
  225.     cdev_init(&vion_cdev, &vion_fops);  
  226.     if (cdev_add(&vion_cdev, dev, 1) == -1) {  
  227.         printk(KERN_ERR "cdev_failed failed\n");  
  228.         class_destroy(cl);  
  229.         unregister_chrdev_region(dev, 1);  
  230.     }  
  231.   
  232.     // Initialize ion client  
  233.     m_client = NULL;  
  234.   
  235. #ifdef DEBUG  
  236.     printk(KERN_INFO "vion driver : major %d, minor %d\n", MAJOR(dev), MINOR(dev));  
  237. #endif  
  238.   
  239.     return 0;  
  240. }  
  241.   
  242. static void __exit verify_iondev_exit(void)  
  243. {  
  244.     dev_t dev = MKDEV(major, minor);  
  245.       
  246.     device_destory(cl, dev);  
  247.     class_destory(cl);  
  248.     cdev_del(&vion_cdev);  
  249.     unregister_chrdev_region(dev, 1);  
  250.   
  251.     return;  
  252. }  
  253.   
  254. module_init(verify_iondev_init);  
  255. module_exit(verify_iondev_exit);  


참고 할 정도의 code는 되지 않을까? ^^