Userspace I/O(UIO)

UIO(Userspace I/O)는 사용자 공간에서 실행되는 I/O 기술입니다. Linux 시스템에서 일반 장치 드라이버는 커널 공간에서 실행되며 사용자 공간에서 응용 프로그램에 의해 호출될 수 있습니다. 그리고 UIO는 커널 공간에서 장치 드라이버의 작은 부분을 실행하고 사용자 공간에서 드라이버의 대부분의 기능을 구현합니다. 그런 다음 커널 공간 UIO에서 수행할 작업은 두 가지 유형으로 나누어 매우 간단해집니다.

  1. 장치에 필요한 리소스 할당 및 기록 및 UIO 장치 등록
  2. 커널 공간에서 구현해야 하는 인터럽트 처리기 구현

img


Linux 시스템에서 UIO 드라이버의 위치를 이해한 후, UIO에 대한 이해를 심화하기 위해 참조 자료(Linux User Space Device Drivers)를 정리하였습니다.

1. Device Driver Architectures

  • Linux 장치 드라이버는 일반적으로 커널 공간에서 실행되는 커널 드라이버로 설계되었습니다.
  • 사용자 공간 I/O는 Linux 커널 2.6.24 이후로 지원되는 또 다른 대체 장치 드라이버 아키텍처입니다.
  • Linux 커널 커뮤니티의 사람들은 사용자 공간 I /O의 필요성에 항상 동의하지 않을 수 있습니다.
  • 산업용 I/O 카드는 오랫동안 사용자 공간 I/O를 활용해 왔습니다.
  • 일부 유형의 장치에서는 Linux 커널 드라이버를 만드는 것이 과도 할 수 있습니다.
  • FPGA의 Soft IP에는 고유한 요구 사항이 있으며 드라이버로 구현함에 있어 항상 적합한 것은 아닙니다.

2. Legacy User Space Driver Methods (/dev/mem)

  • 장치 메모리를 사용자 공간에 매핑하는 /dev/mem이라는 문자 드라이버가 커널에 존재합니다.
  • 이 드라이버를 사용하면 사용자 공간 응용 프로그램이 장치 메모리에 액세스할 수 있습니다.
  • 이는 큰 보안 허점이기 때문에 커널 구성에서 메모리 액세스를 비 활성화할 수 있습니다(CONFIG_STRICT_DEVMEM).

  • 루트 사용자여야 합니다.
  • 새 하드웨어의 프로토타이핑 또는 테스트를 위한 훌륭한 도구이지만, 사용자 공간 장치 드라이버에 적합한 프로덕션 솔루션으로 간주되지 않습니다.
  • 모든 주소를 사용자 공간에 매핑할 수 있기 때문에 버그가 있는 사용자 공간 드라이버가 커널을 중단시킬 수 있습니다.

3. Introduction to UIO

  • Linux 커널은 UIO라는 사용자 공간 드라이버를 수행하기 위한 프레임워크를 제공합니다.

  • 프레임워크는 사용자 공간 드라이버 아래 계층으로 실행되는 문자 모드 커널 드라이버(drivers/uio)입니다.
  • UIO는 드라이버 개발 작업의 일부를 오프로드 하는데 도움이 됩니다.
  • UIO의 “U”는 Universal이 아닙니다.
    • 커널 프레임워크에서 잘 처리되는 장치는 이상적으로는 커널에 있어야 합니다(많은 커널 개발자에게 묻는 경우).
    • 네트워킹은 반도체 공급업체가 향상된 성능을 얻기 위해 사용자 공간 I/O를 수행하는 영역 중 하나입니다.
  • UIO는 간단한 장치 드라이버를 정말 잘 처리합니다.
    • 간단한 드라이버: 커널 프레임워크에 액세스할 필요 없는 장치 액세스 및 인터럽트 처리

4. Kernel Space Driver Characteristics

4.1 Advantages

  • 인터럽트 및 하드웨어 리소스에 대한 액세스를 허용하기 위해 최고 권한 모드로 커널 공간에서 실행됩니다.
  • 커널 공간 드라이버가 복잡한 장치용으로 설계될 수 있는 커널 서비스가 많이 있습니다.
  • 커널은 여러 응용 프로그램이 커널 공간 드라이버에 동시에 액세스할 수 있도록 사용자 공간에 API를 제공합니다.
    • 더 크고 확장 가능한 소프트웨어 시스템을 설계할 수 있습니다.
  • 많은 드라이버가 커널 공간인 경향이 있습니다.
    • 오픈 소스 커뮤니티에서 질문하기가 더 유리합니다.
    • 드라이버를 오픈 소스 커뮤니티로 쉽게 푸시할 수 있습니다.

