GPU 서브시스템 (DRM/KMS)

Linux 커널 GPU 서브시스템: DRM/KMS 아키텍처, GEM/TTM 메모리 관리, DMA-BUF 버퍼 공유, GPU 스케줄러, Atomic Modesetting, DRM 드라이버 구조 종합 가이드.

관련 표준: DisplayPort 2.1 (디스플레이 인터페이스), HDMI 2.1 (멀티미디어 인터페이스), E-EDID (디스플레이 정보 교환), PCIe 6.0 (GPU 인터커넥트) — DRM/KMS 서브시스템이 구현하는 디스플레이 및 GPU 규격입니다. 종합 목록은 참고자료 — 표준 & 규격 섹션을 참고하세요.

DRM (Direct Rendering Manager) 개요

DRM은 Linux 커널의 GPU 접근을 관리하는 서브시스템입니다. 원래 3D 그래픽 가속을 위해 도입되었으나, 현재는 디스플레이 출력(KMS), GPU 메모리 관리(GEM/TTM), GPU 작업 스케줄링까지 포괄하는 핵심 프레임워크로 발전했습니다.

구성 요소역할커널 코드
DRM Core 드라이버 등록, ioctl 디스패치, 파일 오퍼레이션 drivers/gpu/drm/drm_*.c
KMS (Kernel Mode Setting) 디스플레이 모드 설정, CRTC/Encoder/Connector/Plane drivers/gpu/drm/drm_atomic*.c
GEM (Graphics Execution Manager) GPU 메모리 버퍼 할당/관리, mmap drivers/gpu/drm/drm_gem*.c
TTM (Translation Table Manager) VRAM/시스템 메모리 간 버퍼 이동, 페이징 drivers/gpu/drm/ttm/
DMA-BUF 디바이스 간 버퍼 공유 (GPU↔카메라↔디스플레이) drivers/dma-buf/
GPU Scheduler GPU 작업 큐 관리, 우선순위, 타임아웃 처리 drivers/gpu/drm/scheduler/
DRM vs fbdev: fbdev(drivers/video/fbdev/)는 단순 프레임버퍼만 제공하는 레거시 인터페이스입니다. DRM/KMS는 하드웨어 가속, 다중 디스플레이, vsync, overlay plane 등을 지원하며, fbdev는 DRM으로 대체되는 추세입니다. 새 드라이버에서는 반드시 DRM/KMS를 사용해야 합니다.

DRM 아키텍처

User Space Mesa / Vulkan libdrm Wayland / X11 GPGPU (OpenCL) V4L2 / GBM ioctl / mmap DRM Core /dev/dri/card0 (Primary) & /dev/dri/renderD128 (Render) KMS (Mode Setting) CRTC · Encoder · Connector · Plane GEM / TTM 메모리 관리 GPU Scheduler Job Queue · Fence · Timeout DMA-BUF DRM Driver (i915 / amdgpu / nouveau / panfrost / virtio-gpu ...) 드라이버별 HW 초기화, 커맨드 서브미션, IRQ, 전원 관리 MMIO / DMA GPU Hardware

DRM 핵심 구조체

drm_device & drm_driver

drm_device는 하나의 GPU 디바이스를 나타내고, drm_driver는 해당 GPU 유형의 오퍼레이션을 정의합니다.

#include <drm/drm_drv.h>
#include <drm/drm_gem.h>
#include <drm/drm_ioctl.h>

/* DRM 드라이버 정의 — 드라이버 전체에 하나 */
static const struct drm_driver my_gpu_driver = {
    .driver_features    = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC |
                          DRIVER_RENDER,
    /* GEM 오퍼레이션 */
    .gem_prime_import   = drm_gem_prime_import,
    .gem_prime_import_sg_table = drm_gem_shmem_prime_import_sg_table,

    /* DRM ioctl 테이블 (드라이버 고유 ioctl) */
    .ioctls             = my_gpu_ioctls,
    .num_ioctls         = ARRAY_SIZE(my_gpu_ioctls),

    /* /sys, /dev 노드 이름 */
    .name               = "my-gpu",
    .desc               = "My GPU DRM driver",
    .date               = "20260101",
    .major              = 1,
    .minor              = 0,

    .fops               = &my_gpu_fops,
};

drm_file (파일 프라이빗)

유저 프로세스가 /dev/dri/card0를 open하면 drm_file 구조체가 생성됩니다. 프로세스별 GPU 컨텍스트, 인증 상태, GEM 핸들 테이블을 관리합니다.

/* drm_file 주요 필드 (include/drm/drm_file.h) */
struct drm_file {
    struct drm_device  *minor;        /* 소속 DRM 디바이스 */
    bool               authenticated; /* DRM Master 인증 여부 */
    struct idr         object_idr;    /* GEM 핸들 → drm_gem_object 매핑 */
    struct list_head   event_list;    /* 페이지 플립 등 이벤트 큐 */
    void              *driver_priv;   /* 드라이버별 프라이빗 데이터 */
};
Primary vs Render Node:
  • /dev/dri/card0 (Primary) — KMS 모드 설정 + 렌더링. DRM Master 권한 필요 (보통 디스플레이 서버가 소유)
  • /dev/dri/renderD128 (Render) — 렌더링/GPGPU 전용. DRM Master 불필요, 일반 사용자가 접근 가능
DRIVER_RENDER 플래그를 설정한 드라이버만 render node를 생성합니다.

KMS (Kernel Mode Setting)

KMS는 커널에서 디스플레이 모드를 설정하는 프레임워크입니다. 기존의 유저 공간 모드 설정(UMS)과 달리, 커널이 직접 해상도/주사율/디스플레이 파이프라인을 제어하여 콘솔↔그래픽 전환 시 깜박임이 없고 다중 모니터를 안정적으로 관리합니다.

KMS 핵심 오브젝트

Framebuffer drm_framebuffer GEM 객체 참조 Plane drm_plane Primary / Overlay / Cursor CRTC drm_crtc 스캔아웃 + 타이밍 생성 감마/CTM Encoder drm_encoder 신호 변환 Connector drm_connector HDMI/DP/eDP EDID 읽기 Framebuffer GEM 객체를 감싸는 래퍼. 픽셀 포맷, stride, 오프셋 정보 보유. 여러 Plane에서 공유 가능. Plane (Primary / Overlay / Cursor) 프레임버퍼를 화면 위 특정 위치/크기로 배치. Primary는 필수, Overlay/Cursor는 하드웨어 지원 시 사용. CRTC (CRT Controller) Plane들을 합성하여 최종 프레임 생성. 수직/수평 타이밍(해상도/주사율) 관리. 감마 보정, 색 변환 매트릭스(CTM) 적용. Encoder → Connector Encoder: 디지털 신호를 HDMI/DP/LVDS 등으로 변환. Connector: 물리 포트 + EDID 모니터 정보 + 핫플러그 이벤트.
KMS 오브젝트구조체주요 콜백역할
CRTC drm_crtc atomic_check, atomic_flush, atomic_enable 스캔아웃 엔진. 해상도/주사율 타이밍 생성, Plane 합성
Encoder drm_encoder atomic_check, atomic_enable, atomic_disable 내부 디지털 신호를 외부 프로토콜(HDMI/DP)로 변환
Connector drm_connector detect, get_modes, atomic_check 물리적 출력 포트. EDID 파싱, 핫플러그 감지
Plane drm_plane atomic_check, atomic_update, atomic_disable 프레임버퍼를 화면에 배치. 스케일링, 회전, 블렌딩
Framebuffer drm_framebuffer create_handle, destroy GEM 객체 래퍼. 픽셀 포맷, stride, modifier 정보

Atomic Modesetting

Atomic modesetting은 KMS의 현대적 커밋 모델입니다. 여러 디스플레이 속성 변경을 하나의 원자적 트랜잭션으로 묶어 중간 상태 없이 적용하거나 전체를 롤백합니다.

/* Atomic 커밋 흐름 (커널 내부) */

/* 1. 유저가 DRM_IOCTL_MODE_ATOMIC 호출 */
/*    → drm_mode_atomic_ioctl() 진입 */

/* 2. 상태 복제 */
struct drm_atomic_state *state = drm_atomic_state_alloc(dev);
/* 각 오브젝트의 현재 상태를 복제하여 new_state 생성 */

/* 3. 속성 적용 (유저가 요청한 변경사항) */
drm_atomic_set_crtc_for_connector(new_conn_state, crtc);
new_crtc_state->mode_blob = mode;
new_plane_state->fb = framebuffer;
new_plane_state->crtc = crtc;

/* 4. 검증 (atomic_check) — 하드웨어 제약 확인 */
ret = drm_atomic_check_only(state);
/* 각 CRTC/Plane/Connector의 atomic_check 콜백 호출 */
/* 실패 시 -EINVAL 반환, 유저에게 거부 통보 */

/* 5. TEST_ONLY 플래그면 여기서 종료 (검증만 수행) */
if (flags & DRM_MODE_ATOMIC_TEST_ONLY)
    return ret;

/* 6. 커밋 (atomic_commit) — 실제 하드웨어 적용 */
ret = drm_atomic_commit(state);
/* NONBLOCK 플래그: 비동기 커밋 (vsync 대기 안 함) */
/* PAGE_FLIP_EVENT: 완료 시 유저에게 이벤트 전달 */
Atomic 커밋 플래그:
  • DRM_MODE_ATOMIC_TEST_ONLY — 검증만 수행, 실제 적용 안 함. 유저 공간에서 구성 유효성을 미리 확인
  • DRM_MODE_ATOMIC_NONBLOCK — 비동기 커밋. vsync을 기다리지 않고 즉시 반환
  • DRM_MODE_PAGE_FLIP_EVENT — 플립 완료 시 DRM_EVENT_FLIP_COMPLETE 이벤트를 유저에게 전달
  • DRM_MODE_ATOMIC_ALLOW_MODESET — 해상도/주사율 변경이 포함된 full modeset 허용

DRM Properties 시스템

KMS 오브젝트(CRTC, Plane, Connector)의 모든 설정 가능한 속성은 DRM Property로 표현됩니다. Atomic modesetting에서는 property ID와 값을 쌍으로 전달하여 디스플레이 상태를 변경합니다.

Property 타입

타입커널 생성 함수값 형태예시
Range drm_property_create_range() min~max 정수 alpha (0~0xFFFF), rotation angle
Enum drm_property_create_enum() 이름↔값 열거 DPMS (On/Standby/Suspend/Off)
Bitmask drm_property_create_bitmask() 비트 조합 rotation (ROTATE_0 | REFLECT_X)
Blob drm_property_create_blob() 임의 바이너리 데이터 MODE_ID (drm_mode_modeinfo), EDID
Object drm_property_create_object() 다른 KMS 오브젝트 ID CRTC_ID, FB_ID, IN_FENCE_FD
Signed Range drm_property_create_signed_range() 부호 있는 정수 SRC_X, SRC_Y (16.16 고정소수점)

표준 KMS Property

/* === CRTC Properties === */
"ACTIVE"           /* bool: CRTC 활성화 (atomic에서 DPMS 대체) */
"MODE_ID"          /* blob: 디스플레이 모드 (해상도/주사율) */
"OUT_FENCE_PTR"    /* ptr: 커밋 완료 fence fd 반환 위치 */
"VRR_ENABLED"      /* bool: 가변 주사율 활성화 */
"DEGAMMA_LUT"      /* blob: 디감마 LUT (Color Management) */
"CTM"              /* blob: 색 변환 매트릭스 (3x3, S31.32 고정소수) */
"GAMMA_LUT"        /* blob: 감마 LUT */

/* === Plane Properties === */
"FB_ID"            /* object: 표시할 프레임버퍼 */
"CRTC_ID"         /* object: 연결할 CRTC */
"SRC_X/Y/W/H"     /* range: 소스 영역 (16.16 고정소수점) */
"CRTC_X/Y/W/H"    /* range: 화면 위 위치/크기 (스케일링) */
"rotation"         /* bitmask: ROTATE_0/90/180/270 | REFLECT_X/Y */
"alpha"            /* range: 투명도 (0=투명, 0xFFFF=불투명) */
"pixel blend mode" /* enum: None/Pre-multiplied/Coverage */
"zpos"             /* range: 레이어 순서 (높을수록 위) */
"IN_FENCE_FD"      /* range: explicit sync 입력 fence fd */
"COLOR_ENCODING"   /* enum: YCbCr BT.601/709/2020 */
"COLOR_RANGE"      /* enum: YCbCr Limited/Full Range */

/* === Connector Properties === */
"CRTC_ID"          /* object: 연결할 CRTC */
"DPMS"             /* enum: On/Standby/Suspend/Off (레거시) */
"link-status"      /* enum: Good/Bad — 링크 학습 실패 시 Bad */
"EDID"             /* blob: 모니터 EDID 바이너리 (읽기 전용) */
"content type"     /* enum: No Data/Graphics/Photo/Cinema/Game */
"max bpc"          /* range: 최대 색 깊이 (bits per component) */
"HDR_OUTPUT_METADATA" /* blob: HDR 정적/동적 메타데이터 */
"vrr_capable"      /* range: 모니터 VRR 지원 여부 (읽기 전용) */
/* 드라이버에서 커스텀 property 등록 */
struct drm_property *prop;

/* Enum property 생성 */
static const struct drm_prop_enum_list my_scaling_modes[] = {
    { 0, "None" },
    { 1, "Full" },
    { 2, "Center" },
    { 3, "Full aspect" },
};
prop = drm_property_create_enum(dev, 0, "scaling mode",
        my_scaling_modes, ARRAY_SIZE(my_scaling_modes));

/* CRTC에 property 연결 */
drm_object_attach_property(&crtc->base, prop, 0);

