Linux DRM - open()과 ioctl() 코드 흐름

The code path of open() and ioctl() in Linux DRM v6.19: drm_stub_open, drm_file_alloc, drm_ioctl dispatch and permission checks, plus a libdrm probe example.
October 28, 2026

Linux DRM - open()과 ioctl() 코드 흐름

Reqruies
  • Kernel: Linux v6.19 소스 기준
  • Library: libdrm 2.4.129
  • 선행 글: Linux DRM 2편 코어 초기화와 드라이버 등록

Introduction

편주제
1편구조와 전체 그림
2편코어 초기화와 드라이버 등록
2편에서 /dev/dri/card0 같은 노드가 만들어지는 과정을 봤습니다. 이 글은 유저가 그 노드를 쓰는 순간, 즉 open()과 ioctl()이 커널 안에서 어떤 함수들을 거치는지 따라갑니다. 마지막에는 같은 일을 유저 공간에서 libdrm으로 해 보는 예제를 봅니다.

Code Flow 3: open()

유저가 open("/dev/dri/card0")를 호출하면 커널은 주 번호 226으로 앞서 등록한 stub fops를 찾습니다. stub은 진짜 일을 하지 않고 파일 연산 표를 드라이버의 것으로 갈아 끼웁니다.
drivers/gpu/drm/drm_drv.c - drm_stub_open() (L1188-1216)
c
open("/dev/dri/card0") 처리 흐름
Mermaid
sequenceDiagram participant U as 유저 프로세스 participant V as VFS (chrdev 226) participant S as drm_stub_open participant O as drm_open / drm_open_helper U->>V: open("/dev/dri/card0") V->>S: drm_stub_fops.open S->>S: drm_minor_acquire(iminor(inode))<br/>→ struct drm_minor S->>S: replace_fops(filp, driver->fops) S->>O: filp->f_op->open() (= drm_open) O->>O: drm_file_alloc() → struct drm_file O->>O: primary라면 drm_master_open() O-->>U: fd
드라이버의 fops가 가진 .open은 drm_open()입니다. 이것은 매크로 한 줄에서 옵니다.
include/drm/drm_gem.h - #define DRM_GEM_FOPS (L464-473)
c
소스 include/drm/drm_gem.h L464-L473 (Linux v6.19)
이 매크로 안에 앞으로 시리즈에서 보게 될 모든 진입점이 있습니다. .unlocked_ioctl = drm_ioctl(ioctl), .read = drm_read(이벤트 읽기), .mmap = drm_gem_mmap(버퍼 매핑), .poll = drm_poll입니다. drm_open()과 drm_open_helper()는 다음과 같습니다.
drivers/gpu/drm/drm_file.c - drm_open() (L369-403)
c
drm_open()은 minor를 찾고 drm_open_helper()에 넘깁니다.
drivers/gpu/drm/drm_file.c - drm_open_helper() (L316-355)
c
drm_file_alloc()이 open() 하나에 해당하는 상태를 만듭니다. 앞서 본 object_idr, 이벤트 큐 등이 여기서 초기화됩니다.
drivers/gpu/drm/drm_file.c - drm_file_alloc() (L132-197)
c
drm_gem_open()이 handle 표를 초기화하는데, 이 호출은 idr_init_base(&file_private->object_idr, 1)입니다. handle 번호가 1부터 시작하는 이유가 여기 있습니다(0은 "없음"으로 쓰기 위해서입니다). filp->private_data = priv 덕분에 이후의 모든 ioctl, read, mmap은 file->private_data에서 drm_file을 바로 얻습니다.

Code Flow 4: ioctl()

ioctl(fd, DRM_IOCTL_xxx, arg)은 fops->unlocked_ioctl인 drm_ioctl()로 들어옵니다.
drm_ioctl() 의 디스패치
Mermaid
flowchart TD A["ioctl(fd, DRM_IOCTL_xxx, arg)"] --> B["drm_ioctl()"] B --> C{"nr ≥ DRM_COMMAND_BASE<br/>(0x40) ?"} C -->|"예 : 드라이버 ioctl"| D["dev->driver->ioctls[nr - 0x40]"] C -->|"아니오 : 코어 ioctl"| E["drm_ioctls[nr]"] D --> F["copy_from_user → kdata"] E --> F F --> G["drm_ioctl_kernel()"] G --> H{"drm_ioctl_permit()<br/>AUTH · MASTER · ROOT_ONLY · RENDER_ALLOW"} H -->|"거부"| I["-EACCES"] H -->|"통과"| J["func(dev, kdata, file_priv)"] J --> K["copy_to_user → 결과"]
drivers/gpu/drm/drm_ioctl.c - drm_ioctl() (L821-919)
c
핵심만 정리하면 이렇습니다.
  1. ioctl 번호의 type이 'd'(DRM_IOCTL_BASE)인지 확인합니다.
  2. 번호가 0x40 이상이면 드라이버 표(dev->driver->ioctls), 아니면 코어 표(drm_ioctls[])에서 항목을 찾습니다.
  3. 유저가 준 크기를 믿지 않고 커널에 있는 정의(ioctl->cmd)로 버퍼 크기를 계산합니다(Do not trust userspace).
  4. copy_from_user() 후 drm_ioctl_kernel()이 권한을 검사하고 실제 함수를 부릅니다.