4.2 Disadvantages

  • 드라이버 액세스를 위한 시스템 호출 오버헤드
    • 사용자 공간에서 커널 공간으로(또는 그 반대로) 전환이 필요합니다.
    • 실시간 애플리케이션에 영향을 미치는 오버헤드가 비 결정적입니다.
  • 학습의 어려움
    • 커널 API는 응용 프로그램 수준 API와 달라 생산성이 높아지는데 시간이 걸립니다.
  • 버그는 커널 중단을 일으켜 치명적입니다.
  • 디버깅의 어려움
    • 커널 코드는 고도로 최적화되어 있으며 다양한 디버그 도구가 있습니다.
  • 빈번한 커널 API 변경
    • 어떤 커널 버전용으로 빌드된 커널 드라이버가 다른 버전용으로 빌드되지 않을 수 있습니다.

5. User Space Device Driver Characteristics

5.1 Advantages

  • 디버그 도구를 더 쉽게 사용할 수 있고 일반 응용 프로그램 개발과 같이 디버깅이 덜 어렵습니다.
  • 부동 소수점과 같은 사용자 공간 서비스를 사용할 수 있습니다.
  • 시스템 호출이 필요하지 않으므로 장치 액세스가 매우 효율적입니다.
  • Linux의 애플리케이션 API는 매우 안정적입니다.
  • 드라이버는 “C” 뿐만 아니라 모든 언어로 작성할 수 있습니다.

5.2 Disadvantages

  • 커널 프레임워크 및 서비스에 대한 액세스 권한이 없습니다.
    • 연속 메모리 할당, 직접 캐시 제어 및 DMA를 사용할 수 없습니다.
    • 보완을 위해 커널 코드를 복제하거나 커널 드라이버를 사용해야 할 수 있음
  • 인터럽트 처리는 사용자 공간에서 수행할 수 없습니다.
    • 약간의 지연을 야기하는 사용자 공간으로 커널 드라이버에 의해 처리되어야 합니다.
  • 응용 프로그램이 장치 드라이버에 액세스할 수 있도록 미리 정의된 API가 없습니다.
    • 여러 애플리케이션이 드라이버에 액세스하는 경우 동시성도 고려해야 합니다.

6. UIO Framework Features

  • drivers/uio에는 Linux에서 제공하는 두 가지 별개의 UIO 장치 드라이버가 있습니다.
  • UIO 드라이버(drivers/uio.c)
    • 고급 사용자의 경우 UIO 프레임워크를 설정하려면 최소 커널 공간 드라이버가 필요합니다.
    • 커널 공간 드라이버가 맞춤화될 수 있기 때문에 가장 보편적이며 모든 상황을 처리할 가능성이 높습니다.
    • 대부분의 작업은 사용자 공간 드라이버에서 수행할 수 있습니다.
  • UIO 플랫폼 장치 드라이버(drivers/uio_pdev_irqgen.c)
    • 이 드라이버는 커널 공간 드라이버가 필요하지 않도록 UIO 드라이버를 보강합니다.
      • uio에 필요한 커널 공간 드라이버를 제공합니다.
    • 장치 트리와 함께 작동하여 사용하기 쉽습니다.
      • 장치의 장치 트리 노드는 호환되는 “generic uio”를 사용해야 합니다.
    • 커널 공간 코드가 필요하지 않기 때문에 최상의 출발점

7. UIO Driver Kernel Configuration

  • UIO 드라이버는 Linux 커널에서 구성해야 합니다.
CONFIG_UIO=y
CONFIG_UIO_PDRV_GENIRQ=y

8. UIO Platform Device Driver Details

  • 사용자는 사용자 공간 드라이버만 제공합니다.
  • UIO 플랫폼 장치 드라이버는 장치 트리에서 구성하고 UIO 장치를 등록합니다.
  • 사용자 공간 드라이버는 하드웨어에 직접 액세스할 수 있습니다.
  • 사용자 공간 드라이버는 UIO 장치 파일 디스크립터를 읽어 인터럽트를 알립니다.

img