/* atomic_check에서 property 값 읽기 */
static int my_crtc_atomic_check(struct drm_crtc *crtc,
                                struct drm_atomic_state *state)
{
    struct drm_crtc_state *crtc_state =
        drm_atomic_get_new_crtc_state(state, crtc);
    /* crtc_state에서 property 값에 접근 */
    ...
}
Property 조회 도구: modetest -M <driver> -p로 모든 KMS 오브젝트의 property와 현재 값을 확인할 수 있습니다. 유저 공간에서는 DRM_IOCTL_MODE_GETPROPERTY로 property 메타데이터를, DRM_IOCTL_MODE_OBJ_GETPROPERTIES로 오브젝트별 값을 조회합니다.

Color Management 파이프라인

KMS는 CRTC 레벨에서 3단계 색상 처리 파이프라인을 제공합니다. 이 파이프라인으로 HDR 톤매핑, 색 공간 변환, 감마 보정 등을 하드웨어 가속으로 수행합니다.

Plane 합성된 픽셀 1. Degamma LUT DEGAMMA_LUT 비선형 → 선형 변환 (sRGB/PQ EOTF 역변환) 2. CTM Color Transform 3×3 매트릭스 곱셈 (색 공간 변환) 3. Gamma LUT GAMMA_LUT 선형 → 비선형 변환 (디스플레이 감마 적용) 출력 Encoder LUT 크기: DEGAMMA_LUT_SIZE, GAMMA_LUT_SIZE property로 하드웨어 지원 엔트리 수 확인 (읽기 전용). 일반적으로 256 또는 4096 엔트리. CTM은 항상 3×3 = 9개 S31.32 고정소수점 값 (struct drm_color_ctm).
/* Color Management 관련 구조체 */

/* 감마/디감마 LUT 엔트리 (유저 공간에서 설정) */
struct drm_color_lut {
    __u16 red;      /* 0~0xFFFF */
    __u16 green;
    __u16 blue;
    __u16 reserved;
};

/* 색 변환 매트릭스 (CTM) — S31.32 고정소수점 */
struct drm_color_ctm {
    __u64 matrix[9];   /* 3x3, 부호 비트 + 31.32 고정소수 */
};

/* 유저 공간에서 감마 LUT 설정 예시 (libdrm) */
struct drm_color_lut lut[256];
for (int i = 0; i < 256; i++) {
    /* sRGB 감마 커브: 선형 → sRGB 비선형 */
    double v = (double)i / 255.0;
    double srgb = (v <= 0.0031308) ?
        v * 12.92 : 1.055 * pow(v, 1.0/2.4) - 0.055;
    __u16 val = (__u16)(srgb * 0xFFFF);
    lut[i].red = lut[i].green = lut[i].blue = val;
}
/* blob property로 설정 */
drmModeCreatePropertyBlob(fd, lut, sizeof(lut), &blob_id);
/* atomic 커밋에 GAMMA_LUT=blob_id 추가 */
HDR 지원: HDR10에서는 HDR_OUTPUT_METADATA connector property로 SMPTE ST 2086 마스터링 디스플레이 정보와 MaxCLL/MaxFALL 값을 설정합니다. PQ(Perceptual Quantizer) EOTF는 Degamma LUT로 처리하며, BT.2020 색 공간 변환은 CTM으로 수행합니다. 하드웨어가 충분한 LUT 정밀도(최소 1024 엔트리)를 제공해야 정확한 HDR 재현이 가능합니다.

VRR (Variable Refresh Rate) / Adaptive Sync

VRR은 GPU 렌더링 속도에 맞춰 디스플레이 주사율을 동적으로 조절하는 기술입니다. 화면 찢어짐(tearing)과 스터터링(stuttering) 없이 부드러운 프레임 전달을 가능하게 합니다.

기술표준DRM 지원설명
Adaptive-Sync VESA DP Adaptive-Sync DisplayPort connector DP 표준의 VRR. FreeSync 모니터가 주로 사용
HDMI VRR HDMI 2.1 VRR HDMI connector HDMI Forum VRR. QFT (Quick Frame Transport) 포함
Panel Self Refresh eDP PSR/PSR2 eDP connector 정적 화면에서 패널이 자체 리프레시 → GPU 절전
/* VRR 활성화 흐름 (커널 내부) */

/* 1. Connector가 VRR 지원하는지 확인 */
/*    모니터 EDID의 Adaptive-Sync range 파싱 */
connector->vrr_capable = true;  /* 드라이버가 EDID 기반으로 설정 */

/* 2. 유저 공간에서 CRTC의 VRR_ENABLED property 설정 */
/*    → atomic commit에 포함 */
new_crtc_state->vrr_enabled = true;

/* 3. 드라이버 atomic_check에서 VRR 설정 검증 */
static int my_crtc_atomic_check(struct drm_crtc *crtc,
                                struct drm_atomic_state *state)
{
    struct drm_crtc_state *crtc_state =
        drm_atomic_get_new_crtc_state(state, crtc);

    if (crtc_state->vrr_enabled) {
        /* VRR 범위 확인 (min_vfreq ~ max_vfreq) */
        struct drm_connector *conn = get_connector(crtc_state);
        if (!conn->display_info.monitor_range.max_vfreq)
            return -EINVAL;

        /* 하드웨어 VRR 타이밍 설정 */
        my_state->vmin = conn->display_info.monitor_range.min_vfreq;
        my_state->vmax = conn->display_info.monitor_range.max_vfreq;
    }
    return 0;
}

/* 4. 페이지 플립 시 VRR 타이밍 적용 */
/*    GPU가 프레임을 완료하면 즉시 vsync 발생 */
/*    → 가변 vblank 주기 */
VRR 확인 명령:
  • modetest -M amdgpu -p에서 vrr_capable property 확인
  • cat /sys/class/drm/card0-DP-1/vrr_capable — sysfs에서 직접 조회
  • Wayland에서는 wp_tearing_control_v1 프로토콜로 VRR/tearing 제어
  • X11에서는 xrandr --prop으로 VRR 속성 확인

Format Modifier (타일링/압축)

GPU는 메모리 접근 효율을 위해 픽셀 데이터를 단순 행(linear) 방식이 아닌 타일(tile) 형태로 배치합니다. Format Modifier는 프레임버퍼의 메모리 레이아웃을 기술하는 64비트 토큰입니다.

/* Format Modifier 구조 (include/uapi/drm/drm_fourcc.h) */
/*
 * [63:56] = vendor (DRM_FORMAT_MOD_VENDOR_*)
 * [55:0]  = vendor별 modifier 값
 *
 * 0 = DRM_FORMAT_MOD_LINEAR (행 순차 배치, 모든 디바이스 공통)
 */

#define DRM_FORMAT_MOD_LINEAR  0

/* Intel 타일링 modifier 예시 */
#define I915_FORMAT_MOD_X_TILED    /* X-tiling (레거시) */
#define I915_FORMAT_MOD_Y_TILED    /* Y-tiling (Gen9+) */
#define I915_FORMAT_MOD_4_TILED    /* Tile-4 (Gen12.5+, DG2/MTL) */
#define I915_FORMAT_MOD_Y_TILED_CCS /* Y-tiling + CCS 압축 */

/* AMD modifier 예시 */
#define AMD_FMT_MOD                /* 타일 버전, pipe_xor_bits, DCC 등 인코딩 */

/* ARM AFBC (Arm Frame Buffer Compression) */
#define DRM_FORMAT_MOD_ARM_AFBC    /* Mali GPU 프레임버퍼 무손실 압축 */

/* Broadcom */
#define DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED /* VideoCore T-tiling */

/* NVIDIA */
#define DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D /* GOB 기반 타일링 */
레이아웃설명장점단점
Linear 행 순차 배치 (pitch × height) 모든 디바이스 호환, 단순 GPU 캐시 효율 낮음
Tiled 정사각 또는 직사각 타일 단위 배치 2D 공간 지역성 향상, GPU 캐시 효율 디바이스별 포맷 비호환
Compressed 타일링 + 무손실 압축 (CCS, DCC, AFBC) 메모리 대역폭 절감 30~50% 디바이스 간 공유 시 압축 해제 필요
/* 프레임버퍼 생성 시 modifier 지정 (ADDFB2) */
struct drm_mode_fb_cmd2 cmd = {
    .width   = 1920,
    .height  = 1080,
    .pixel_format = DRM_FORMAT_XRGB8888,
    .handles[0] = gem_handle,
    .pitches[0] = pitch,
    .modifier[0] = I915_FORMAT_MOD_Y_TILED, /* modifier 지정 */
    .flags   = DRM_MODE_FB_MODIFIERS,       /* modifier 사용 플래그 */
};
drmIoctl(fd, DRM_IOCTL_MODE_ADDFB2, &cmd);

/* Plane이 지원하는 modifier 목록 조회 */
/* DRM_IOCTL_MODE_GETPLANE2 또는 IN_FORMATS blob property */
/* → (format, modifier) 쌍의 목록 반환 */

/* 드라이버: Plane에 지원 modifier 등록 */
static const uint64_t my_modifiers[] = {
    DRM_FORMAT_MOD_LINEAR,
    I915_FORMAT_MOD_Y_TILED,
    I915_FORMAT_MOD_Y_TILED_CCS,
    DRM_FORMAT_MOD_INVALID  /* 종료 마커 */
};
drm_universal_plane_init(dev, plane, ...,
    formats, num_formats, my_modifiers, ...);
Modifier 호환성: DMA-BUF로 버퍼를 공유할 때 modifier가 다르면 importer가 버퍼를 읽을 수 없습니다. 디바이스 간 버퍼 공유 시에는 DRM_FORMAT_MOD_LINEAR를 사용하거나, 양쪽 디바이스가 모두 지원하는 modifier를 협상해야 합니다. Wayland에서는 zwp_linux_dmabuf_v1 프로토콜로 compositor와 클라이언트 간 modifier를 협상합니다.

GEM (Graphics Execution Manager) 메모리 관리

GEM은 GPU 메모리 버퍼 객체를 관리하는 프레임워크입니다. 유저 공간에서는 정수 핸들로 버퍼를 참조하고, 커널은 내부적으로 drm_gem_object로 관리합니다.

/* GEM 객체 구조 (include/drm/drm_gem.h) */
struct drm_gem_object {
    struct drm_device     *dev;          /* 소속 디바이스 */
    struct file           *filp;         /* shmem backing store */
    size_t                size;          /* 버퍼 크기 */
    struct kref           refcount;      /* 참조 카운팅 */
    uint32_t              name;          /* flink 이름 (레거시) */
    struct dma_buf        *dma_buf;      /* DMA-BUF 익스포트 시 */
    const struct drm_gem_object_funcs *funcs;
};

/* GEM 오퍼레이션 콜백 */
struct drm_gem_object_funcs {
    void (*free)(struct drm_gem_object *obj);
    int  (*open)(struct drm_gem_object *obj, struct drm_file *file);
    void (*close)(struct drm_gem_object *obj, struct drm_file *file);
    int  (*pin)(struct drm_gem_object *obj);
    int  (*vmap)(struct drm_gem_object *obj, struct iosys_map *map);
    struct sg_table *(*get_sg_table)(struct drm_gem_object *obj);
};

GEM 버퍼 생명주기

단계ioctl / 함수설명
생성 DRM_IOCTL_MODE_CREATE_DUMB 비가속 dumb 버퍼 생성 (KMS 전용). 드라이버별 ioctl로 가속 버퍼 생성
매핑 DRM_IOCTL_MODE_MAP_DUMBmmap() 가짜 오프셋 반환 후 mmap으로 유저 공간에 매핑
공유 DRM_IOCTL_PRIME_HANDLE_TO_FD GEM 핸들을 DMA-BUF fd로 변환하여 다른 디바이스와 공유
임포트 DRM_IOCTL_PRIME_FD_TO_HANDLE 외부 DMA-BUF fd를 로컬 GEM 핸들로 변환
해제 DRM_IOCTL_GEM_CLOSE 핸들 해제. 참조 카운트가 0이 되면 실제 메모리 반환

GEM SHMEM 헬퍼

대부분의 임베디드/모바일 GPU 드라이버는 drm_gem_shmem_object를 사용합니다. 시스템 메모리(shmem)에 버퍼를 할당하며, CMA(Contiguous Memory Allocator) 통합도 지원합니다.

#include <drm/drm_gem_shmem_helper.h>

/* GEM SHMEM 기반 드라이버의 dumb_create 구현 */
static int my_dumb_create(struct drm_file *file,
                           struct drm_device *dev,
                           struct drm_mode_create_dumb *args)
{
    /* pitch 정렬 (하드웨어 요구사항에 맞춤) */
    args->pitch = ALIGN(args->width * DIV_ROUND_UP(args->bpp, 8), 64);
    args->size  = args->pitch * args->height;

    /* shmem 헬퍼가 할당 + 핸들 반환 */
    return drm_gem_shmem_dumb_create(file, dev, args);
}

/* 드라이버에서 vmap으로 커널 가상 주소 획득 */
struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(gem_obj);
struct iosys_map map;
drm_gem_shmem_vmap(shmem, &map);
/* map.vaddr로 커널에서 버퍼 접근 */

TTM (Translation Table Manager)

TTM은 전용 VRAM(비디오 메모리)이 있는 GPU를 위한 메모리 관리자입니다. 버퍼를 VRAM과 시스템 메모리 사이에서 이동(eviction/migration)시키는 고급 기능을 제공합니다.

메모리 관리자백업 스토리지VRAM 지원사용 드라이버
GEM (shmem) 시스템 메모리 (shmem/CMA) 없음 panfrost, lima, vc4, v3d, virtio-gpu
GEM + VRAM helper 시스템 + VRAM 단순 ast, simpledrm, hibmc
TTM 시스템 + VRAM + GART 전체 (eviction, migration) amdgpu, nouveau, i915 (부분), vmwgfx, radeon
/* TTM 메모리 도메인 (placement) */
struct ttm_place {
    uint32_t  mem_type;   /* TTM_PL_SYSTEM, TTM_PL_VRAM, TTM_PL_TT */
    uint32_t  flags;      /* TTM_PL_FLAG_CONTIGUOUS 등 */
    uint64_t  fpfn;       /* 시작 페이지 프레임 번호 */
    uint64_t  lpfn;       /* 끝 페이지 프레임 번호 (0=제한 없음) */
};