drivers/gpu/drm/drm_ioctl.c - drm_ioctl_kernel() (L787-805)
c
코어 ioctl 표는 매크로로 만든 배열입니다. 이 시리즈에서 만날 항목만 골라 보면 다음과 같습니다. 오른쪽 끝의 플래그가 앞서 본 권한 규칙입니다.
drivers/gpu/drm/drm_ioctl.c - drm_ioctls (L632-735)
c

libdrm: The View from User Space

이제 유저 공간에서 위 흐름을 직접 따라가 보겠습니다. 아래 프로그램은 DRM 장치를 열어서 드라이버 정보와 KMS 객체를 나열합니다.
drm_probe.c - 드라이버 정보와 KMS 객체 나열
c
각 libdrm 함수는 커널 ioctl 하나로 이어집니다. 위 코드에서 drm_ioctls[] 표의 이름을 그대로 찾아볼 수 있습니다.
libdrm 함수ioctl커널 핸들러플래그
drmGetVersion()DRM_IOCTL_VERSIONdrm_version()RENDER_ALLOW
drmGetCap()DRM_IOCTL_GET_CAPdrm_getcap()RENDER_ALLOW
drmSetClientCap()DRM_IOCTL_SET_CLIENT_CAPdrm_setclientcap()없음
drmModeGetResources()DRM_IOCTL_MODE_GETRESOURCESdrm_mode_getresources()없음
drmModeGetConnector()DRM_IOCTL_MODE_GETCONNECTORdrm_mode_getconnector()없음
drmModeGetPlaneResources()DRM_IOCTL_MODE_GETPLANERESOURCESdrm_mode_getplane_res()없음
drmModeObjectGetProperties()DRM_IOCTL_MODE_OBJ_GETPROPERTIESdrm_mode_obj_get_properties_ioctl()없음
이 프로그램은 libdrm 2.4.129로 컴파일되는 것을 확인했습니다. 실행하려면 /dev/dri/card0에 접근 권한이 필요합니다(데스크톱 로그인 세션이면 보통 가능하고, 아니면 video/render 그룹이 필요합니다). strace -e trace=openat,ioctl ./drm_probe로 실행하면 유저가 호출하는 ioctl 순서를 볼 수 있습니다(strace 버전에 따라 DRM_IOCTL_VERSION 같은 이름까지 풀어서 보여 줍니다).

Code Locations

단계함수위치 (Linux v6.19)
open: 파일 상태 만들기drm_open_helper()drivers/gpu/drm/drm_file.c
open: drm_file 할당drm_file_alloc()drivers/gpu/drm/drm_file.c
open: master 지정drm_master_open()drivers/gpu/drm/drm_auth.c
ioctl: 진입과 디스패치drm_ioctl()drivers/gpu/drm/drm_ioctl.c
ioctl: 권한 검사drm_ioctl_permit()drivers/gpu/drm/drm_ioctl.c
ioctl: 코어 ioctl 표drm_ioctlsdrivers/gpu/drm/drm_ioctl.c

Summary

이 Post 에서 확인한 것은 다음과 같습니다.
  • open: stub이 드라이버의 fops로 갈아 끼우고, drm_file_alloc()이 open() 하나의 상태(handle 표, 이벤트 큐)를 만듭니다.
  • ioctl: drm_ioctl()이 코어 표와 드라이버 표에서 항목을 찾고, drm_ioctl_permit()이 권한을 검사한 뒤 핸들러를 부릅니다.
  • libdrm 함수는 대부분 이 ioctl의 얇은 포장입니다.
다음부터는 렌더링 쪽입니다. 먼저 drm_file의 object_idr에 들어 있는 GEM handle이 어떻게 만들어지고, 그 버퍼를 CPU에서 어떻게 읽고 쓰는지 봅니다.
Jooojub
System S/W engineer
Explore Tags
Series
    Recent Post
    © 2026. jooojub. All right reserved.