9. Kernel UIO API - Sys Filesystem

  • 커널의 UIO 드라이버는 UIO 장치를 설명하는 sys 파일 시스템에 파일 속성을 생성합니다.
  • /sys/class/uio는 모든 파일 속성의 루트 디렉토리입니다.
  • 각 UIO 장치에 대해 /sys/class/uio 아래에 별도의 번호가 지정된 디렉토리 구조가 생성됩니다.
    • 첫 번째 UIO 장치: /sys/class/uio/uio0
    • /sys/class/uio/uio0/name에는 uio_info 구조의 이름과 관련된 장치의 이름이 포함됩니다.
    • /sys/class/uio/uio0/maps는 장치에 대한 모든 메모리 범위가 있는 디렉토리입니다.
    • 번호가 매겨진 각 맵 디렉토리에는 주소, 이름, 오프셋 및 크기를 포함하여 장치 메모리를 설명하는 속성이 있습니다.
      • /sys/class/uio/uio0/maps/map0

10. User Space Driver Flow

  1. 커널 공간 UIO 장치 드라이버는 사용자 공간 드라이버가 시작되기 전에 로드되어야 합니다(모듈을 사용하는 경우).

  2. 사용자 공간 응용 프로그램이 시작되고 UIO 장치 파일이 열립니다(/dev/uioX 여기서 X는 0, 1, 2 …)
    • 사용자 공간에서 UIO 장치는 다른 장치와 마찬가지로 파일 시스템의 장치 노드입니다.
  3. 장치 메모리 주소 정보는 관련 sysfs 디렉토리에서 찾을 수 있으며 크기만 필요합니다.

  4. 장치 메모리는 UIO 드라이버의 mmap() 함수를 호출하여 프로세스 주소 공간에 매핑됩니다.

  5. 애플리케이션이 장치 하드웨어에 액세스하여 장치를 제어합니다.

  6. munmap()을 호출하여 장치 메모리가 매핑 해제됩니다.

  7. UIO 장치 파일이 닫힙니다.

11. User Space Driver Example

 1 #define UIO_SIZE "/sys/class/uio/uio0/maps/map0/size"
 2 
 3 int main(int argc, char **argv)
 4 {
 5         int             uio_fd;
 6         unsigned int    uio_size;
 7         FILE            *size_fp;
 8         void            *base_address;
 9 
10         /*
11          * 1. Open the UIO device so that it is ready to use
12          */
13         uio_fd = open("/dev/uio0", O_RDWR);
14 
15         /*
16          * 2. Get the size of the memory region from the size sysfs file
17          *    attribute
18          */
19         size_fp = fopen(UIO_SIZE, O_RDONLY);
20         fscanf(size_fp, "0x%08X", &uio_size);
21 
22         /*
23          * 3. Map the device registers into the process address space so they
24          *    are directly accessible
25          */
26         base_address = mmap(NULL, uio_size,
27                            PROT_READ|PROT_WRITE,
28                            MAP_SHARED, uio_fd, 0);
29 
30         // Access to the hardware can now occur ...
31 
32         /*
33          * 4. Unmap the device registers to finish
34          */
35         munmap(base_address, uio_size);
36 
37         ...
38 }

12. Mapping Device Memory Details

  • Linux의 문자 장치 드라이버 프레임워크는 장치 메모리를 사용자 공간 프로세스 주소 공간에 매핑하는 기능을 제공합니다.
  • 문자 드라이버는 사용자 공간 응용 프로그램이 호출할 수 있는 mmap() 함수를 구현할 수 있습니다.
  • mmap() 함수는 호출 프로세스의 가상 주소 공간에 새 매핑을 만듭니다.
    • 지정된 물리적 주소에 해당하는 가상 주소가 반환됩니다.
    • 파일 내용이 메모리 읽기 및 쓰기에 의해 액세스되도록 파일을 메모리 공간에 매핑하는 데 사용할 수도 있습니다.
  • 사용자 공간 프로그램이 가상 주소 범위에서 읽거나 쓸 때마다 장치에 액세스하고 있습니다.
  • 시스템 호출이 필요하지 않으므로 향상된 성능을 제공합니다.

13. Mapping Device Memory Flow

img

14. User Space Application Interrupt Processing

  • 인터럽트는 사용자 공간에서 직접 처리되지 않습니다.
  • 인터럽트는 UIO 커널 드라이버에 의해 처리될 수 있으며, UIO 장치 파일 디스크립터를 통해 사용자 공간으로 인터럽트를 릴레이 합니다.
  • 인터럽트가 발생할 때 알림을 받으려는 사용자 공간 드라이버는 UIO 장치 파일 설명자에서 select() 또는 read()를 호출합니다.
    • 읽기는 blocking 또는 non-blocking 모드로 수행할 수 있습니다.
  • read()는 이벤트(인터럽트) 수를 반환합니다.
  • 스레드를 사용하여 인터럽트를 처리할 수 있습니다.
  • 또는 사용자 제공 커널 드라이버가 인터럽트를 처리한 다음 공유 메모리와 같은 다른 메커니즘을 통해 사용자 공간 드라이버에 데이터를 전달할 수 있습니다.
    • 인터럽트가 매우 빠른 장치에 필요할 수 있습니다.