/* TTM 버퍼 오브젝트 주요 필드 */
struct ttm_buffer_object {
    struct drm_gem_object    base;         /* GEM 상속 */
    struct ttm_device        *bdev;         /* TTM 디바이스 */
    struct ttm_resource      *resource;     /* 현재 메모리 위치 */
    struct ttm_placement     placement;     /* 선호 도메인 */
    struct ttm_tt            *ttm;          /* 시스템 메모리 페이지 */
};

/* TTM 메모리 도메인 상수 */
#define TTM_PL_SYSTEM  0  /* 시스템 RAM (커널 관리) */
#define TTM_PL_TT      1  /* GART/GTT 매핑된 시스템 메모리 */
#define TTM_PL_VRAM    2  /* 디바이스 전용 비디오 메모리 */
TTM Eviction (퇴거): VRAM이 부족하면 TTM은 사용 빈도가 낮은 버퍼를 시스템 메모리로 이동시킵니다. 드라이버는 ttm_device_funcs.evict_flags 콜백으로 퇴거 정책을 설정합니다. GPU가 해당 버퍼에 접근하면 다시 VRAM으로 마이그레이션됩니다.

DMA-BUF (버퍼 공유)

심화 학습: DMA-BUF의 exporter/importer 구조, dma_fence/dma_resv, DMA-BUF Heaps 및 DMA 서브시스템 전체 종합 가이드는 DMA 심화 페이지를 참조하십시오.

DMA-BUF는 디바이스 간 버퍼 공유를 위한 커널 프레임워크입니다. GPU에서 렌더링한 버퍼를 디스플레이 컨트롤러, 비디오 인코더, 카메라 등 다른 디바이스와 복사 없이 공유할 수 있습니다.

Exporter (GPU 드라이버) 버퍼 소유자 DMA-BUF (fd) struct dma_buf + sg_table Display (KMS) V4L2 (카메라) 다른 GPU export import Implicit Fence (dma_resv) 동기화: GPU 작업 완료 대기 User Space: SCM_RIGHTS로 fd 전달 또는 PRIME ioctl
/* DMA-BUF exporter 오퍼레이션 (드라이버 구현) */
static const struct dma_buf_ops my_dmabuf_ops = {
    .attach         = my_dmabuf_attach,       /* importer 디바이스 등록 */
    .detach         = my_dmabuf_detach,
    .map_dma_buf    = my_dmabuf_map,          /* sg_table 반환 (DMA 주소) */
    .unmap_dma_buf  = my_dmabuf_unmap,
    .release        = my_dmabuf_release,      /* 버퍼 해제 */
    .mmap           = my_dmabuf_mmap,         /* 유저 공간 매핑 */
    .vmap           = my_dmabuf_vmap,         /* 커널 가상 주소 매핑 */
};

/* GEM → DMA-BUF export (PRIME) */
struct dma_buf *dmabuf = drm_gem_prime_export(gem_obj, flags);
/* → 유저 공간에서 fd = DRM_IOCTL_PRIME_HANDLE_TO_FD(handle) */

/* DMA-BUF → GEM import (PRIME) */
struct drm_gem_object *obj = drm_gem_prime_import(dev, dmabuf);
/* → 유저 공간에서 handle = DRM_IOCTL_PRIME_FD_TO_HANDLE(fd) */

dma_fence & dma_resv (동기화)

GPU 작업은 비동기로 실행되므로, 버퍼를 공유하는 디바이스들은 작업 완료를 동기화해야 합니다.

구조체역할사용 예
dma_fence GPU 작업 하나의 완료를 나타내는 동기화 프리미티브 GPU 커맨드 서브미션 후 fence 생성
dma_resv 버퍼별 fence 집합 관리 (reservation object) DMA-BUF에 내장. 읽기/쓰기 fence 추적
sync_file dma_fence를 유저 공간 fd로 노출 Explicit sync (Vulkan, Android)
/* Fence 기본 사용 패턴 */
struct dma_fence *fence;

/* GPU 작업 제출 시 fence 생성 */
fence = my_gpu_submit_job(job);

/* 버퍼의 reservation object에 fence 등록 */
dma_resv_add_fence(bo->base.resv, fence, DMA_RESV_USAGE_WRITE);

/* 다른 디바이스가 이 버퍼를 사용하기 전에 대기 */
ret = dma_resv_wait_timeout(bo->base.resv, DMA_RESV_USAGE_WRITE,
                            true, msecs_to_jiffies(5000));

/* Fence 시그널 (GPU 인터럽트 핸들러에서 호출) */
dma_fence_signal(fence);
dma_fence_put(fence);

GPU 커맨드 서브미션

GPU 렌더링 작업은 커맨드 버퍼(command buffer)를 통해 제출됩니다. 유저 공간(Mesa/Vulkan 드라이버)이 GPU 명령어를 버퍼에 기록하고, 커널 드라이버가 이를 GPU 하드웨어에 전달합니다.

User Space Mesa GL/Vulkan 커맨드 버퍼 생성 GEM BO 할당 텍스처/버텍스/UBO Relocation/VA Map 주소 패칭 Submit ioctl 드라이버 고유 ioctl ioctl Kernel DRM Driver 권한/BO 검증 유효성 검사 dma_resv 락 의존성 fence 대기 drm_sched 등록 스케줄러 큐잉 run_job → HW 제출 링 버퍼/도어벨 기록 fence fd 반환 (explicit sync) 또는 dma_resv에 등록 (implicit) MMIO / Doorbell GPU Hardware Command Parser Shader Units Rasterizer ROP/Blender 완료 IRQ → fence signal

서브미션 모델 비교

모델드라이버ioctl특징
Ring Buffer i915 (레거시) I915_GEM_EXECBUFFER2 커맨드 버퍼를 링 버퍼에 복사. relocation으로 GPU 주소 패칭
Submit Queue amdgpu AMDGPU_CS IB(Indirect Buffer) 체인 제출. 다중 엔진(GFX/SDMA/UVD) 지원
Exec Queue xe (Intel) DRM_XE_EXEC VM-bind 기반. relocation 없음, GPU 페이지 테이블 직접 관리
Submitqueue msm (Qualcomm) MSM_GEM_SUBMIT 우선순위별 큐. faults 지원 (sparse binding)
Simple Submit panfrost, lima 드라이버별 drm_sched 기반. 단순 job 제출
/* amdgpu 커맨드 서브미션 예시 (유저 공간, libdrm_amdgpu) */
struct amdgpu_cs_request cs_req = {};
struct amdgpu_cs_ib_info ib_info = {
    .ib_mc_address = ib_gpu_addr,  /* IB의 GPU 가상 주소 */
    .size          = ib_size_dw,   /* 더블워드 단위 크기 */
};
cs_req.ip_type       = AMDGPU_HW_IP_GFX;   /* GFX 엔진 */
cs_req.number_of_ibs = 1;
cs_req.ibs           = &ib_info;

/* 의존 fence 목록 (이전 작업 완료 대기) */
struct amdgpu_cs_fence_info fence_info = { ... };
cs_req.fence_info = fence_info;

/* 서브미션 */
amdgpu_cs_submit(ctx, 0, &cs_req, 1);

/* 완료 대기 (선택적) */
struct amdgpu_cs_fence fence = {
    .context = ctx,
    .ip_type = AMDGPU_HW_IP_GFX,
    .fence   = cs_req.seq_no,
};
amdgpu_cs_query_fence_status(&fence, timeout_ns, 0, &expired);
/* 커널 드라이버의 커맨드 서브미션 검증 (보안상 중요) */

/* 1. 버퍼 오브젝트 유효성 */
/*    - 유저가 전달한 GEM 핸들이 실제 소유인지 확인 */
/*    - BO가 올바른 메모리 도메인에 있는지 확인 */

/* 2. 커맨드 파싱 (일부 드라이버) */
/*    - 금지된 GPU 레지스터 접근 차단 */
/*    - 다른 프로세스의 GPU 주소 공간 접근 방지 */
/*    - 최신 GPU는 하드웨어 격리 (per-context page table)로 대체 */

/* 3. 의존성 처리 */
/*    - 공유 BO의 dma_resv에서 기존 fence 확인 */
/*    - 의존 fence를 drm_sched_job의 dependency로 등록 */
/*    - 모든 의존 fence가 시그널된 후에만 GPU에 제출 */

/* 4. fence 등록 */
/*    - 새 작업의 fence를 사용 BO의 dma_resv에 추가 */
/*    - explicit sync: fence를 sync_file fd로 유저에게 반환 */
VM-Bind vs Relocation: 레거시 드라이버(i915 execbuf)는 커맨드 버퍼 내 GPU 주소를 매번 패칭(relocation)했습니다. 현대 드라이버(xe, amdgpu VM)는 유저 공간이 GPU 페이지 테이블에 직접 매핑(VM-bind)하여 커맨드 버퍼가 고정 GPU 가상 주소를 사용합니다. VM-bind 모델은 relocation 오버헤드를 제거하고, sparse binding(일부 영역만 매핑)을 가능하게 합니다.

GPU 스케줄러 (drm_sched)

DRM GPU 스케줄러(drivers/gpu/drm/scheduler/)는 여러 프로세스의 GPU 작업을 공정하게 스케줄링하고, GPU 행(hang) 감지 및 복구를 처리합니다.

#include <drm/gpu_scheduler.h>

/* 스케줄러 초기화 (드라이버 probe에서) */
struct drm_gpu_scheduler sched;

drm_sched_init(&sched,
    &my_sched_ops,              /* 드라이버 콜백 */
    NULL,                       /* workqueue (NULL=기본) */
    num_hw_rings,               /* 하드웨어 큐 수 */
    0,                          /* credit limit */
    msecs_to_jiffies(10000),   /* 타임아웃 (10초) */
    NULL,                       /* workqueue for timeout */
    NULL,                       /* score */
    "my-gpu",                   /* 이름 */
    dev->dev);

/* 스케줄러 오퍼레이션 */
static const struct drm_sched_backend_ops my_sched_ops = {
    .run_job    = my_run_job,        /* GPU에 작업 제출 */
    .timedout_job = my_timedout_job, /* 타임아웃 시 GPU 리셋 */
    .free_job   = my_free_job,       /* 완료된 작업 해제 */
};

/* run_job 구현 예시 */
static struct dma_fence *my_run_job(struct drm_sched_job *job)
{
    struct my_job *mj = to_my_job(job);

    /* GPU 하드웨어 레지스터에 커맨드 버퍼 주소 기록 */
    writel(mj->cmd_buf_addr, gpu->regs + CMD_SUBMIT);

    /* 완료 fence 반환 (GPU 인터럽트에서 시그널) */
    return dma_fence_get(mj->fence);
}
구성 요소설명
drm_sched_entity클라이언트별 작업 큐. 유저 컨텍스트 하나가 entity 하나를 소유. 우선순위 지정 가능
drm_sched_jobGPU에 제출할 작업 하나. 의존 fence 목록 보유
drm_gpu_scheduler하나의 GPU 엔진(링 버퍼)을 관리. 라운드 로빈으로 entity에서 job 디큐
우선순위DRM_SCHED_PRIORITY_KERNEL > HIGH > NORMAL > LOW
타임아웃job이 지정 시간 내 완료 안 되면 timedout_job 콜백 → GPU 리셋

DRM 드라이버 기본 구조

최소한의 KMS DRM 드라이버 (디스플레이만 지원, GPU 가속 없음)의 골격입니다.

#include <linux/module.h>
#include <linux/platform_device.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include <drm/drm_fbdev_shmem.h>

struct my_drm {
    struct drm_device              drm;
    struct drm_simple_display_pipe pipe;
    struct drm_connector           connector;
    void __iomem                  *regs;
};

/* --- Connector --- */
static int my_connector_get_modes(struct drm_connector *conn)
{
    /* 고정 해상도 모드 추가 (실제로는 EDID 파싱) */
    return drm_add_modes_noedid(conn, 1920, 1080);
}

static const struct drm_connector_helper_funcs my_conn_helpers = {
    .get_modes = my_connector_get_modes,
};

/* --- Simple Display Pipe --- */
static void my_pipe_enable(struct drm_simple_display_pipe *pipe,
                           struct drm_crtc_state *crtc_state,
                           struct drm_plane_state *plane_state)
{
    struct my_drm *my = container_of(pipe, struct my_drm, pipe);
    struct drm_framebuffer *fb = plane_state->fb;

    /* 하드웨어 레지스터에 프레임버퍼 주소/포맷 설정 */
    writel(drm_fb_dma_get_gem_addr(fb, plane_state, 0),
           my->regs + FB_ADDR_REG);
    writel(fb->pitches[0], my->regs + FB_PITCH_REG);
    /* 디스플레이 엔진 활성화 */
    writel(1, my->regs + DISPLAY_ENABLE_REG);
}

static void my_pipe_update(struct drm_simple_display_pipe *pipe,
                           struct drm_plane_state *old_state)
{
    struct drm_plane_state *state = pipe->plane.state;
    if (state->fb)
        my_pipe_enable(pipe, NULL, state);
}

static const struct drm_simple_display_pipe_funcs my_pipe_funcs = {
    .enable  = my_pipe_enable,
    .update  = my_pipe_update,
};

/* --- DRM Driver --- */
DEFINE_DRM_GEM_FOPS(my_fops);

static const struct drm_driver my_driver = {
    .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
    .fops            = &my_fops,
    DRM_GEM_SHMEM_DRIVER_OPS,
    .name  = "my-display",
    .desc  = "My simple display driver",
    .date  = "20260101",
    .major = 1,
    .minor = 0,
};

