GPU 서브시스템 (DRM/KMS)
Linux 커널 GPU 서브시스템: DRM/KMS 아키텍처, GEM/TTM 메모리 관리, DMA-BUF 버퍼 공유, GPU 스케줄러, Atomic Modesetting, DRM 드라이버 구조 종합 가이드.
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/ |
drivers/video/fbdev/)는 단순 프레임버퍼만 제공하는 레거시 인터페이스입니다.
DRM/KMS는 하드웨어 가속, 다중 디스플레이, vsync, overlay plane 등을 지원하며, fbdev는 DRM으로 대체되는 추세입니다.
새 드라이버에서는 반드시 DRM/KMS를 사용해야 합니다.
DRM 아키텍처
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; /* 드라이버별 프라이빗 데이터 */
};
/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 핵심 오브젝트
| 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: 완료 시 유저에게 이벤트 전달 */
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 값에 접근 */
...
}
modetest -M <driver> -p로 모든 KMS 오브젝트의 property와 현재 값을 확인할 수 있습니다.
유저 공간에서는 DRM_IOCTL_MODE_GETPROPERTY로 property 메타데이터를, DRM_IOCTL_MODE_OBJ_GETPROPERTIES로 오브젝트별 값을 조회합니다.
Color Management 파이프라인
KMS는 CRTC 레벨에서 3단계 색상 처리 파이프라인을 제공합니다. 이 파이프라인으로 HDR 톤매핑, 색 공간 변환, 감마 보정 등을 하드웨어 가속으로 수행합니다.
/* 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_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 주기 */
modetest -M amdgpu -p에서vrr_capableproperty 확인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, ...);
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_DUMB → mmap() |
가짜 오프셋 반환 후 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_device_funcs.evict_flags 콜백으로 퇴거 정책을 설정합니다.
GPU가 해당 버퍼에 접근하면 다시 VRAM으로 마이그레이션됩니다.
DMA-BUF (버퍼 공유)
DMA-BUF는 디바이스 간 버퍼 공유를 위한 커널 프레임워크입니다. GPU에서 렌더링한 버퍼를 디스플레이 컨트롤러, 비디오 인코더, 카메라 등 다른 디바이스와 복사 없이 공유할 수 있습니다.
/* 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 하드웨어에 전달합니다.
서브미션 모델 비교
| 모델 | 드라이버 | 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로 유저에게 반환 */
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_job | GPU에 제출할 작업 하나. 의존 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 드라이버
| 드라이버 | 하드웨어 | 커널 경로 | 메모리 관리 | 특징 |
|---|---|---|---|---|
| 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 프레임버퍼 위에서 동작 |
nvidia-open).
nouveau는 커뮤니티 리버스 엔지니어링 기반으로, 최신 GPU에서는 전력 관리(reclocking)와 성능이 제한됩니다.
최근 커널에서는 GSP(GPU System Processor) 펌웨어를 통해 nouveau의 기능이 개선되고 있습니다.
DRM Bridge & Panel 프레임워크
임베디드/모바일 SoC에서는 디스플레이 출력이 여러 개의 하드웨어 블록 체인으로 구성됩니다.
drm_bridge는 이 체인의 중간 단계(MIPI-DSI to HDMI 변환기, LVDS 직렬화기 등)를,
drm_panel은 최종 디스플레이 패널을 추상화합니다.
/* 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);
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 내 오프셋 */
};
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로 리셋 감지 */
- 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
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_GETRESOURCES | CRTC/Connector/Encoder ID 목록 | 없음 |
DRM_IOCTL_MODE_GETCONNECTOR | Connector 상태, 지원 모드 조회 | 없음 |
DRM_IOCTL_MODE_GETENCODER | Encoder 정보 조회 | 없음 |
DRM_IOCTL_MODE_GETCRTC | CRTC 현재 모드 조회 | 없음 |
DRM_IOCTL_MODE_SETCRTC | 모드 설정 (레거시) | DRM Master |
DRM_IOCTL_MODE_ATOMIC | Atomic 모드 설정/페이지 플립 | DRM Master |
DRM_IOCTL_MODE_CREATE_DUMB | Dumb 버퍼 생성 | 없음 |
DRM_IOCTL_MODE_MAP_DUMB | Dumb 버퍼 mmap 오프셋 | 없음 |
DRM_IOCTL_MODE_DESTROY_DUMB | Dumb 버퍼 해제 | 없음 |
DRM_IOCTL_PRIME_HANDLE_TO_FD | GEM → DMA-BUF fd | 없음 |
DRM_IOCTL_PRIME_FD_TO_HANDLE | DMA-BUF fd → GEM | 없음 |
DRM_IOCTL_GEM_CLOSE | GEM 핸들 해제 | 없음 |
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 헤드셋, 멀티시트 디스플레이, 게임 전용 출력 등에서 사용됩니다.
| 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, 페이지 플립 수행 */
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 통보 → 재인증 시도 */
디스플레이 연결 프로토콜
| 프로토콜 | 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) |
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 작업 재개 */
/dev/dri/renderD128은 DRM Master 없이 접근 가능하므로, 일반 사용자도 GPU 컴퓨트를 사용할 수 있습니다.
그러나 GPU 가상 메모리 격리가 제대로 구현되어야 다른 프로세스의 GPU 데이터가 유출되지 않습니다.
per-process GPU page table과 command 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 가속기를 관리합니다.
/dev/dri/renderD*)와 별도의 /dev/accel/accel* 디바이스 노드를 사용합니다.
이를 통해 GPU용 유저 공간 도구(Mesa, Vulkan)와 NPU용 ML 프레임워크가 충돌 없이 공존할 수 있습니다.
NPU vs GPU vs CPU 연산 특성
| 특성 | CPU | GPU | NPU |
|---|---|---|---|
| 아키텍처 | 범용 파이프라인, 소수의 고성능 코어 | 대량의 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는 다음과 같은 공통 구조를 가집니다.
NPU 데이터 흐름
NPU에서 추론을 수행하는 전체 데이터 흐름은 다음과 같습니다.
- 모델 컴파일: ONNX/TFLite 모델을 NPU 전용 바이너리(blob)로 오프라인 컴파일. 레이어별 타일링, 양자화, 메모리 할당 계획이 포함됨
- 버퍼 할당: GEM BO(Buffer Object)를 통해 입력/출력/가중치 버퍼를 커널 드라이버에 할당 요청
- 커맨드 버퍼 생성: UMD가 DMA 전송, 연산 커맨드 등을 커맨드 버퍼에 기록
- 잡 제출: ioctl을 통해 커맨드 버퍼를 커널에 제출 →
drm_sched가 스케줄링 - HW 실행: 펌웨어가 레이어 단위로 Compute Cluster에 디스패치, SRAM ↔ DDR DMA 전송 관리
- 완료 통보: IRQ →
dma_fence시그널 → 유저 공간에 결과 전달
양자화(Quantization)와 데이터 타입
NPU는 전력 효율을 위해 낮은 정밀도의 데이터 타입을 사용합니다. MAC 어레이의 데이터 타입에 따라 연산 처리량(TOPS)이 크게 달라집니다.
| 데이터 타입 | 비트 폭 | 범위/정밀도 | 용도 | 상대 처리량 |
|---|---|---|---|---|
| FP32 | 32-bit | IEEE 754 단정밀도 | 학습 기준선 | 1× (기준) |
| FP16 | 16-bit | ±65504, 정밀도 ~3.3자리 | 학습/추론 혼합 | 2× |
| BF16 | 16-bit | FP32 동적 범위, 낮은 정밀도 | 학습 (기울기 안정성) | 2× |
| INT8 | 8-bit | -128~127 / 0~255 | 추론 최적화 (PTQ/QAT) | 4× |
| INT4 | 4-bit | -8~7 / 0~15 | LLM 가중치 압축 | 8× |
| FP8 (E4M3/E5M2) | 8-bit | E4M3: ±448 / E5M2: ±57344 | 차세대 학습/추론 | 4× |
- 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_driver에 DRIVER_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;
}
주요 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 # 디버그 인터페이스
- 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_PARAM | R | HW 파라미터 조회 (플랫폼 타입, TOPS, 타일 수 등) |
DRM_IOCTL_IVPU_SET_PARAM | W | 런타임 파라미터 설정 |
DRM_IOCTL_IVPU_BO_CREATE | RW | GEM BO 생성 (크기, 플래그 → 핸들, NPU VA) |
DRM_IOCTL_IVPU_BO_INFO | RW | BO 정보 조회 (mmap offset, VA, 크기) |
DRM_IOCTL_IVPU_SUBMIT | W | 컴퓨트 잡 제출 (커맨드 버퍼 + BO 리스트) |
DRM_IOCTL_IVPU_BO_WAIT | RW | BO 사용 완료 대기 (타임아웃 지정) |
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 디버그 속성
- 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/로 이동했습니다.
| 특성 | Gaudi | Gaudi2 | Gaudi3 |
|---|---|---|---|
| TPC (Tensor Processing Core) | 8개 | 24개 | 64개 |
| MME (Matrix Math Engine) | 2개 | 2개 | 8개 |
| HBM | 32GB HBM2 | 96GB HBM2E | 128GB HBM2E |
| 네트워크 | 10× 100GbE RoCE | 24× 100GbE RoCE | 24× 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)
};
NPU와 DMA-BUF: 디바이스 간 버퍼 공유
DMA-BUF를 통해 NPU, GPU, 카메라(ISP), 비디오 디코더 간에 제로카피 버퍼 공유가 가능합니다. 이는 카메라 → NPU → GPU → 디스플레이 같은 ML 파이프라인에서 필수적입니다.
/* 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 디버깅 도구
| 도구 | 용도 | 사용법 |
|---|---|---|
dmesg | NPU 드라이버 로그 | dmesg | grep -i "ivpu\|npu\|accel\|xdna" |
lspci -vvv | NPU PCI 디바이스 정보 | lspci -d ::0x1200 -vvv (Processing Accel class) |
ftrace | DRM/Accel 내부 추적 | echo drm:* > /sys/kernel/debug/tracing/set_event |
perf | 드라이버 성능 분석 | perf record -a -g -e drm:* -- sleep 5 |
| Intel NPU Plugin | OpenVINO 통합 프로파일링 | OpenVINO benchmark_app + NPU 플러그인 |
| xrt-smi | AMD XDNA 모니터링 | Xilinx Runtime CLI (xrt-smi examine) |
NPU 유저 공간 소프트웨어 스택
NPU 드라이버는 커널에서 HW 접근과 메모리 관리만 담당하고, 실제 ML 모델 컴파일/실행은 유저 공간 스택이 처리합니다.
| 계층 | Intel NPU | AMD XDNA | Qualcomm 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/) |
# 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 서브시스템 APIinclude/uapi/drm/— 유저 공간 API (ioctl, 구조체)include/uapi/drm/ivpu_accel.h— Intel NPU ioctl 정의drivers/dma-buf/— DMA-BUF 프레임워크Documentation/gpu/— 커널 공식 GPU 문서Documentation/accel/— Accel 서브시스템 문서
- 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으로 완전 전원 차단을 권장. 펌웨어 재로딩 비용과 절전 효과의 트레이드오프 고려