15. User Space Application Interrupt Processing Example

 1 int pending = 0;
 2 int reenable = 1;
 3 
 4 /*
 5  * 1. The UIO device is opened as previously described
 6  */
 7 int uio_fd = open("/dev/uio0", O_RDWR);
 8 
 9 /*
10  * 2. Read the UIO device file descriptor to wait for an interrupt,
11  *    the read blocks by default, a non blocking read can also be used
12  *
13  *    NOTE: The pending variable contains the number of interrupts that have
14  *          occurred if multiple
15  */
16 read(uio_fd, (void *)&pending, sizeof(int));
17 
18 
19 //
20 // add device specific processing like acking the interrupt in the device here
21 //
22 
23 
24 /*
25  * 3. Re-enable the interrupt at the interrupt controller level
26  */
27 write(uio_fd, (void *)&reenable, sizeof(int));

16. UIO Driver Details

img

  • 사용자는 커널 드라이버와 사용자 공간 드라이버를 제공합니다.
  • 커널 공간 드라이버는 장치 트리에서 구성하고 UIO 장치를 등록하는 플랫폼 드라이버입니다.
  • 커널 공간 드라이버는 커널 공간에서 인터럽트 핸들러를 제공할 수도 있습니다.
  • 사용자 공간 드라이버는 하드웨어에 직접 액세스할 수 있습니다.

17. Kernel UIO API - Basics

  • API는 작고 사용하기 쉽습니다 .
struct uio_info
-- name      : device name
-- version   : device driver version
-- irq       : interrupt number
-- irq_flags : flags for request_irq()
-- handler   : driver irq handler (optional)
-- mem[]     : memory regions that can be mapped to user space
   o    addr : memory address
   o memtype : type of memory region (physical, logical, virtual)

18. Kernel UIO API - Registration

  • uio_register_device() 함수는 드라이버를 UIO 프레임워크에 연결합니다.

    • 입력으로 struct uio_info가 필요합니다.
  • 일반적으로 플랫폼 장치 드라이버의 probe() 함수에서 호출됩니다.
    • 장치 파일 /dev/uio#(#부터 시작) 및 모든 관련 sysfs 파일 속성 생성
  • uio_unregister_device() 함수는 UIO 프레임워크에서 드라이버 연결을 끊습니다.

    • 일반적으로 플랫폼 장치 드라이버의 정리 기능에서 호출됩니다.
    • 장치 파일 삭제 /dev/uio#

19. Kernel Space Driver Example

 1 probe()
 2 {
 3     /*
 4      * 1. Platform device driver initialization in the driver probe() function
 5      */
 6     dev = devm_kzalloc(&pdev->dev, (sizeof(struct uio_timer_dev)), GFP_KERNEL);
 7     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 8     dev->regs = devm_ioremap_resource(&pdev->dev, res);
 9     irq = platform_get_irq(pdev, 0);
10 
11     /*
12      * 2. Add basic UIO structure initialization
13      */
14     dev->uio_info.name = "uio_timer";
15     dev->uio_info.version = 1;
16     dev->uio_info.priv = dev;
17 
18     /*
19      * 3. Add the memory region initialization for the UIO
20      */
21     dev->uio_info.mem[0].name = "registers";
22     dev->uio_info.mem[0].addr = res->start;
23     dev->uio_info.mem[0].size = resource_size(res);
24     dev->uio_info.mem[0].memtype = UIO_MEM_PHYS;
25 
26     /*
27      * 4. Add the interrupt initialization for the UIO
28      */
29     dev->uio_info.irq = irq;
30     dev->uio_info.handler = uio_irq_handler;
31 
32     /*
33      * 5. Register the UIO device with the kernel framework
34      */
35     uio_register_device(&pdev->dev, &dev->info);
36 }

20. UIO Framework Details

  • UIO 드라이버
    • 장치의 장치 트리 노드는 호환 속성에서 원하는 모든 것을 사용할 수 있습니다. 플랫폼 장치 드라이버와 마찬가지로 커널 공간 드라이버에서 사용되는 것과 일치하기만 하면 되기 때문입니다.
  • UIO 플랫폼 장치 드라이버
    • 장치의 장치 트리 노드는 호환 속성에서 “generic - uio”를 사용해야 합니다.

참조 자료