/* --- Platform Driver --- */
static int my_probe(struct platform_device *pdev)
{
    struct my_drm *my;
    struct drm_device *drm;
    static const uint32_t formats[] = { DRM_FORMAT_XRGB8888 };
    int ret;

    my = devm_drm_dev_alloc(&pdev->dev, &my_driver,
                            struct my_drm, drm);
    if (IS_ERR(my))
        return PTR_ERR(my);
    drm = &my->drm;

    /* MMIO 매핑 */
    my->regs = devm_platform_ioremap_resource(pdev, 0);
    if (IS_ERR(my->regs))
        return PTR_ERR(my->regs);

    /* Connector 초기화 */
    drm_connector_init(drm, &my->connector,
                       &my_conn_funcs, DRM_MODE_CONNECTOR_Unknown);
    drm_connector_helper_add(&my->connector, &my_conn_helpers);

    /* Simple display pipe 초기화 (CRTC+Plane+Encoder 한 번에) */
    ret = drm_simple_display_pipe_init(drm, &my->pipe,
            &my_pipe_funcs, formats, ARRAY_SIZE(formats),
            NULL, &my->connector);
    if (ret)
        return ret;

    drm_mode_config_reset(drm);
    ret = drm_dev_register(drm, 0);
    if (ret)
        return ret;

    /* fbdev 에뮬레이션 (콘솔 출력) */
    drm_fbdev_shmem_setup(drm, 32);
    return 0;
}

MODULE_LICENSE("GPL");
drm_simple_display_pipe: 간단한 디스플레이 전용 드라이버(CRTC 1개, Plane 1개, Encoder 1개)에 적합한 헬퍼입니다. 복잡한 다중 CRTC/Plane 구성이 필요하면 각 KMS 오브젝트를 개별적으로 초기화해야 합니다.

주요 오픈소스 DRM 드라이버

드라이버하드웨어커널 경로메모리 관리특징
i915 Intel 내장 GPU drivers/gpu/drm/i915/ GEM + GTT/GGTT GuC/HuC 펌웨어 지원, Display 13/14, Xe 드라이버로 전환 중
xe Intel Xe GPU (DG2+) drivers/gpu/drm/xe/ TTM 기반 i915 후속. 디스크리트 GPU(Arc) 지원, drm_sched 사용
amdgpu AMD Radeon (GCN+) drivers/gpu/drm/amd/ TTM DC (Display Core), KFD (HSA 컴퓨트), SR-IOV, RAS
nouveau NVIDIA (리버스 엔지니어링) drivers/gpu/drm/nouveau/ TTM 커뮤니티 개발, 최신 GPU 지원 제한적, GSP 펌웨어 지원
panfrost ARM Mali (Midgard/Bifrost) drivers/gpu/drm/panfrost/ GEM shmem OpenGL ES 지원, drm_sched 사용, MMU 자체 관리
lima ARM Mali (Utgard) drivers/gpu/drm/lima/ GEM shmem 레거시 ARM Mali-400/450, OpenGL ES 2.0
vc4 Broadcom VideoCore IV drivers/gpu/drm/vc4/ GEM CMA Raspberry Pi 0~3. v3d는 Pi 4/5용 별도 드라이버
msm Qualcomm Adreno drivers/gpu/drm/msm/ GEM Freedreno 스택, submitqueue 기반 커맨드 제출
virtio-gpu 가상 GPU (QEMU/virgl) drivers/gpu/drm/virtio/ GEM shmem VM 내 3D 가속, virglrenderer로 호스트 GPU 사용
simpledrm EFI/BIOS 프레임버퍼 drivers/gpu/drm/tiny/simpledrm.c GEM shmem 부팅 초기 콘솔. UEFI GOP/VBE 프레임버퍼 위에서 동작
nouveau 참고: NVIDIA는 공식 오픈소스 커널 드라이버를 별도로 제공합니다 (Turing 이후, nvidia-open). nouveau는 커뮤니티 리버스 엔지니어링 기반으로, 최신 GPU에서는 전력 관리(reclocking)와 성능이 제한됩니다. 최근 커널에서는 GSP(GPU System Processor) 펌웨어를 통해 nouveau의 기능이 개선되고 있습니다.

DRM Bridge & Panel 프레임워크

임베디드/모바일 SoC에서는 디스플레이 출력이 여러 개의 하드웨어 블록 체인으로 구성됩니다. drm_bridge는 이 체인의 중간 단계(MIPI-DSI to HDMI 변환기, LVDS 직렬화기 등)를, drm_panel은 최종 디스플레이 패널을 추상화합니다.

Encoder SoC 디스플레이 출력 Bridge #1 예: DSI→HDMI (lt9611, sii902x 등) Bridge #2 예: LVDS 직렬화기 (sn65dsi86 등) Panel drm_panel (또는 Connector) LCD 각 Bridge는 pre_enable → enable → disable → post_disable 콜백을 체인 순서로 실행 DRM 코어가 체인 순회를 자동 관리 (atomic helper)
/* DRM Bridge 드라이버 구현 예시 */
#include <drm/drm_bridge.h>

static const struct drm_bridge_funcs my_bridge_funcs = {
    /* 디스플레이 파이프라인 활성화 순서 */
    .pre_enable  = my_bridge_pre_enable,  /* PLL/클럭 설정 */
    .enable      = my_bridge_enable,      /* 출력 활성화 */
    .disable     = my_bridge_disable,
    .post_disable = my_bridge_post_disable,

    /* 모드 검증 */
    .mode_valid  = my_bridge_mode_valid,  /* 지원 해상도 필터링 */
    .mode_fixup  = my_bridge_mode_fixup,  /* adjusted_mode 수정 */

    /* Atomic 지원 (권장) */
    .atomic_pre_enable = my_bridge_atomic_pre_enable,
    .atomic_enable     = my_bridge_atomic_enable,
    .atomic_disable    = my_bridge_atomic_disable,

    /* 출력 감지 (DRM_BRIDGE_OP_DETECT) */
    .detect      = my_bridge_detect,
    .get_modes   = my_bridge_get_modes,   /* EDID 기반 모드 목록 */
    .get_edid    = my_bridge_get_edid,    /* EDID 읽기 */
};

static int my_bridge_probe(struct i2c_client *client)
{
    struct my_bridge *mb = devm_kzalloc(&client->dev, sizeof(*mb), GFP_KERNEL);

    mb->bridge.funcs = &my_bridge_funcs;
    mb->bridge.of_node = client->dev.of_node;
    mb->bridge.type = DRM_MODE_CONNECTOR_HDMIA;  /* 출력 커넥터 타입 */
    mb->bridge.ops = DRM_BRIDGE_OP_DETECT |
                     DRM_BRIDGE_OP_EDID |
                     DRM_BRIDGE_OP_MODES;

    drm_bridge_add(&mb->bridge);
    return 0;
}
/* DRM Panel 드라이버 구현 예시 */
#include <drm/drm_panel.h>

static const struct drm_panel_funcs my_panel_funcs = {
    .prepare    = my_panel_prepare,     /* 전원 ON, 리셋 해제 */
    .enable     = my_panel_enable,      /* 백라이트 ON, 디스플레이 ON */
    .disable    = my_panel_disable,     /* 백라이트 OFF */
    .unprepare  = my_panel_unprepare,   /* 전원 OFF */
    .get_modes  = my_panel_get_modes,   /* 고정 모드 반환 */
};

static int my_panel_probe(struct mipi_dsi_device *dsi)
{
    struct my_panel *panel = devm_kzalloc(&dsi->dev, sizeof(*panel), GFP_KERNEL);

    drm_panel_init(&panel->panel, &dsi->dev, &my_panel_funcs,
                   DRM_MODE_CONNECTOR_DSI);
    drm_panel_add(&panel->panel);
    return 0;
}

/* 디스플레이 드라이버에서 bridge + panel 연결 */
struct drm_bridge *bridge;
bridge = devm_drm_of_get_bridge(dev, dev->of_node, 0, 0);
/* drm_panel은 자동으로 panel-bridge로 래핑됨 */
drm_bridge_attach(encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);

/* 체인의 마지막 bridge에서 connector 생성 */
connector = drm_bridge_connector_init(drm, encoder);
Device Tree 바인딩: Bridge와 Panel은 Device Tree의 ports/port/endpoint 노드로 연결됩니다. of_graph_get_remote_port_parent()로 연결된 디바이스를 탐색하고, devm_drm_of_get_bridge()가 DT 그래프를 따라 bridge 또는 panel-bridge를 자동으로 찾습니다.

GPU 가상 메모리 (GPUVM)

현대 GPU는 자체 MMU를 가지며, 프로세스별 독립적인 GPU 가상 주소 공간을 제공합니다. 커널 DRM 프레임워크는 drm_gpuvm(drivers/gpu/drm/drm_gpuvm.c)으로 이를 추상화합니다.

/* GPU 가상 주소 공간 개념 */

/*
 * CPU 가상 메모리와 유사한 구조:
 *
 * Process A의 GPU VA 공간    Process B의 GPU VA 공간
 * ┌──────────────────┐      ┌──────────────────┐
 * │ 0x0000_0000_0000 │      │ 0x0000_0000_0000 │
 * │ (unmapped)       │      │ (unmapped)       │
 * ├──────────────────┤      ├──────────────────┤
 * │ 0x0001_0000_0000 │      │ 0x0001_0000_0000 │
 * │ Shader Code      │─┐    │ Shader Code      │─┐
 * ├──────────────────┤ │    ├──────────────────┤ │
 * │ 0x0002_0000_0000 │ │    │ 0x0002_0000_0000 │ │
 * │ Vertex Buffer    │ │    │ Texture Data     │ │
 * ├──────────────────┤ │    ├──────────────────┤ │
 * │ ...              │ │    │ ...              │ │
 * └──────────────────┘ │    └──────────────────┘ │
 *                      ▼                         ▼
 *              ┌──────────────────────────┐
 *              │   GPU MMU (Page Tables)  │
 *              │   GPU VA → Physical Addr │
 *              └──────────────────────────┘
 *                      │
 *              ┌───────┴────────┐
 *              ▼                ▼
 *       ┌──────────┐   ┌──────────┐
 *       │  VRAM    │   │ System   │
 *       │          │   │ Memory   │
 *       └──────────┘   └──────────┘
 */

/* drm_gpuvm 주요 구조체 */
struct drm_gpuvm {
    const char              *name;
    struct drm_device       *drm;
    struct rb_root_cached   rb;     /* GPU VA 매핑 레드-블랙 트리 */
    u64 mm_start, mm_range;          /* 관리 범위 */
};

/* GPU VA 매핑 하나 */
struct drm_gpuva {
    u64 va_start, va_range;           /* GPU 가상 주소 범위 */
    struct drm_gem_object *gem_obj;   /* 백업 GEM 객체 */
    u64 gem_offset;                   /* GEM 내 오프셋 */
};
IOMMU vs GPU MMU: IOMMU(VT-d/SMMU)는 디바이스의 DMA 주소를 물리 주소로 변환하는 시스템 레벨 하드웨어입니다. GPU MMU는 GPU 내부의 셰이더/엔진이 사용하는 가상 주소를 물리 주소로 변환합니다. 두 계층은 독립적으로 동작하며, GPU DMA 트래픽은 IOMMU도 통과합니다.

GPU 전원 관리

GPU는 시스템에서 가장 전력을 많이 소비하는 디바이스 중 하나이므로, 정교한 전원 관리가 필수적입니다.

메커니즘설명커널 인터페이스
Runtime PM GPU 유휴 시 자동 절전. D3cold까지 진입 가능 pm_runtime_get_sync() / pm_runtime_put_autosuspend()
DVFS Dynamic Voltage and Frequency Scaling. 부하에 따라 클럭/전압 조절 devfreq 프레임워크 또는 드라이버 자체 구현
Power Gating 미사용 GPU 블록(셰이더 유닛 등)의 전원을 완전히 차단 드라이버별 구현 (HW 의존)
Clock Gating 미사용 블록의 클럭만 차단 (power gating보다 빠른 복구) 드라이버별 구현
Hybrid GPU 내장/외장 GPU 전환 (PRIME offload, reverse PRIME) DRI_PRIME=1 환경 변수, switcheroo sysfs
# GPU Runtime PM 상태 확인
cat /sys/class/drm/card0/device/power/runtime_status
# active / suspended / suspending

# Runtime PM 자동 절전 지연 설정 (밀리초)
echo 5000 > /sys/class/drm/card0/device/power/autosuspend_delay_ms

# GPU 클럭 주파수 확인 (amdgpu 예시)
cat /sys/class/drm/card0/device/pp_dpm_sclk
# 0: 300Mhz
# 1: 600Mhz *
# 2: 900Mhz

# PRIME GPU 오프로드 (외장 GPU로 렌더링)
DRI_PRIME=1 glxinfo | grep "OpenGL renderer"

# vga_switcheroo 상태 (하이브리드 GPU)
cat /sys/kernel/debug/vgaswitcheroo/switch

GPU 리셋 및 복구

GPU는 잘못된 셰이더, 무한 루프, 하드웨어 결함 등으로 응답 불능(hang) 상태에 빠질 수 있습니다. DRM 프레임워크는 hang 감지, GPU 리셋, 작업 재제출의 3단계 복구 메커니즘을 제공합니다.

Hang 감지

메커니즘설명구현
스케줄러 타임아웃 drm_sched이 job별 타이머 설정. 만료 시 timedout_job 콜백 대부분의 현대 드라이버
하드웨어 워치독 GPU 내부 워치독 타이머가 hang 감지 후 인터럽트 발생 amdgpu (UVD/VCE), 일부 ARM GPU
Heartbeat 주기적으로 GPU에 nop 작업 제출 후 완료 확인 i915 (engine heartbeat)
Seqno 모니터링 GPU가 작업 완료 시 증가시키는 시퀀스 번호를 주기적으로 확인 레거시 드라이버

리셋 수준

/* GPU 리셋 수준 (세밀한 것부터 거친 순서) */

/* Level 1: Per-engine 리셋 (권장, 최소 영향) */
/* 특정 GPU 엔진(GFX, SDMA, 비디오 등)만 리셋 */
/* 다른 엔진은 계속 동작 */
amdgpu_device_reset_engine(adev, ring);  /* amdgpu 예시 */

/* Level 2: Per-context 리셋 (하드웨어 지원 필요) */
/* 특정 GPU 컨텍스트만 무효화 */
/* i915: 잘못된 컨텍스트를 ban (금지) 처리 */
intel_context_ban(ce, NULL);

/* Level 3: 전체 GPU 리셋 */
/* 모든 엔진/컨텍스트 중단 후 GPU 완전 재초기화 */
amdgpu_device_gpu_recover(adev, NULL, false);

/* Level 4: FLR (Function Level Reset) - PCIe */
/* PCIe 레벨에서 디바이스 전체 리셋 */
pci_reset_function(pdev);

/* Level 5: BACO (Bus Active, Chip Off) - AMD */
/* GPU 전원을 완전히 차단 후 재투입 */
amdgpu_device_baco_enter(adev_to_drm(adev));
amdgpu_device_baco_exit(adev_to_drm(adev));

복구 흐름

/* drm_sched 타임아웃 → 복구 흐름 */

/* 1. 타임아웃 콜백 호출 */
static enum drm_gpu_sched_stat
my_timedout_job(struct drm_sched_job *job)
{
    struct my_device *dev = job_to_dev(job);

    /* 2. GPU 상태 덤프 (디버깅용) */
    dev_coredump_snapshot(dev);  /* devcoredump 프레임워크 */

    /* 3. GPU 리셋 수행 */
    my_gpu_reset(dev);

    /* 4. 스케줄러에 리셋 완료 통보 */
    /*    → 대기 중인 fence들을 에러로 시그널 */
    /*    → guilty 작업의 entity에 guilty 플래그 설정 */
    return DRM_GPU_SCHED_STAT_NOMINAL;
}

/* 5. 유저 공간 알림 */
/*    - 리셋 후 fence가 에러(-EIO)로 시그널됨 */
/*    - Vulkan: VK_ERROR_DEVICE_LOST 반환 */
/*    - GL: GL_CONTEXT_LOST 또는 glGetGraphicsResetStatusARB() */
/*    - amdgpu: AMDGPU_CTX_QUERY2 ioctl로 리셋 감지 */
GPU 리셋 영향:
  • VRAM 콘텐츠: 전체 리셋 시 VRAM 내용이 소실될 수 있음. TTM이 시스템 메모리에 백업한 BO만 복구 가능
  • 디스플레이: GPU 리셋 중 화면이 일시적으로 검은색으로 전환될 수 있음 (modeset 재설정 필요)
  • 멀티 GPU: SR-IOV 환경에서는 VF(Virtual Function) 리셋이 다른 VF에 영향을 줄 수 있음
  • RAS (Reliability): amdgpu는 ras_controller로 ECC 에러 감지 후 수정 불가 에러 시 자동 리셋
# GPU 리셋 관련 sysfs/debugfs

# amdgpu: 수동 GPU 복구 트리거
echo 1 > /sys/kernel/debug/dri/0/amdgpu_gpu_recover

# i915: 엔진 리셋 카운트
cat /sys/kernel/debug/dri/0/i915_reset_info

# devcoredump: GPU 상태 덤프 읽기
cat /sys/class/devcoredump/devcd0/data > gpu_dump.bin

# devcoredump 삭제 (5분 후 자동 삭제)
echo 1 > /sys/class/devcoredump/devcd0/data

# dmesg에서 GPU 리셋 로그 확인
dmesg | grep -i "gpu\|reset\|hang\|timed out"

커널 설정 (Kconfig)

# DRM 핵심 설정
CONFIG_DRM=m                      # DRM 코어 모듈
CONFIG_DRM_KMS_HELPER=m           # KMS 헬퍼 함수
CONFIG_DRM_GEM_SHMEM_HELPER=m     # GEM shmem 헬퍼
CONFIG_DRM_SCHED=m                # GPU 스케줄러
CONFIG_DRM_TTM=m                  # TTM 메모리 관리자
CONFIG_DRM_DISPLAY_HELPER=m       # DP/HDMI 디스플레이 헬퍼

# 드라이버별 설정
CONFIG_DRM_I915=m                 # Intel i915
CONFIG_DRM_XE=m                   # Intel Xe
CONFIG_DRM_AMDGPU=m               # AMD GPU
CONFIG_DRM_NOUVEAU=m              # NVIDIA (오픈소스)
CONFIG_DRM_PANFROST=m             # ARM Mali (Midgard/Bifrost)
CONFIG_DRM_LIMA=m                 # ARM Mali (Utgard)
CONFIG_DRM_VC4=m                  # Broadcom VideoCore
CONFIG_DRM_MSM=m                  # Qualcomm Adreno
CONFIG_DRM_VIRTIO_GPU=m           # 가상 GPU (QEMU)
CONFIG_DRM_SIMPLEDRM=y            # EFI/BIOS 프레임버퍼

# fbdev 에뮬레이션 (DRM 위에서 /dev/fb0 제공)
CONFIG_DRM_FBDEV_EMULATION=y

# DMA-BUF
CONFIG_DMA_SHARED_BUFFER=y

DRM 디버깅

디버깅 도구

도구용도사용법
modetest KMS 오브젝트 나열, 모드 테스트 modetest -M i915 (libdrm 제공)
drm_info DRM 디바이스 전체 정보 출력 (JSON) drm_info
intel_gpu_top Intel GPU 엔진별 사용률 모니터링 intel_gpu_top (intel-gpu-tools)
umr AMD GPU 레지스터 읽기/디코딩 umr -O bits -r *.mmMC_VM_FB_LOCATION_BASE
weston-debug Wayland compositor 디버깅 Weston의 DRM backend 로그
debugfs DRM 내부 상태 확인 /sys/kernel/debug/dri/0/

커널 디버그 메시지

# DRM 디버그 레벨 설정 (비트마스크)
echo 0x1ff > /sys/module/drm/parameters/debug

# 비트마스크 의미:
#   0x001 = DRM_UT_CORE     — DRM 코어
#   0x002 = DRM_UT_DRIVER   — 드라이버
#   0x004 = DRM_UT_KMS      — 모드 설정
#   0x008 = DRM_UT_PRIME    — PRIME/DMA-BUF
#   0x010 = DRM_UT_ATOMIC   — Atomic modesetting
#   0x020 = DRM_UT_VBL      — Vblank
#   0x040 = DRM_UT_STATE    — Atomic 상태
#   0x080 = DRM_UT_LEASE    — DRM 리스
#   0x100 = DRM_UT_DP       — DisplayPort
#   0x200 = DRM_UT_DRMRES   — 리소스 관리

# 부팅 시 커널 파라미터로 설정
# drm.debug=0x1e (DRIVER+KMS+PRIME+ATOMIC)

# DRM debugfs 정보 확인
ls /sys/kernel/debug/dri/0/
# clients  gem_names  name  state  ...

# GEM 객체 목록 (메모리 사용량 확인)
cat /sys/kernel/debug/dri/0/gem_names

# 현재 KMS 상태 (모든 CRTC/Plane/Connector)
cat /sys/kernel/debug/dri/0/state

# GPU 행(hang) 시 ftrace로 추적
echo 1 > /sys/kernel/debug/tracing/events/drm/enable
cat /sys/kernel/debug/tracing/trace_pipe
GPU 행(hang) 디버깅:
  • dmesg에서 "GPU HANG" 또는 "timed out" 메시지 확인
  • i915: /sys/kernel/debug/dri/0/i915_gpu_info에서 엔진 상태 덤프
  • amdgpu: /sys/kernel/debug/dri/0/amdgpu_gpu_recover로 수동 GPU 리셋
  • GPU coredump: 일부 드라이버는 devcoredump 프레임워크로 GPU 상태 덤프 생성 (/sys/class/devcoredump/)

DRM ioctl 요약

ioctl용도필요 권한
DRM_IOCTL_VERSION드라이버 이름/버전 조회없음
DRM_IOCTL_GET_CAP기능 조회 (PRIME, ATOMIC 등)없음
DRM_IOCTL_MODE_GETRESOURCESCRTC/Connector/Encoder ID 목록없음
DRM_IOCTL_MODE_GETCONNECTORConnector 상태, 지원 모드 조회없음
DRM_IOCTL_MODE_GETENCODEREncoder 정보 조회없음
DRM_IOCTL_MODE_GETCRTCCRTC 현재 모드 조회없음
DRM_IOCTL_MODE_SETCRTC모드 설정 (레거시)DRM Master
DRM_IOCTL_MODE_ATOMICAtomic 모드 설정/페이지 플립DRM Master
DRM_IOCTL_MODE_CREATE_DUMBDumb 버퍼 생성없음
DRM_IOCTL_MODE_MAP_DUMBDumb 버퍼 mmap 오프셋없음
DRM_IOCTL_MODE_DESTROY_DUMBDumb 버퍼 해제없음
DRM_IOCTL_PRIME_HANDLE_TO_FDGEM → DMA-BUF fd없음
DRM_IOCTL_PRIME_FD_TO_HANDLEDMA-BUF fd → GEM없음
DRM_IOCTL_GEM_CLOSEGEM 핸들 해제없음
DRM_IOCTL_MODE_ADDFB2프레임버퍼 객체 생성없음
DRM_IOCTL_MODE_PAGE_FLIP페이지 플립 (레거시)DRM Master
DRM_IOCTL_SET_CLIENT_CAP클라이언트 기능 활성화 (ATOMIC 등)DRM Master

DRM Lease (디스플레이 리스)

DRM Lease는 DRM Master가 자신의 KMS 리소스(CRTC, Connector, Plane) 일부를 다른 프로세스에게 독점적으로 위임하는 메커니즘입니다. VR 헤드셋, 멀티시트 디스플레이, 게임 전용 출력 등에서 사용됩니다.

DRM Master (Wayland Compositor) CRTC 0,1 / Conn 0,1 Plane 0,1,2,3 Lease #1 Lease #2 Lessee #1 (VR) CRTC 1 + Conn 1 (HMD) 독립적인 DRM Master 권한 Lessee #2 CRTC 0 + Conn 0 별도 디스플레이 관리 VR 헤드셋 메인 모니터
ioctl설명호출자
DRM_IOCTL_MODE_CREATE_LEASE KMS 오브젝트 집합을 리스로 생성, lessee fd 반환 DRM Master (Lessor)
DRM_IOCTL_MODE_LIST_LESSEES 현재 활성 lessee 목록 조회 DRM Master
DRM_IOCTL_MODE_GET_LEASE 리스에 포함된 오브젝트 ID 목록 조회 Lessee
DRM_IOCTL_MODE_REVOKE_LEASE 리스 취소 (오브젝트 회수) DRM Master
/* DRM Lease 생성 (유저 공간, libdrm) */
uint32_t objects[] = {
    crtc_id,       /* 리스할 CRTC */
    connector_id,  /* 리스할 Connector */
    plane_id,      /* 리스할 Plane */
};

struct drm_mode_create_lease lease = {
    .object_ids   = (uint64_t)(uintptr_t)objects,
    .object_count = 3,
    .flags        = O_CLOEXEC | O_NONBLOCK,
};

drmIoctl(master_fd, DRM_IOCTL_MODE_CREATE_LEASE, &lease);
/* lease.lessee_id = 리스 ID */
/* lease.fd = lessee가 사용할 새 DRM fd */

/* Lessee는 lease.fd를 통해 리스된 오브젝트만 접근 가능 */
/* → 독립적인 atomic commit, 페이지 플립 수행 */
VR 활용: OpenXR 런타임(Monado 등)이 compositor로부터 VR 헤드셋 출력을 lease로 받아 직접 제어합니다. 이렇게 하면 compositor를 거치지 않고 HMD에 직접 프레임을 전달하여 latency를 최소화할 수 있습니다. Wayland에서는 wp_drm_lease_device_v1 프로토콜로 compositor가 lease를 제공합니다.

Content Protection (HDCP)

HDCP(High-bandwidth Digital Content Protection)는 디스플레이 출력의 콘텐츠를 암호화하여 무단 복제를 방지하는 DRM(Digital Rights Management) 기술입니다. Linux DRM 서브시스템은 KMS property를 통해 HDCP를 지원합니다.

HDCP 버전대역폭사용 인터페이스특징
HDCP 1.4 TMDS (HDMI 1.x/DP) HDMI, DP, DVI 기본 암호화, 리피터 인증
HDCP 2.2 HDMI 2.0+ HDMI 2.x 향상된 암호화(AES-128), LC 검증
HDCP 2.3 HDMI 2.1/DP 2.x HDMI 2.1, DP 2.x 최신 보안 개선
/* HDCP KMS Properties (Connector) */

/* "Content Protection" property */
/* 값: Undesired (0) → Desired (1) → Enabled (2) */
/*     유저가 Desired로 설정하면 드라이버가 HDCP 인증 시작 */
/*     성공 시 Enabled로 전환 (읽기 전용) */

/* "HDCP Content Type" property */
/* 값: Type 0 — HDCP 1.4+ 허용 */
/*     Type 1 — HDCP 2.2+ 필수 (4K UHD 콘텐츠) */

/* Connector에 HDCP property 등록 (드라이버) */
drm_connector_attach_content_protection_property(connector, true);
/* true = HDCP Content Type property도 함께 등록 */

/* HDCP 인증 흐름 (드라이버 내부) */
/* 1. 유저가 "Content Protection" = Desired 설정 */
/* 2. atomic_check에서 HDCP 요청 감지 */
/* 3. 드라이버가 수신기(모니터)와 HDCP 핸드셰이크 */
/*    - AKE_Init / AKE_Send_Cert / AKE_Send_Km ... */
/* 4. 인증 성공 → "Content Protection" = Enabled */
/* 5. 링크 실패/핫플러그 → "Content Protection" = Desired */
/*    → 유저 공간에 uevent 통보 → 재인증 시도 */
HDCP 요구사항: HDCP는 하드웨어 지원이 필수입니다. GPU의 디스플레이 엔진이 HDCP 암호화를 지원해야 하며, 모니터/TV도 해당 HDCP 버전을 지원해야 합니다. Intel GPU(i915)와 AMD GPU(amdgpu DC)가 Linux에서 HDCP를 지원하는 주요 드라이버입니다. HDCP 키는 GPU 펌웨어 또는 NVRAM에 저장되며, 커널은 키 자체를 직접 관리하지 않습니다.

디스플레이 연결 프로토콜

프로토콜DRM Connector 타입최대 대역폭특징
HDMI 2.1 DRM_MODE_CONNECTOR_HDMIA/B 48 Gbps ARC/eARC, VRR, DSC, 4K@120Hz
DisplayPort 2.1 DRM_MODE_CONNECTOR_DisplayPort 80 Gbps (UHBR20) MST (데이지체인), DSC, Adaptive-Sync
eDP DRM_MODE_CONNECTOR_eDP DP 기반 내장 디스플레이 (노트북), PSR (Panel Self Refresh)
DSI (MIPI) DRM_MODE_CONNECTOR_DSI 레인당 ~4.5 Gbps 모바일/임베디드 디스플레이, 커맨드/비디오 모드
DPI (RGB) DRM_MODE_CONNECTOR_DPI 병렬 전송 패럴렐 RGB, 임베디드 LCD 직결
LVDS DRM_MODE_CONNECTOR_LVDS 채널당 ~112 MHz 레거시 노트북/산업용 패널
VGA DRM_MODE_CONNECTOR_VGA ~400 MHz (아날로그) 레거시 아날로그, DAC 필요
USB-C (DP Alt) DRM_MODE_CONNECTOR_USB DP 기반 USB Type-C를 통한 DP 출력, typec_mux 드라이버

fbdev 에뮬레이션

DRM/KMS는 /dev/fb0 (fbdev) 인터페이스를 에뮬레이션하여 레거시 애플리케이션과 콘솔 출력을 지원합니다. 커널 콘솔(fbcon), Plymouth(부팅 스플래시), 일부 임베디드 UI 프레임워크가 fbdev에 의존합니다.

/* fbdev 에뮬레이션 구조 */

/*
 * ┌─────────────────────────────────┐
 * │  User Space (/dev/fb0)          │
 * │  - fbcon (커널 콘솔)             │
 * │  - mmap()으로 프레임버퍼 직접 접근  │
 * └──────────┬──────────────────────┘
 *            │ fb_ops
 * ┌──────────▼──────────────────────┐
 * │  DRM fbdev 에뮬레이션 레이어     │
 * │  drm_fbdev_shmem / drm_fbdev_ttm│
 * │  - GEM 객체를 fb_info에 매핑     │
 * │  - fb_dirty()로 변경 영역 통보   │
 * └──────────┬──────────────────────┘
 *            │ KMS atomic commit
 * ┌──────────▼──────────────────────┐
 * │  DRM/KMS (CRTC + Plane + ...)   │
 * └─────────────────────────────────┘
 */

/* 드라이버에서 fbdev 에뮬레이션 활성화 */
#include <drm/drm_fbdev_shmem.h>

static int my_probe(struct platform_device *pdev)
{
    /* ... DRM 초기화 ... */

    /* fbdev 에뮬레이션 설정 (32 = bpp) */
    drm_fbdev_shmem_setup(drm, 32);
    /* → /dev/fb0 자동 생성 */
    /* → fbcon 자동 연결 (CONFIG_FRAMEBUFFER_CONSOLE) */
}
fbdev 헬퍼메모리 관리사용 드라이버
drm_fbdev_shmem GEM shmem 기반 panfrost, lima, vc4, simpledrm, virtio-gpu
drm_fbdev_ttm TTM 기반 (VRAM) amdgpu, nouveau, radeon
drm_fbdev_dma DMA/CMA 기반 meson, sun4i, rockchip
드라이버 자체 구현 드라이버별 i915 (커스텀 fbdev)
fbcon과 DRM: CONFIG_FRAMEBUFFER_CONSOLE=y 설정 시, DRM 드라이버가 로드되면 자동으로 fbcon이 해당 fbdev에 연결됩니다. 이를 통해 커널 패닉 메시지, systemd 부팅 로그 등이 물리 디스플레이에 출력됩니다. fbcon=map:0 커널 파라미터로 fbcon이 사용할 fb 디바이스를 지정할 수 있습니다.

GPU 컴퓨트 (GPGPU)

GPU는 렌더링 외에도 대규모 병렬 연산(GPGPU)에 사용됩니다. Linux 커널은 render node(/dev/dri/renderD128)를 통해 비특권 GPU 컴퓨트 접근을 제공하며, AMD는 KFD(Kernel Fusion Driver)로 HSA 컴퓨트를 추가 지원합니다.

프레임워크커널 인터페이스유저 공간GPU
OpenCL (Mesa) DRM render node Mesa Clover/Rusticl AMD, Intel, 일부 ARM
ROCm (AMD) KFD (/dev/kfd) ROCm runtime, HIP AMD GCN/RDNA/CDNA
oneAPI (Intel) DRM render node (xe/i915) Level Zero, SYCL Intel Xe/Arc
Vulkan Compute DRM render node Vulkan compute shader 모든 Vulkan 지원 GPU
CUDA (NVIDIA) nvidia.ko (독점) CUDA runtime NVIDIA (nouveau 미지원)

KFD (Kernel Fusion Driver) — AMD HSA

/* KFD 아키텍처 (drivers/gpu/drm/amd/amdkfd/) */

/*
 * /dev/kfd — AMD GPU 컴퓨트 전용 디바이스 노드
 *
 * ┌────────────────────────┐
 * │  ROCm Runtime / HIP    │  User Space
 * │  (libhsakmt, HSA RT)   │
 * └──────────┬─────────────┘
 *            │ KFD ioctl
 * ┌──────────▼─────────────┐
 * │  KFD (amdkfd)          │  Kernel
 * │  - 프로세스별 GPU 큐 관리 │
 * │  - SVM (Shared Virtual  │
 * │    Memory) 관리          │
 * │  - GPU 이벤트/시그널     │
 * └──────────┬─────────────┘
 *            │
 * ┌──────────▼─────────────┐
 * │  amdgpu DRM 드라이버    │
 * │  (하드웨어 접근)         │
 * └────────────────────────┘
 */

/* KFD 주요 ioctl */
KFD_IOC_CREATE_QUEUE       /* GPU 컴퓨트 큐 생성 (AQL 형식) */
KFD_IOC_DESTROY_QUEUE      /* 큐 삭제 */
KFD_IOC_ALLOC_MEMORY_OF_GPU /* GPU 메모리 할당 (VRAM/GTT/System) */
KFD_IOC_MAP_MEMORY_TO_GPU   /* GPU 페이지 테이블에 메모리 매핑 */
KFD_IOC_CREATE_EVENT       /* GPU 이벤트 생성 (완료 통보) */
KFD_IOC_WAIT_EVENTS        /* GPU 이벤트 대기 */
KFD_IOC_SVM                /* SVM 범위 등록/속성 설정 */

SVM (Shared Virtual Memory)

SVM은 CPU와 GPU가 동일한 가상 주소 공간을 공유하는 기술입니다. malloc()으로 할당한 메모리를 GPU가 동일한 포인터로 접근할 수 있습니다.

/* SVM 개념 */
/*
 * CPU Process VA Space
 * ┌──────────────────────────┐
 * │ 0x7fff_xxxx: Stack       │
 * │ ...                      │
 * │ 0x0040_0000: Code        │
 * │ 0x1000_0000: malloc buf ←┼── CPU가 이 주소로 접근
 * └──────────────────────────┘
 *                               │
 *                               ▼ 동일한 가상 주소
 * GPU VA Space                  │
 * ┌──────────────────────────┐  │
 * │ 0x1000_0000: malloc buf ←┼──┘ GPU도 이 주소로 접근
 * └──────────────────────────┘
 *
 * 구현:
 * - GPU 페이지 테이블을 CPU 페이지 테이블과 동기화
 * - GPU 페이지 폴트 발생 시 on-demand 페이징
 * - HMM (Heterogeneous Memory Management) 프레임워크 활용
 * - MMU notifier로 CPU 페이지 테이블 변경 추적
 */

/* 커널 SVM 지원 (amdgpu/KFD) */
#include <linux/hmm.h>

/* GPU 페이지 폴트 핸들러 */
/* 1. GPU가 매핑되지 않은 주소 접근 → 인터럽트 */
/* 2. 커널이 CPU 페이지 테이블에서 물리 주소 조회 */
/*    (페이지 없으면 CPU 페이지 폴트도 처리) */
/* 3. GPU 페이지 테이블에 매핑 추가 */
/* 4. GPU 작업 재개 */
Render Node 보안: /dev/dri/renderD128은 DRM Master 없이 접근 가능하므로, 일반 사용자도 GPU 컴퓨트를 사용할 수 있습니다. 그러나 GPU 가상 메모리 격리가 제대로 구현되어야 다른 프로세스의 GPU 데이터가 유출되지 않습니다. per-process GPU page tablecommand validation이 보안의 핵심입니다.
# GPU 컴퓨트 관련 확인 명령

# Render node 확인
ls -la /dev/dri/renderD*

# KFD 디바이스 확인 (AMD)
ls -la /dev/kfd

# GPU 토폴로지 (AMD ROCm)
cat /sys/class/kfd/kfd/topology/nodes/0/properties

# GPU 메모리 사용량 (amdgpu)
cat /sys/class/drm/card0/device/mem_info_vram_used
cat /sys/class/drm/card0/device/mem_info_gtt_used

# clinfo (OpenCL 디바이스 정보)
clinfo

# vulkaninfo (Vulkan 컴퓨트 능력)
vulkaninfo --summary

NPU (Neural Processing Unit) 개요

NPU(Neural Processing Unit)는 딥러닝 추론/학습 워크로드에 최적화된 전용 하드웨어 가속기입니다. Linux 커널 6.2부터 DRM Accel(drivers/accel/) 서브시스템이 도입되어, GPU와 분리된 전용 디바이스 노드(/dev/accel/accel0)를 통해 NPU 및 AI 가속기를 관리합니다.

DRM Accel vs DRM GPU: DRM Accel은 DRM 인프라(GEM, DMA-BUF, drm_sched 등)를 재사용하지만, 렌더 노드(/dev/dri/renderD*)와 별도의 /dev/accel/accel* 디바이스 노드를 사용합니다. 이를 통해 GPU용 유저 공간 도구(Mesa, Vulkan)와 NPU용 ML 프레임워크가 충돌 없이 공존할 수 있습니다.

NPU vs GPU vs CPU 연산 특성

특성CPUGPUNPU
아키텍처범용 파이프라인, 소수의 고성능 코어대량의 SIMD/SIMT 코어MAC 어레이 + 전용 데이터 흐름 엔진
연산 강점분기 예측, 직렬 연산대규모 병렬 부동소수점INT8/FP16 행렬 곱셈 (GEMM)
전력 효율낮음 (범용)중간 (SIMD 활용)높음 (고정 함수 + 저정밀도)
메모리 접근캐시 계층 (L1/L2/L3)HBM/GDDR + 텍스처 캐시SRAM 스크래치패드 + DMA
프로그래밍 모델일반 코드CUDA/OpenCL/Vulkan Compute컴파일러가 생성한 명령 스트림
대표 TOPS/W~0.1~1–5~10–50+

NPU 하드웨어 아키텍처 개념

NPU의 핵심은 Systolic Array(맥동 배열)를 기반으로 한 행렬 연산 유닛과 전용 데이터 흐름 제어기입니다. 대부분의 NPU는 다음과 같은 공통 구조를 가집니다.

Host Interface PCIe / AXI Mailbox Command Queue IRQ Controller FW Loader MMU/IOMMU Compute Cluster (NCE / Tile) MAC Array (Systolic Array) INT8/FP16 GEMM Activation ReLU/Sigmoid/ SoftMax/Pool SRAM Scratchpad Memory DMA Engine Tile ↔ DDR/HBM Firmware / Microcontroller Task Scheduler Layer Dispatch Power Manager DVFS / Clock Gate External Memory DDR / LPDDR / HBM 모델 가중치 + 입출력 텐서 DMA-BUF 공유 가능 User Space Stack ML Framework (TFLite/ONNX) UMD (User Mode Driver) libdrm / ioctl /dev/accel/accel0

NPU 데이터 흐름

NPU에서 추론을 수행하는 전체 데이터 흐름은 다음과 같습니다.

  1. 모델 컴파일: ONNX/TFLite 모델을 NPU 전용 바이너리(blob)로 오프라인 컴파일. 레이어별 타일링, 양자화, 메모리 할당 계획이 포함됨
  2. 버퍼 할당: GEM BO(Buffer Object)를 통해 입력/출력/가중치 버퍼를 커널 드라이버에 할당 요청
  3. 커맨드 버퍼 생성: UMD가 DMA 전송, 연산 커맨드 등을 커맨드 버퍼에 기록
  4. 잡 제출: ioctl을 통해 커맨드 버퍼를 커널에 제출 → drm_sched가 스케줄링
  5. HW 실행: 펌웨어가 레이어 단위로 Compute Cluster에 디스패치, SRAM ↔ DDR DMA 전송 관리
  6. 완료 통보: IRQ → dma_fence 시그널 → 유저 공간에 결과 전달

양자화(Quantization)와 데이터 타입

NPU는 전력 효율을 위해 낮은 정밀도의 데이터 타입을 사용합니다. MAC 어레이의 데이터 타입에 따라 연산 처리량(TOPS)이 크게 달라집니다.

데이터 타입비트 폭범위/정밀도용도상대 처리량
FP3232-bitIEEE 754 단정밀도학습 기준선1× (기준)
FP1616-bit±65504, 정밀도 ~3.3자리학습/추론 혼합
BF1616-bitFP32 동적 범위, 낮은 정밀도학습 (기울기 안정성)
INT88-bit-128~127 / 0~255추론 최적화 (PTQ/QAT)
INT44-bit-8~7 / 0~15LLM 가중치 압축
FP8 (E4M3/E5M2)8-bitE4M3: ±448 / E5M2: ±57344차세대 학습/추론
양자화 기법:
  • PTQ (Post-Training Quantization): 학습 완료 후 가중치/활성화를 INT8로 변환. 캘리브레이션 데이터셋으로 스케일 팩터 결정
  • QAT (Quantization-Aware Training): 학습 중에 양자화 시뮬레이션. PTQ보다 정확도 손실이 적음
  • Mixed Precision: 레이어별로 FP16/INT8을 혼합. 정확도에 민감한 레이어는 FP16, 나머지는 INT8 사용

DRM Accel 서브시스템

DRM Accel은 Linux 6.2에서 도입된 NPU/AI 가속기 전용 프레임워크입니다. DRM의 인프라를 재사용하면서도 GPU와 별개의 디바이스 노드 네임스페이스를 제공합니다.

디바이스 노드 구조

# GPU 디바이스 노드 (DRM)
/dev/dri/card0          # DRM master (KMS + 렌더)
/dev/dri/renderD128     # render-only (non-root GPU 접근)

# NPU/Accel 디바이스 노드 (DRM Accel)
/dev/accel/accel0       # AI 가속기 전용 노드
/dev/accel/accel1       # 두 번째 가속기 (있는 경우)

# sysfs 계층
/sys/class/accel/accel0/
    ├── device           # → PCI/platform 디바이스 심링크
    ├── dev              # major:minor 번호
    └── power/           # 전원 관리 속성

Accel 드라이버 등록

Accel 드라이버는 drm_dev_alloc() 대신 accel_minor_alloc()을 내부적으로 사용합니다. 드라이버는 drm_driverDRIVER_COMPUTE_ACCEL 플래그를 설정하여 Accel 모드를 활성화합니다.

#include <drm/drm_accel.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem.h>
#include <drm/drm_ioctl.h>

/* Accel 드라이버 플래그: DRIVER_COMPUTE_ACCEL을 설정하면
 * /dev/accel/accelN 노드가 생성됨 (GPU용 /dev/dri/* 대신)
 * 참고: DRIVER_RENDER와 동시에 사용 불가 */
static const struct drm_driver my_npu_driver = {
    .driver_features    = DRIVER_GEM | DRIVER_COMPUTE_ACCEL,
    .name               = "my_npu",
    .desc               = "My NPU Accelerator",
    .date               = "20250101",
    .major              = 1,
    .minor              = 0,

    /* GEM 오퍼레이션 */
    .gem_create_object  = my_npu_gem_create_object,

    /* ioctl 테이블 */
    .ioctls             = my_npu_ioctls,
    .num_ioctls         = ARRAY_SIZE(my_npu_ioctls),

    /* 파일 오퍼레이션 */
    .fops               = &my_npu_fops,
};

/* probe에서 등록 */
static int my_npu_probe(struct pci_dev *pdev,
                        const struct pci_device_id *ent)
{
    struct drm_device *drm;
    struct my_npu_device *ndev;
    int ret;

    ndev = devm_drm_dev_alloc(&pdev->dev, &my_npu_driver,
                              struct my_npu_device, drm);
    if (IS_ERR(ndev))
        return PTR_ERR(ndev);

    drm = &ndev->drm;
    pci_set_drvdata(pdev, ndev);

    /* HW 초기화: MMIO 매핑, IRQ 등록, 펌웨어 로딩 */
    ret = my_npu_hw_init(ndev);
    if (ret)
        return ret;

    /* drm_dev_register()가 내부적으로 accel_minor를 할당
     * → /dev/accel/accelN 생성 */
    ret = drm_dev_register(drm, 0);
    if (ret)
        goto err_hw;

    dev_info(&pdev->dev, "NPU registered as /dev/accel/accel%d\n",
             drm->accel->index);
    return 0;

err_hw:
    my_npu_hw_fini(ndev);
    return ret;
}

GEM Buffer Object 관리

NPU 드라이버는 GEM을 사용해 가중치, 텐서, 커맨드 버퍼 등의 메모리를 관리합니다. 일반적으로 drm_gem_shmem_object 헬퍼를 확장하거나, 독자적인 GEM 오브젝트를 구현합니다.

/* NPU 전용 GEM 버퍼 오브젝트 */
struct my_npu_bo {
    struct drm_gem_shmem_object base;
    u64  npu_addr;             /* NPU 가상 주소 (디바이스 MMU 매핑) */

    enum {
        NPU_BO_INPUT     = 0,   /* 입력 텐서 */
        NPU_BO_OUTPUT    = 1,   /* 출력 텐서 */
        NPU_BO_WEIGHTS   = 2,   /* 모델 가중치 (읽기 전용) */
        NPU_BO_CMDQ      = 3,   /* 커맨드 큐 */
    } usage;

    bool shared;               /* DMA-BUF로 다른 디바이스와 공유 가능 */
};

/* GEM 생성 ioctl 핸들러 */
static int my_npu_gem_create_ioctl(struct drm_device *dev,
                                    void *data,
                                    struct drm_file *file)
{
    struct my_npu_create_bo *args = data;
    struct my_npu_bo *bo;
    int ret;

    args->size = ALIGN(args->size, SZ_4K);

    bo = my_npu_bo_alloc(dev, args->size, args->flags);
    if (IS_ERR(bo))
        return PTR_ERR(bo);

    /* NPU MMU에 매핑 → npu_addr 할당 */
    ret = my_npu_mmu_map(bo);
    if (ret)
        goto err_free;

    ret = drm_gem_handle_create(file, &bo->base.base, &args->handle);
    drm_gem_object_put(&bo->base.base);
    args->npu_addr = bo->npu_addr;
    return ret;

err_free:
    drm_gem_object_put(&bo->base.base);
    return ret;
}

컴퓨트 잡 제출 (Job Submission)

NPU 드라이버는 drm_sched(GPU 스케줄러)를 재사용하여 컴퓨트 잡을 관리합니다.

/* NPU 잡 구조체 — drm_sched_job을 임베드 */
struct my_npu_job {
    struct drm_sched_job  base;
    struct my_npu_bo     *cmd_bo;      /* 커맨드 버퍼 */
    struct my_npu_bo    **bos;         /* 입출력 버퍼 참조 */
    u32                  bo_count;
    struct dma_fence     *done_fence;  /* 완료 fence */
};

/* drm_sched 백엔드 콜백 */
static struct dma_fence *
my_npu_run_job(struct drm_sched_job *sched_job)
{
    struct my_npu_job *job =
        container_of(sched_job, struct my_npu_job, base);
    struct my_npu_device *ndev = job_to_ndev(job);

    /* 1. 커맨드 버퍼의 NPU 가상 주소를 HW 레지스터에 기록 */
    writel(lower_32_bits(job->cmd_bo->npu_addr),
           ndev->mmio + NPU_REG_CMD_ADDR_LO);
    writel(upper_32_bits(job->cmd_bo->npu_addr),
           ndev->mmio + NPU_REG_CMD_ADDR_HI);

    /* 2. 실행 트리거 — 펌웨어가 처리 시작 */
    writel(NPU_CMD_EXECUTE, ndev->mmio + NPU_REG_DOORBELL);

    /* 3. HW fence 반환 — IRQ 핸들러에서 시그널됨 */
    return dma_fence_get(job->done_fence);
}

static enum drm_gpu_sched_stat
my_npu_timedout_job(struct drm_sched_job *sched_job)
{
    struct my_npu_job *job =
        container_of(sched_job, struct my_npu_job, base);

    dev_err(job->dev, "NPU job timeout — resetting engine\n");
    my_npu_engine_reset(job_to_ndev(job));

    return DRM_GPU_SCHED_STAT_NOMINAL;
}

static const struct drm_sched_backend_ops my_npu_sched_ops = {
    .run_job      = my_npu_run_job,
    .timedout_job = my_npu_timedout_job,
};

펌웨어 로딩

대부분의 NPU는 부팅 시 펌웨어를 로딩해야 합니다. 커널의 request_firmware() API를 사용하여 /lib/firmware/에서 NPU 펌웨어 바이너리를 로드합니다.

static int my_npu_load_firmware(struct my_npu_device *ndev)
{
    const struct firmware *fw;
    int ret;

    ret = request_firmware(&fw, "my_npu/fw.bin", ndev->dev);
    if (ret) {
        dev_err(ndev->dev, "firmware load failed: %d\n", ret);
        return ret;
    }

    /* 펌웨어를 NPU의 내부 SRAM 또는 전용 메모리 영역에 복사 */
    memcpy_toio(ndev->fw_region, fw->data, fw->size);

    /* 부트 레지스터 — 펌웨어 실행 시작 */
    writel(NPU_FW_BOOT, ndev->mmio + NPU_REG_FW_CTRL);

    /* 부팅 완료 대기 (mailbox 통신) */
    ret = my_npu_wait_fw_ready(ndev, 5000);
    release_firmware(fw);
    return ret;
}

NPU MMU (디바이스 가상 메모리)

NPU는 자체 MMU(또는 IOMMU 연동)를 통해 디바이스 가상 주소 공간을 관리합니다. 이를 통해 연속적이지 않은 물리 페이지를 NPU에서 연속 주소로 접근할 수 있습니다.

struct my_npu_mmu {
    struct my_npu_device  *ndev;
    dma_addr_t            pgd_dma;    /* L1 페이지 디렉토리 */
    u64                  *pgd_cpu;    /* CPU 매핑 */
    struct drm_mm         va_space;   /* VA 할당 관리 */
    struct mutex          lock;
};

static int my_npu_mmu_map(struct my_npu_bo *bo)
{
    struct my_npu_mmu *mmu = &bo_to_ndev(bo)->mmu;
    struct drm_mm_node *node = &bo->mm_node;
    struct sg_table *sgt;
    int ret;

    mutex_lock(&mmu->lock);

    /* VA 공간에서 영역 할당 */
    ret = drm_mm_insert_node_generic(&mmu->va_space, node,
                                      bo->base.base.size,
                                      SZ_4K, 0, DRM_MM_INSERT_BEST);
    if (ret)
        goto out;

    bo->npu_addr = node->start;

    sgt = drm_gem_shmem_get_pages_sgt(&bo->base);
    if (IS_ERR(sgt)) {
        drm_mm_remove_node(node);
        ret = PTR_ERR(sgt);
        goto out;
    }

    my_npu_mmu_write_ptes(mmu, bo->npu_addr, sgt);
    my_npu_mmu_flush_tlb(mmu);

out:
    mutex_unlock(&mmu->lock);
    return ret;
}
NPU MMU와 시스템 IOMMU: NPU 자체 MMU와 시스템 IOMMU(예: ARM SMMU, Intel VT-d)는 별개입니다. 시스템 IOMMU는 NPU 디바이스의 DMA 접근을 물리 메모리 보호 관점에서 제한하고, NPU MMU는 디바이스 내부의 가상 주소 변환을 담당합니다. 두 단계 변환이 모두 적용됩니다.

주요 Linux NPU 드라이버

드라이버하드웨어커널 경로디바이스 노드도입 버전
intel_vpu (IVPU) Intel Meteor Lake / Arrow Lake / Lunar Lake NPU drivers/accel/ivpu/ /dev/accel/accel0 6.3
habanalabs Intel Gaudi / Gaudi2 / Gaudi3 (데이터센터 AI) drivers/accel/habanalabs/ /dev/accel/accel0 6.2 (이전)
amdxdna AMD Ryzen AI (XDNA / XDNA2 NPU) drivers/accel/amdxdna/ /dev/accel/accel0 6.11
qaic Qualcomm Cloud AI 100 drivers/accel/qaic/ /dev/accel/accel0 6.4
etnaviv (NPU 확장) Vivante VIP (i.MX 8M Plus 등) drivers/gpu/drm/etnaviv/ /dev/dri/renderD* 4.5+
samsung_npu Samsung Exynos NPU (모바일) Out-of-tree (vendor kernel) /dev/vertex*
mtk_apu MediaTek APU (Dimensity SoC) Out-of-tree (vendor kernel) /dev/apusys*

Intel NPU (IVPU) 드라이버 상세

Intel NPU(이전 명칭 VPU)는 Meteor Lake부터 Intel 클라이언트 프로세서에 통합된 AI 추론 가속기입니다. drivers/accel/ivpu/에 위치하며, DRM Accel 프레임워크를 사용하는 대표적인 드라이버입니다.

drivers/accel/ivpu/ 구조:
├── ivpu_drv.c          # 드라이버 진입점, probe/remove
├── ivpu_drv.h          # 디바이스 구조체 정의
├── ivpu_hw.c           # HW 추상화 (IP 버전별 분기)
├── ivpu_ipc.c          # 펌웨어 IPC (mailbox 프로토콜)
├── ivpu_job.c          # 잡 제출/완료 (drm_sched 사용)
├── ivpu_gem.c          # GEM BO 관리 (shmem 기반)
├── ivpu_mmu.c          # 디바이스 MMU (3-level 페이지 테이블)
├── ivpu_mmu_context.c  # MMU 컨텍스트 (프로세스별 주소 공간)
├── ivpu_fw.c           # 펌웨어 로딩/부팅
├── ivpu_pm.c           # 전원 관리 (Runtime PM)
├── ivpu_jsm_msg.c      # JSM (Job Submission Message) 프로토콜
└── ivpu_debugfs.c      # 디버그 인터페이스
Intel NPU 세대별 특성:
  • Meteor Lake (NPU 3720): 첫 통합 NPU, 최대 ~10 TOPS (INT8)
  • Arrow Lake (NPU 3720): 아키텍처 유사, 소프트웨어 최적화
  • Lunar Lake (NPU 4): 최대 ~48 TOPS, MCDM 아키텍처 대폭 개선

Intel NPU ioctl 인터페이스

ioctl방향설명
DRM_IOCTL_IVPU_GET_PARAMRHW 파라미터 조회 (플랫폼 타입, TOPS, 타일 수 등)
DRM_IOCTL_IVPU_SET_PARAMW런타임 파라미터 설정
DRM_IOCTL_IVPU_BO_CREATERWGEM BO 생성 (크기, 플래그 → 핸들, NPU VA)
DRM_IOCTL_IVPU_BO_INFORWBO 정보 조회 (mmap offset, VA, 크기)
DRM_IOCTL_IVPU_SUBMITW컴퓨트 잡 제출 (커맨드 버퍼 + BO 리스트)
DRM_IOCTL_IVPU_BO_WAITRWBO 사용 완료 대기 (타임아웃 지정)
DRM_IOCTL_IVPU_METRIC_STREAMER_*RW성능 메트릭 수집 (대역폭, 활용률)

AMD XDNA (Ryzen AI) 드라이버

AMD Ryzen AI 프로세서(Phoenix, Hawk Point, Strix Point)에 통합된 XDNA NPU입니다. FPGA 기반 AI Engine 타일로 구성되며, 각 타일은 VLIW 프로세서 + 벡터 유닛을 포함합니다.

drivers/accel/amdxdna/ 구조:
├── aie2_ctx.c          # AIE2 (AI Engine 2) 컨텍스트 관리
├── aie2_message.c      # 펌웨어 메시지 (ERT 프로토콜)
├── aie2_pci.c          # PCIe 드라이버 probe/remove
├── aie2_pm.c           # 전원 관리, DPM 클럭 제어
├── aie2_solver.c       # 하드웨어 리소스 할당 (파티션/컬럼)
├── amdxdna_ctx.c       # DRM 컨텍스트 (클라이언트별 상태)
├── amdxdna_gem.c       # GEM BO 관리
├── amdxdna_mailbox.c   # 호스트↔펌웨어 통신
└── amdxdna_sysfs.c     # sysfs 디버그 속성
AMD XDNA 아키텍처 특징:
  • AI Engine 타일: 각 타일에 VLIW 스칼라 프로세서 + 512-bit 벡터 유닛 + 로컬 데이터 메모리(DM)
  • 컬럼 기반 할당: 복수의 타일을 컬럼 단위로 묶어 워크로드에 할당 (리소스 파티셔닝)
  • 데이터 흐름: 타일 간 스트림 인터커넥트로 파이프라인 구성 (ISA는 Xilinx Versal에서 유래)
  • XDNA2 (Strix Point): Block FP (BFLOAT16 블록), 최대 ~50 TOPS

Intel Gaudi (habanalabs) 드라이버

데이터센터급 AI 학습/추론 가속기인 Intel Gaudi 시리즈를 지원합니다. DRM Accel 프레임워크 도입 이전부터 존재하며, 6.2에서 drivers/accel/로 이동했습니다.

특성GaudiGaudi2Gaudi3
TPC (Tensor Processing Core)8개24개64개
MME (Matrix Math Engine)2개2개8개
HBM32GB HBM296GB HBM2E128GB HBM2E
네트워크10× 100GbE RoCE24× 100GbE RoCE24× 200GbE RoCE
BF16 성능~370 TFLOPS~865 TFLOPS~1835 TFLOPS
FP8 성능~1730 TFLOPS~3670 TFLOPS
/* habanalabs 드라이버 핵심 구조 (간략화) */
struct hl_device {
    struct drm_device      drm;
    const struct hl_asic_funcs *asic_funcs;  /* ASIC별 함수 포인터 */
    struct hl_cs_counters_atomic  cs_cnt;    /* 커맨드 서브미션 */
    struct hl_vm            vm;              /* 메모리 관리 */
    struct hl_nic          *nic;             /* 멀티 디바이스 통신 */
    struct hwmon_ops       *hwmon;           /* 전원/열 관리 */
};

/* hl_asic_funcs: ASIC 추상화 인터페이스 */
struct hl_asic_funcs {
    int  (*hw_init)(struct hl_device *hdev);
    void (*hw_fini)(struct hl_device *hdev, bool hard);
    int  (*suspend)(struct hl_device *hdev);
    int  (*resume)(struct hl_device *hdev);
    int  (*cb_mmap)(struct hl_device *hdev,
                    struct vm_area_struct *vma);
    void (*ring_doorbell)(struct hl_device *hdev,
                          u32 hw_queue_id, u32 pi);
    /* ... 50+ 함수 포인터 ... */
};

NPU 전원 관리

NPU는 항상 활성 상태일 필요가 없으므로, Runtime PM을 통한 공격적인 전원 관리가 중요합니다. 특히 노트북/모바일 SoC에서는 추론이 없을 때 NPU를 완전히 꺼서 전력을 절약합니다.

static int my_npu_runtime_suspend(struct device *dev)
{
    struct my_npu_device *ndev = dev_get_drvdata(dev);

    my_npu_wait_idle(ndev);               /* 잡 완료 대기 */
    my_npu_mmu_save_state(ndev);           /* MMU 컨텍스트 저장 */
    clk_disable_unprepare(ndev->clk);     /* 클럭 게이팅 */
    my_npu_power_off(ndev);                /* 전원 도메인 OFF */
    return 0;
}

static int my_npu_runtime_resume(struct device *dev)
{
    struct my_npu_device *ndev = dev_get_drvdata(dev);
    int ret;

    my_npu_power_on(ndev);                 /* 전원 ON */
    clk_prepare_enable(ndev->clk);         /* 클럭 활성화 */

    ret = my_npu_load_firmware(ndev);       /* FW 재로딩 */
    if (ret)
        return ret;

    my_npu_mmu_restore_state(ndev);        /* MMU 복원 */
    return 0;
}

static const struct dev_pm_ops my_npu_pm_ops = {
    SET_RUNTIME_PM_OPS(my_npu_runtime_suspend,
                       my_npu_runtime_resume, NULL)
    SET_SYSTEM_SLEEP_PM_OPS(my_npu_system_suspend,
                            my_npu_system_resume)
};
DVFS (Dynamic Voltage and Frequency Scaling): NPU 워크로드 강도에 따라 동적으로 클럭 주파수와 전압을 조절합니다. 가벼운 추론(이미지 분류)은 낮은 OPP(Operating Performance Point)로, 대규모 LLM 추론은 최고 OPP로 실행하여 전력 효율을 극대화합니다.

NPU와 DMA-BUF: 디바이스 간 버퍼 공유

DMA-BUF를 통해 NPU, GPU, 카메라(ISP), 비디오 디코더 간에 제로카피 버퍼 공유가 가능합니다. 이는 카메라 → NPU → GPU → 디스플레이 같은 ML 파이프라인에서 필수적입니다.

Camera ISP 프레임 캡처 DMA-BUF NPU 객체 검출 추론 DMA-BUF GPU 바운딩 박스 오버레이 DMA-BUF Display (KMS) 화면 출력 공유 물리 메모리 (DMA-BUF) CPU 복사 없이 디바이스 간 직접 접근 (제로카피)
/* NPU에서 DMA-BUF 익스포트 (exporter 역할) */
static struct dma_buf *
my_npu_gem_prime_export(struct drm_gem_object *obj, int flags)
{
    struct dma_buf_export_info exp_info = {
        .exp_name  = "my_npu",
        .owner     = obj->dev->driver->fops->owner,
        .ops       = &my_npu_dmabuf_ops,
        .size      = obj->size,
        .flags     = flags,
        .priv      = obj,
    };
    return drm_gem_dmabuf_export(obj->dev, &exp_info);
}

/* GPU에서 NPU의 DMA-BUF를 임포트 (importer 역할) */
struct drm_gem_object *
gpu_gem_prime_import(struct drm_device *dev,
                     struct dma_buf *dma_buf)
{
    struct dma_buf_attachment *attach;
    struct sg_table *sgt;

    attach = dma_buf_attach(dma_buf, dev->dev);
    if (IS_ERR(attach))
        return ERR_CAST(attach);

    sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
    if (IS_ERR(sgt)) {
        dma_buf_detach(dma_buf, attach);
        return ERR_CAST(sgt);
    }

    /* sg_table을 GPU 페이지 테이블에 매핑
     * → NPU가 쓴 결과를 GPU가 직접 읽기 가능 */
    return gpu_create_gem_from_sgt(dev, sgt, dma_buf->size);
}

NPU 커널 설정 (Kconfig)

# DRM Accel 기본 지원
CONFIG_DRM=m                       # DRM 코어 (필수)
CONFIG_DRM_ACCEL=y                 # Accel 서브시스템 활성화

# Intel NPU (IVPU)
CONFIG_DRM_ACCEL_IVPU=m            # Intel VPU/NPU 드라이버

# Intel Gaudi (habanalabs)
CONFIG_DRM_ACCEL_HABANALABS=m      # Gaudi/Gaudi2/Gaudi3

# AMD XDNA (Ryzen AI)
CONFIG_DRM_ACCEL_AMDXDNA=m         # AMD XDNA NPU

# Qualcomm Cloud AI
CONFIG_DRM_ACCEL_QAIC=m            # Qualcomm AIC100

# 공통 의존성
CONFIG_DRM_GEM_SHMEM_HELPER=m      # GEM shmem 헬퍼
CONFIG_DRM_SCHED=m                 # GPU/NPU 스케줄러
CONFIG_DMA_SHARED_BUFFER=y         # DMA-BUF (디바이스 간 공유)
CONFIG_FW_LOADER=y                 # 펌웨어 로더 (필수)
CONFIG_IOMMU_API=y                 # IOMMU 지원

NPU 디버깅

sysfs / debugfs 인터페이스

# Accel 디바이스 확인
ls -la /dev/accel/
ls -la /sys/class/accel/

# Intel NPU debugfs
cat /sys/kernel/debug/accel/0/info
cat /sys/kernel/debug/accel/0/fw_version
cat /sys/kernel/debug/accel/0/busy_time_us
cat /sys/kernel/debug/accel/0/freq

# Runtime PM 상태 확인
cat /sys/class/accel/accel0/device/power/runtime_status

# HW 센서 (hwmon)
cat /sys/class/accel/accel0/device/hwmon/*/temp1_input
cat /sys/class/accel/accel0/device/hwmon/*/power1_input

# AMD XDNA sysfs
cat /sys/class/accel/accel0/device/npu_clk_freq
cat /sys/class/accel/accel0/device/npu_power_state

NPU 디버깅 도구

도구용도사용법
dmesgNPU 드라이버 로그dmesg | grep -i "ivpu\|npu\|accel\|xdna"
lspci -vvvNPU PCI 디바이스 정보lspci -d ::0x1200 -vvv (Processing Accel class)
ftraceDRM/Accel 내부 추적echo drm:* > /sys/kernel/debug/tracing/set_event
perf드라이버 성능 분석perf record -a -g -e drm:* -- sleep 5
Intel NPU PluginOpenVINO 통합 프로파일링OpenVINO benchmark_app + NPU 플러그인
xrt-smiAMD XDNA 모니터링Xilinx Runtime CLI (xrt-smi examine)

NPU 유저 공간 소프트웨어 스택

NPU 드라이버는 커널에서 HW 접근과 메모리 관리만 담당하고, 실제 ML 모델 컴파일/실행은 유저 공간 스택이 처리합니다.

계층Intel NPUAMD XDNAQualcomm Cloud AI
ML 프레임워크 OpenVINO, ONNX Runtime ONNX Runtime, PyTorch Qualcomm AI Engine Direct
컴파일러 NPU Compiler (VPUX) Vitis AI / XDNA Compiler QNN SDK
UMD (런타임) intel-npu-acceleration-library XRT (Xilinx Runtime) libqaic
KMD (커널) ivpu (drivers/accel/ivpu/) amdxdna (drivers/accel/amdxdna/) qaic (drivers/accel/qaic/)
NPU 추론 실행 예시 (OpenVINO + Intel NPU):
# 1. 모델 벤치마크
benchmark_app -m model.onnx -d NPU -hint latency

# 2. Python API로 추론
import openvino as ov

core = ov.Core()
model = core.compile_model("model.onnx", "NPU")
result = model.infer_new_request({"input": input_data})

참고 사항

커널 소스 참고 경로:
  • drivers/gpu/drm/ — DRM 코어 + 모든 GPU 드라이버
  • drivers/accel/ — DRM Accel (NPU/AI 가속기 드라이버)
  • drivers/accel/ivpu/ — Intel NPU (IVPU) 드라이버
  • drivers/accel/habanalabs/ — Intel Gaudi 드라이버
  • drivers/accel/amdxdna/ — AMD XDNA NPU 드라이버
  • drivers/accel/qaic/ — Qualcomm Cloud AI 100 드라이버
  • include/drm/ — DRM 헤더 파일
  • include/drm/drm_accel.h — Accel 서브시스템 API
  • include/uapi/drm/ — 유저 공간 API (ioctl, 구조체)
  • include/uapi/drm/ivpu_accel.h — Intel NPU ioctl 정의
  • drivers/dma-buf/ — DMA-BUF 프레임워크
  • Documentation/gpu/ — 커널 공식 GPU 문서
  • Documentation/accel/ — Accel 서브시스템 문서
GPU/NPU 드라이버 개발 시 주의사항:
  • Fence 시그널링 규칙dma_fence는 반드시 유한 시간 내에 시그널되어야 합니다. 무한 대기 fence는 시스템 전체를 교착시킬 수 있습니다
  • 메모리 매핑 주의 — GPU VRAM을 WC(Write-Combining)로 매핑할 때 캐시 일관성에 주의. ioremap_wc() 사용
  • Atomic check 무결성atomic_check에서 부작용(side effect) 금지. 검증만 수행하고, 하드웨어 변경은 atomic_commit에서 수행
  • KMS 핫플러그 — Connector 상태 변경은 drm_kms_helper_hotplug_event()로 유저 공간에 통보. HPD(Hot Plug Detect) IRQ 핸들러에서 호출
  • GPU 리셋 격리 — 하나의 컨텍스트 행이 다른 컨텍스트에 영향을 주지 않도록 per-engine 또는 per-context 리셋 구현 권장
  • 펌웨어 로딩 — 최신 GPU/NPU는 대부분 request_firmware()로 마이크로코드를 로딩합니다. initramfs에 펌웨어 포함 필요 (CONFIG_EXTRA_FIRMWARE)
  • NPU 전원 관리 — NPU는 유휴 시 Runtime PM으로 완전 전원 차단을 권장. 펌웨어 재로딩 비용과 절전 효과의 트레이드오프 고려