GPU 서브시스템 (DRM/KMS)
Linux GPU 서브시스템(DRM/KMS)을 디스플레이 파이프라인(Pipeline)과 연산 가속 경로를 함께 보는 관점에서 심층 분석합니다. DRM core와 KMS 객체 모델(CRTC/plane/encoder/connector), GEM/TTM 메모리 관리(Memory Management)자, DMA-BUF 기반 장치 간 버퍼(Buffer) 공유, atomic modesetting과 vblank 동기화, fence 기반 GPU 작업 순서 제어, 전원·클럭·열 관리(Thermal Management), userspace(Mesa/Wayland)와의 ioctl 계약, debugfs/tracepoint로 프레임 드롭과 hang을 진단하는 방법까지 GPU 드라이버 개발 핵심을 다룹니다.
핵심 요약
- 노드 분리 — 화면 제어는 primary node, 비특권 렌더링은 render node, 비그래픽 연산은 accel node가 담당합니다.
- 상태 모델 — KMS는 CRTC/plane/connector를 atomic state로 묶어 한 번에 검증하고 적용합니다.
- 버퍼 수명주기 — BO 생성, 핸들 배정, mmap, DMA-BUF 공유, fence 동기화가 한 세트로 움직입니다.
- 메모리 계층 — 단순 SoC GPU는 GEM shmem, 전용 VRAM GPU는 TTM/VRAM helper/자체 GPUVM을 주로 사용합니다.
- 복구 설계 — hang 감지, fence 타임아웃, 엔진 리셋, 전원 재기동이 운영 안정성을 좌우합니다.
단계별 이해
- 어느 노드를 여는지부터 구분
compositor는 primary node에서 KMS를 제어하고, 일반 앱은 render node에서 커맨드 제출을 시작합니다. - 버퍼를 어떤 백엔드에 둘지 결정
scanout 전용이면 dumb buffer, 일반 렌더링이면 드라이버 전용 BO, 장치 간 공유면 DMA-BUF까지 함께 봅니다. - atomic 상태를 조립
plane/CRTC/connector 속성을 새 상태 객체에 채운 뒤atomic_check로 하드웨어 제약을 검증합니다. - 렌더링과 표시를 동기화
dma_resv,syncobj,IN_FENCE_FD/OUT_FENCE_PTR로 렌더 완료 시점을 맞춥니다. - 운영 중 고장 경로를 준비
GPU hang, hotplug, runtime suspend, reset recovery를 debugfs·tracepoint·drm_sched 타임아웃으로 추적합니다.
세부 문서 안내
GPU 서브시스템 문서는 주제별로 3개의 세부 문서로 나뉘어 있습니다. 각 문서에서 해당 영역을 심층적으로 다룹니다.
| 문서 | 주요 내용 | 핵심 키워드 |
|---|---|---|
| DRM 코어 및 디스플레이 (KMS) | DRM 코어 아키텍처, 디바이스 노드, KMS 객체 모델, atomic modesetting, DRM properties, VRR, format modifier, DRM bridge/panel, 디스플레이 프로토콜, HDCP, DRM lease, fbdev 에뮬레이션, 드라이버 골격, 주요 DRM 드라이버, ioctl 요약 | DRM, KMS, CRTC, encoder, connector, plane, atomic, VRR, HDCP |
| GPU 메모리 관리 및 스케줄러(Scheduler) | GEM 메모리 관리, TTM, DMA-BUF 버퍼 공유, GPU 커맨드 서브미션, drm_sched 스케줄러, GPUVM 가상 메모리(Virtual Memory), GPU 전원 관리(Power Management), GPU 리셋 및 복구, 커널 설정, DRM 디버깅(Debugging) | GEM, TTM, DMA-BUF, dma_fence, drm_sched, GPUVM, GPU PM, GPU reset |
| GPU 컴퓨팅 (GPGPU) | GPU 컴퓨트 개요, CUDA/NVIDIA 아키텍처, OpenCL 크로스 플랫폼 컴퓨트, Vulkan Compute 파이프라인, ROCm/HIP AMD 컴퓨트, Intel oneAPI/Level Zero | GPGPU, CUDA, OpenCL, Vulkan Compute, ROCm, HIP, oneAPI |
DRM 서브시스템 구조 개요
DRM은 Linux 커널의 GPU 접근을 관리하는 서브시스템입니다. 원래 3D 그래픽 가속을 위해 도입되었으나, 현재는 디스플레이 출력(KMS), GPU 메모리 관리(GEM/TTM), GPU 작업 스케줄링까지 포괄하는 핵심 프레임워크로 발전했습니다.
| 구성 요소 | 역할 | 상세 문서 |
|---|---|---|
| DRM Core | 드라이버 등록(Driver Registration), ioctl 디스패치(Dispatch), 파일 오퍼레이션 | DRM 코어 및 디스플레이 |
| KMS (Kernel Mode Setting) | 디스플레이 모드 설정, CRTC/Encoder/Connector/Plane | KMS 섹션 |
| GEM / TTM | GPU 메모리 버퍼 할당/관리, VRAM/시스템 메모리 간 이동 | GEM 섹션 |
| DMA-BUF | 디바이스 간 버퍼 공유 (GPU↔카메라↔디스플레이) | DMA-BUF 섹션 |
| GPU Scheduler | GPU 작업 큐(Workqueue) 관리, 우선순위(Priority), 타임아웃 처리 | GPU 스케줄러 섹션 |
| GPU 컴퓨트 | CUDA, OpenCL, Vulkan Compute, ROCm/HIP, oneAPI | GPU 컴퓨팅 |
디바이스 노드와 권한 모델 요약
현대 DRM UAPI는 GPU 하나를 단일 문자 디바이스로만 노출하지 않습니다. 같은 하드웨어라도 화면 제어, 비특권 렌더링, 비그래픽 연산을 서로 다른 노드로 나누어 권한 경계와 사용자 공간(User Space) 스택을 분리합니다.
| 노드 종류 | 대표 경로 | 주 용도 |
|---|---|---|
| Primary | /dev/dri/card0 |
KMS modeset, connector/plane/lease 제어 |
| Render | /dev/dri/renderD128 |
OpenGL/Vulkan/VA-API/OpenCL 등 비특권 렌더링과 GPGPU |
| Accel | /dev/accel/accel0 |
NPU/AI/신호 처리 같은 비그래픽 compute |
디바이스 노드의 상세 구조와 권한 모델, drm_file 구조체(Struct), DRM master 개념 등은
DRM 코어 및 디스플레이 — 디바이스 노드와 권한 모델 섹션을 참고하세요.
DRM 아키텍처
각 계층의 상세 내용은 다음 문서에서 확인할 수 있습니다:
- KMS (Mode Setting) → DRM 코어 및 디스플레이 — KMS
- GEM / TTM / DMA-BUF → GPU 메모리 관리 및 스케줄러
- GPU Scheduler → GPU 스케줄러 (drm_sched)
- GPU 컴퓨트 (GPGPU) → GPU 컴퓨팅
DRM 코어 초기화 및 드라이버 등록
DRM 드라이버는 PCI 디바이스 탐지 → 리소스 할당 → 커널 등록 → 사용자 공간 노출의 단계를 거칩니다.
이 섹션에서는 drm_driver 구조체의 핵심 필드와 초기화 흐름을 실제 커널 소스 코드와 함께 설명합니다.
drm_driver 구조체 핵심 필드
struct drm_driver는 DRM 드라이버의 모든 동작을 정의하는 핵심 구조체입니다.
다음은 2025 년 기준 주요 필드입니다:
/* include/drm/drm_drv.h — drm_driver 구조체 (핵심 필드) */
struct drm_driver {
/* 드라이버 메타데이터 */
const char *name; /* 드라이버 이름 (예: "i915", "amdgpu") */
const char *desc; /* 설명 문자열 */
const char *date; /* 버전/날짜 */
const struct file_operations *fops; /* 파일 오퍼레이션 */
/* 드라이버 기능 플래그 */
u32 driver_features; /* DRIVER_GEM, DRIVER_MODESET, DRIVER_RENDER 등 */
/* IOCTL 테이블 — 사용자 공간 API 노출 */
const struct drm_ioctl_desc *ioctls;
int num_ioctls;
/* PCI 디바이스 테이블 — 자동 탐지용 */
const struct pci_device_id *pci_driver;
/* 콜백 함수들 — 드라이버 수명주기 */
int (*load)(struct drm_device *, unsigned long flags);
void (*unload)(struct drm_device *);
int (*open)(struct drm_device *, struct drm_file *);
void (*postclose)(struct drm_device *, struct drm_file *);
/* KMS 관련 함수 포인터 */
const struct drm_mode_config_funcs *mode_config_funcs;
const struct drm_mode_config_helper_funcs *mode_config_helpers;
/* GEM/메모리 관리 함수 */
const struct drm_gem_object_funcs *gem_object_funcs;
const struct ttm_place *placements; /* TTM 메모리 영역 */
/* 동기화 및 스케줄링 */
const struct drm_sched_driver *sched_driver; /* drm_sched ops */
/* 디버깅 및 정보 제공 */
void (*debugfs_init)(struct drm_minor *);
int (*gem_prime_pin)(struct drm_gem_object *obj);
};
DRIVER_GEM— GEM 메모리 관리 사용DRIVER_MODESET— KMS (Kernel Mode Setting) 지원DRIVER_RENDER— render node (/dev/dri/renderD*) 생성DRIVER_COMPUTE_ACCEL— compute 가속기 기능 (비그래픽 작업)DRIVER_GPU_SCHEDULER— drm_sched 사용
PCI Probe — 디바이스 탐지 및 초기화
대부분의 GPU 드라이버는 PCI 서브시스템을 통해 탐지됩니다. 다음은 amdgpu 드라이버의 실제 probe 함수입니다:
/* drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c — PCI probe 함수 */
static int
amdgpu_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct drm_device *ddev;
struct amdgpu_device *adev;
int ret;
/* 1. DRM 디바이스 할당 */
ddev = devm_drm_dev_alloc(&pdev->dev, &amdgpu_kms_driver,
struct amdgpu_device, ddev);
if (IS_ERR(ddev))
return PTR_ERR(ddev);
adev = drm_to_adev(ddev);
adev->ddev = ddev;
adev->pdev = pdev;
pci_set_drvdata(pdev, adev);
/* 2. PCI 리소스 활성화 */
ret = pci_enable_device(pdev);
if (ret < 0) {
dev_err(&pdev->dev, "Cannot enable PCI device\n");
return ret;
}
/* 3. MMIO 리소스 매핑 */
adev->rmmio = pci_iomap(pdev, 0, 0);
if (!adev->rmmio) {
dev_err(&pdev->dev, "Cannot map MMIO region\n");
ret = -ENOMEM;
goto err_disable_pci;
}
/* 4. DRM 디바이스 등록 (사용자 공간에 /dev/dri/cardN 노출) */
ret = drm_dev_register(ddev, 0);
if (ret)
goto err_unmap_mmio;
/* 5. 추가 초기화 (펌웨어 로드, 파워 관리 등) */
ret = amdgpu_device_init(adev);
if (ret)
goto err_unregister;
return 0;
err_unregister:
drm_dev_unregister(ddev);
err_unmap_mmio:
pci_iounmap(pdev, adev->rmmio);
err_disable_pci:
pci_disable_device(pdev);
return ret;
}
코드 설명
-
12-17 행
devm_drm_dev_alloc()은 DRM 디바이스와 드라이버 전용 구조체 (amdgpu_device) 를 한 번에 할당합니다. 리소스 관리는 device-managed 함수로 자동 정리됩니다. - 23-28 행 PCI 디바이스를 활성화하고 MMIO(Memory-Mapped I/O) 영역을 매핑합니다. GPU 레지스터 접근에 필수적입니다.
-
34-38 행
drm_dev_register()는 DRM 코어에 디바이스를 등록하고/dev/dri/card0,/dev/dri/renderD128노드를 생성합니다. - 41-44 행 드라이버 전용 초기화 (펌웨어 로드, IP 블록 탐지, 파워 관리 초기화) 를 수행합니다.
drm_device 구조체와 수명주기
struct drm_device는 DRM 코어가 관리하는 디바이스 컨텍스트입니다:
/* include/drm/drm_device.h — drm_device 구조체 (핵심 필드) */
struct drm_device {
/* 디바이스 식별 */
struct device *dev; /* Linux device 모델 */
struct drm_driver *driver; /* 드라이버 ops */
/* KMS 객체 관리 */
struct drm_mode_config mode_config; /* CRTC/encoder/connector/plane 목록 */
/* 메모리 관리 */
struct drm_gem_object *gem_object_list;
struct mutex gem_mutex;
/* 파일/클라이언트 관리 */
struct list_head filelist;
struct drm_file *file_idr;
/* 마스터 권한 — 인증된 클라이언트만 KMS 제어 */
struct drm_master *master;
/* 스케줄러 */
struct drm_gpu_scheduler *sched[DRM_GPU_SCHED_MAX_ENTITIES];
/* 디버깅 */
struct drm_minor *primary;
struct drm_minor *render;
};
커맨드 서브미션 (Command Submission)
GPU 에 작업을 지시하려면 사용자 공간이 커맨드 버퍼 (Command Buffer) 를 작성하여 커널에 제출 (Submit) 합니다. 커널은 이를 검증 후 GPU 의 링 버퍼 (Ring Buffer) 에 기록하고, GPU 가 자동으로 처리합니다.
링 버퍼 아키텍처
링 버퍼는 순환 큐 (Circular Queue) 구조로, CPU(프로듀서) 가 커맨드를 쓰고 GPU(컨슈머) 가 읽어서 실행합니다.
/* drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h — 링 버퍼 구조체 */
struct amdgpu_ring {
struct amdgpu_device *adev;
unsigned enum amd_ip_block_type type; /* GFX, SDMA, Compute 등 */
const struct amdgpu_ring_funcs *funcs;
u32 *ring; /* 링 버퍼 포인터 (VRAM/GTT 매핑) */
unsigned long gpu_addr; /* GPU 가 보는 주소 */
u32 wptr; /* Write Pointer — CPU 가 쓴 위치 */
u32 rptr; /* Read Pointer — GPU 가 읽은 위치 */
u32 count_dw; /* 현재 커맨드 DW (DWORD) 수 */
u32 max_dw; /* 최대 커맨드 크기 */
u32 ring_size; /* 링 버퍼 크기 (바이트) */
struct mutex mutex; /* 직렬화용 뮤텍스 */
bool ready; /* 링 사용 가능 여부 */
/* 스케줄러 연동 */
struct drm_sched_rq *sched_rq;
struct drm_gpu_scheduler *sched;
};
Indirect Buffer (IB) — 간접 커맨드 버퍼
복잡한 워크로드는 여러 개의 Indirect Buffer(IB) 로 구성됩니다. 각 IB 는 GPU 가 실행할 커맨드 시퀀스를 담으며, 메인 링 버퍼는 IB 를 체이닝합니다.
Fence 와 동기화
Fence 는 GPU 작업의 완료를 나타내는 동기화 프리미티브입니다. DMA Fence 는 커널 내부에서, Sync File 은 사용자 공간에서 사용됩니다.
/* include/linux/dma-fence.h — DMA Fence 구조체 */
struct dma_fence {
refcount_t refcount; /* 참조 카운트 */
spinlock_t lock; /* 상태 보호용 스핀락 */
unsigned long flags; /* FLAG_SIGNALED 등 */
int status; /* 0: 성공, -errno: 실패 */
/* Fence 식별자 */
u64 seqno; /* 시퀀스 번호 */
u32 context; /* 컨텍스트 ID */
/* 콜백 */
struct rcu_head rcu;
const struct dma_fence_ops *ops;
};
/* Fence 시그널 (완료 알림) */
static inline void
dma_fence_signal(struct dma_fence *fence)
{
if (!test_and_set_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags))
__dma_fence_signal(fence);
}
timeout_msec 파라미터로 타임아웃을 감지하고 GPU 리셋을 트리거합니다.
GPU 인터럽트 처리
GPU 는 작업 완료, 오류, VBlank(수직 귀선 소거), thermal 이벤트 등을 커널에 알리기 위해 인터럽트 (Interrupt) 를 사용합니다. 현대 GPU 는 수백 개의 인터럽트 소스를 가지며, 이를 효율적으로 처리하기 위해 MSI-X(Message Signaled Interrupts) 와 인터럽트 도메인을 활용합니다.
GPU 인터럽트 종류
| 인터럽트 종류 | 소스 | 처리 우선순위 | 용도 |
|---|---|---|---|
| Graphics IRQ | GFX 링, Compute 링 | 높음 | 3D 렌더링/컴퓨트 작업 완료 알림 |
| Display IRQ | CRTC, VBlank, Page Flip | 매우 높음 | 디스플레이 타이밍, 페이지 플립 완료 |
| DMA/SDMA IRQ | SDMA 엔진 | 중간 | DMA 전송 완료, 버퍼 복사 |
| Error IRQ | PARITY, ECC, Hang | 즉시 처리 | 하드웨어 오류, GPU Hang 감지 |
| Power/Thermal IRQ | PMFW, Thermal 센서 | 낮음 | 전원 상태 변경, 과열 경고 |
인터럽트 핸들러(Handler) 아키텍처
GPU 인터럽트 처리는 Top Half(하드 IRQ) 와 Bottom Half(스레디드 IRQ) 로 나뉩니다:
- Top Half — 최소한의 긴급 작업만 수행 (레지스터(Register) 읽기, 인터럽트 소스 식별),
spin_lock으로 보호 - Bottom Half — 실제 처리 (Fence 시그널, 에러 로깅, 스케줄러 웨이크업),
kthread컨텍스트에서 실행
/* drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c — AMDGPU 인터럽트 핸들러 */
static irqreturn_t
amdgpu_irq_handler(int irq, void *dev_id)
{
struct amdgpu_device *adev = dev_id;
u32 status;
/* 1. 인터럽트 소스 식별 (MMIO 레지스터 읽기) */
status = RREG32_SOC15(GC, 0, mmGC_INT_CNTL);
/* 2. 처리할 인터럽트가 없으면 IRQ_NONE 반환 */
if (!(status & GC_INT_MASK))
return IRQ_NONE;
/* 3. 인터럽트 소스 클리어 (Ack) */
WREG32_SOC15(GC, 0, mmGC_INT_ACK, status);
/* 4. 스레디드 IRQ 깨우기 (Bottom Half) */
wake_up_interruptible(&adev->irq_thread_wq);
return IRQ_WAKE_THREAD;
}
/* 스레디드 인터럽트 핸들러 (Bottom Half) */
static irqreturn_t
amdgpu_irq_thread(int irq, void *dev_id)
{
struct amdgpu_device *adev = dev_id;
/* 5. Fence 시그널, 에러 처리, 스케줄러 알림 등 */
amdgpu_fence_process(adev);
amdgpu_dm_irq_process(adev); /* 디스플레이 IRQ */
amdgpu_ras_process(adev); /* 오류 처리 */
return IRQ_HANDLED;
}
코드 설명
-
8-11 행
MMIO 레지스터를 읽어 어떤 인터럽트가 발생했는지 식별합니다.
IRQ_NONE을 반환하면 커널은 이 디바이스가 인터럽트를 발생시키지 않았다고 판단합니다. - 14-15 행 인터럽트 레지스터를 클리어하여 다음 인터럽트를 받을 준비를 합니다. 이를 "Ack" 라고 합니다.
-
18-19 행
IRQ_WAKE_THREAD를 반환하면 커널이 스레디드 핸들러를 깨웁니다. 이렇게 하면 Top Half 는 빠르게 종료되고, 무거운 작업은 Bottom Half 에서 처리됩니다. - 25-31 행 스레디드 컨텍스트에서 실제 처리를 수행합니다. Fence 시그널, 디스플레이 업데이트, 에러 로깅 등이 이루어집니다.
MSI-X (Message Signaled Interrupts Extended)
현대 GPU 는 MSI-X 를 사용하여 여러 개의 인터럽트 벡터를 할당받습니다. 각 엔진 (GFX, Compute, SDMA, Display) 이 독립적인 IRQ 라인을 가지므로, 한 엔진의 인터럽트 처리가 다른 엔진을 블로킹하지 않습니다.
/* drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c — MSI-X 초기화 */
int
amdgpu_irq_init(struct amdgpu_device *adev)
{
int ret;
/* 1. MSI-X 지원 여부 확인 */
if (pci_enable_msix_range(adev->pdev, NULL, 1, 32) < 0) {
dev_warn(adev->dev, "MSI-X failed, fallback to INTx\n");
goto use_intx;
}
/* 2. 각 IRQ 벡터에 핸들러 등록 */
ret = request_threaded_irq(adev->irq.irq, amdgpu_irq_handler,
amdgpu_irq_thread, IRQF_ONESHOT,
"amdgpu", adev);
if (ret)
goto err_disable_msix;
return 0;
err_disable_msix:
pci_disable_msix(adev->pdev);
use_intx:
/* 레거시 INTx 모드 fallback */
return -EINVAL;
}
VBlank 인터럽트와 페이지(Page) 플립
VBlank(Vertical Blanking Interval) 는 디스플레이의 수직 귀선 소거 기간으로, 이 시점에 프레임버퍼를 교체해야 화면이 찢어지지 않습니다 (Tearing-free).
/* drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c — VBlank IRQ */
static void
dm_dce_crtc_high_irq(struct amdgpu_crtc *acrtc)
{
struct drm_crtc *crtc = &acrtc->base;
struct drm_device *dev = crtc->dev;
/* VBlank 이벤트 발생 — DRM 코어에 알림 */
drm_crtc_handle_vblank(crtc);
/* 대기 중인 페이지 플립 완료 처리 */
amdgpu_dm_complete_flip(crtc);
}
GPU 전원 관리 (Power Management)
현대 GPU 는 높은 성능과 전력 효율을 동시에 달성하기 위해 동적 전원 관리 (Dynamic Power Management, DPM) 를 사용합니다.
리눅스 커널 GPU 드라이버는 runtime PM, 클럭 게이팅 (Clock Gating), 전원 게이팅 (Power Gating), DVFS(Dynamic Voltage and Frequency Scaling) 등을 구현합니다.
전원 관리 계층
| 계층 | 구성 요소 | 역할 |
|---|---|---|
| 시스템 레벨 | ACPI, P-State, S-State | 전체 시스템 전원 상태 (S0~S5), CPU-GPU 협조 |
| 디바이스 레벨 | Runtime PM, PCI ASPM | GPU 디바이스 활성/유휴 상태 전환 |
| 블록 레벨 | Clock/Power Gating | GFX, SDMA, UVD, VCE 등 개별 IP 블록 전원 제어 |
| 코어 레벨 | CU/Shutter Gating | individual Compute Unit 전원 차단 |
Runtime PM — 자동 유휴 진입
Runtime PM 은 디바이스가 유휴 상태(Idle State)일 때 자동으로 전원을 차단하고, 접근 시 복구하는 메커니즘입니다.
사용자 공간은 /sys/bus/pci/devices/<BDF>/power/control 에서 제어할 수 있습니다.
/* drivers/gpu/drm/amd/pm/amdgpu_pm.c — Runtime PM 콜백 */
static int
amdgpu_runtime_suspend(struct device *dev)
{
struct drm_device *drm_dev = dev_get_drvdata(dev);
struct amdgpu_device *adev = drm_to_adev(drm_dev);
/* 1. 모든 엔진이 유휴인지 확인 */
if (!amdgpu_device_is_idle(adev))
return -EBUSY;
/* 2. 디스플레이가 모두 꺼졌는지 확인 (노트북 D3cold 진입 조건) */
if (amdgpu_device_has_dc_support(adev) &&
amdgpu_dm_has_display_in_use(adev))
return -EBUSY;
/* 3. VRAM 콘텐츠 백업 (필요시) */
amdgpu_device_evict_resources(adev);
/* 4. GPU 전원 차단 */
amdgpu_device_ip_suspend(adev);
amdgpu_device_fini_hw(adev);
pci_save_state(adev->pdev);
return 0;
}
static int
amdgpu_runtime_resume(struct device *dev)
{
struct drm_device *drm_dev = dev_get_drvdata(dev);
struct amdgpu_device *adev = drm_to_adev(drm_dev);
/* 1. PCI 상태 복구 */
pci_restore_state(adev->pdev);
/* 2. GPU 전원 온 및 IP 블록 재초기화 */
amdgpu_device_init_hw(adev);
amdgpu_device_ip_resume(adev);
/* 3. VRAM 콘텐츠 복구 (필요시) */
amdgpu_device_recover_vram(adev);
return 0;
}
코드 설명
- 9-11 행 GPU 가 실제로 유휴 상태인지 확인합니다. 실행 중인 작업이 있으면 서스펜드를 거부합니다.
- 14-17 행 디스플레이가 사용 중이면 (노트북 외부 모니터 연결 등) D3cold 진입을 막습니다. 이는 화면 깨짐을 방지합니다.
- 20-24 행 VRAM 의 중요한 콘텐츠를 시스템 메모리로 대피시킵니다. D3cold 에서 VRAM 은 전원 차단으로 내용이 소실됩니다.
- 33-39 행 재개 시 PCI 상태를 복원하고, GPU 를 다시 초기화합니다. 이 과정은 수백 밀리초가 소요될 수 있습니다.
클럭 게이팅 & 전원 게이팅
클럭 게이팅 은 사용하지 않는 블록의 클럭을 차단하여 동적 전력 소비를 줄이고, 전원 게이팅 은 블록 자체의 전원을 차단하여 누설 전력을 제거합니다.
| 게이팅 종류 | 적용 블록 | 전력 절감 효과 | 복구 지연(Latency) |
|---|---|---|---|
| Clock Gating | GFX, SDMA, UVD, VCE | 10-30% | < 1ms |
| Power Gating | GFX, VCE, UVD | 20-50% | 1-10ms |
| CU Gating | Compute Unit 개별 | 5-15% | < 1ms |
| Memory Gating | VRAM 채널 | 5-10% | 1-5ms |
/* drivers/gpu/drm/amd/pm/amdgpu_pm.c — 클럭/전원 게이팅 제어 */
static void
amdgpu_update_clock_gating(struct amdgpu_device *adev, bool enable)
{
u32 mask = AMD_CG_SUPPORT_GFX_MGCG | /* GFX Medium Grain Clock Gating */
AMD_CG_SUPPORT_GFX_CGCG | /* GFX Coarse Grain Clock Gating */
AMD_CG_SUPPORT_SDMA_MGCG | /* SDMA Clock Gating */
AMD_CG_SUPPORT_UVD_MGCG; /* UVD Clock Gating */
if (enable)
amdgpu_device_ip_set_clockgating_state(adev, AMD_CG_STATE_GATE);
else
amdgpu_device_ip_set_clockgating_state(adev, AMD_CG_STATE_UNGATE);
}
static void
amdgpu_update_power_gating(struct amdgpu_device *adev, bool enable)
{
if (enable) {
/* GFX 블록 전원 차단 */
amdgpu_device_ip_set_powergating_state(adev, AMD_PG_STATE_GATE);
} else {
/* GFX 블록 전원 온 */
amdgpu_device_ip_set_powergating_state(adev, AMD_PG_STATE_UNGATE);
}
}
DVFS (Dynamic Voltage and Frequency Scaling)
PPTable(PowerPlay Table) 는 GPU 의 전압 - 주파수 곡선을 정의하며, 드라이버는 워크로드에 따라 최적의 P-State 를 선택합니다.
열 관리 (Thermal Management)
GPU 는 열 스로틀링 (Thermal Throttling) 으로 온도를 제한하며, 임계치를 초과하면 강제 셧다운됩니다.
/* drivers/gpu/drm/amd/pm/amdgpu_thermal.c — 열 관리 */
struct amdgpu_thermal_controller {
int min_temp; /* 팬 최소 동작 온도 (°C × 1000) */
int max_temp; /* 팬 최대 동작 온도 */
int shutdown_temp; /* 강제 셧다운 임계치 (보통 110-120°C) */
int throttle_temp; /* 스로틀링 시작 온도 (보통 85-95°C) */
int current_temp; /* 현재 온도 (센서 읽기) */
};
static void
amdgpu_thermal_update(struct amdgpu_device *adev)
{
int temp = amdgpu_thermal_get_temperature(adev);
if (temp >= adev->thermal.shutdown_temp) {
dev_crit(adev->dev, "GPU temperature (%d°C) exceeds shutdown limit!\n", temp / 1000);
amdgpu_device_shutdown(adev);
} else if (temp >= adev->thermal.throttle_temp) {
dev_warn(adev->dev, "GPU thermal throttling activated (%d°C)\n", temp / 1000);
amdgpu_dpm_force_performance_level(adev, AMD_DPM_FORCED_LEVEL_LOW);
}
}
cat /sys/class/drm/card0/device/hwmon/hwmon*/temp*_input 로 온도를 모니터링할 수 있습니다.
GPU 리셋 및 복구 (Reset & Recovery)
GPU 는 복잡한 병렬 처리 장치이므로 hangs(정지), timeouts(타임아웃), errors(오류) 가 빈번하게 발생합니다. 리눅스 커널은 이러한 상황에서 GPU 리셋 을 통해 시스템을 복구하며, 가능한 한 사용자 공간의 작업을 계속 이어나갈 수 있도록 합니다.
리셋 트리거 (Reset Triggers)
| 트리거 유형 | 감지 방법 | 리셋 범위 |
|---|---|---|
| Ring Timeout | Fence 시그널 미발생 (10 초 기준) | 관련 링 단위 리셋 |
| VM Fault | IOMMU 페이지 폴트(Page Fault), ECC 오류 | 개별 CU/Shader 단위 |
| RAS Error | ECC Uncorrectable Error, parity error | 관련 IP 블록 리셋 |
| Full GPU Reset | 복구 실패 및 최종 수단 | GPU 전체 콜드 리셋 |
GPU 리셋 플로우
/* drivers/gpu/drm/amd/amdgpu/amdgpu_device.c — GPU 리셋 */
bool
amdgpu_device_reset_sriov(struct amdgpu_device *adev, bool from_full_gpu)
{
int ret;
/* 1. 모든 IP 블록 프리즌 (작업 중단) */
ret = amdgpu_device_ip_freeze(adev);
if (ret)
return false;
/* 2. RAS 에러 클리어 (ECC 등) */
amdgpu_ras_reset_all_error_count(adev);
/* 3. BACO 리셋 (Board Active Chip Off) — 전력 완전 차단 */
ret = amdgpu_device_baco_reset(adev);
if (ret)
goto unfreeze;
/* 4. 모든 IP 블록 재초기화 */
ret = amdgpu_device_ip_resume(adev);
if (ret)
goto error;
/* 5. VRAM 테스트 — 메모리 손상 여부 확인 */
ret = amdgpu_vram_test(adev);
if (ret) {
dev_err(adev->dev, "VRAM test failed after reset!\n");
goto error;
}
return true;
error:
amdgpu_device_fini_hw(adev);
unfreeze:
amdgpu_device_ip_unfreeze(adev);
return false;
}
코드 설명
- 8-11 행 모든 IP 블록을 "freezing"하여 새로운 작업 제출을 막습니다. 이는 리셋 중 추가적인 커맨드가 들어가는 것을 방지합니다.
- 14-15 행 RAS(Reliability, Availability, Serviceability) 에러 카운터를 초기화합니다. ECC 오류 기록을 클리어합니다.
- 18-22 행 BACO 리셋은 GPU 의 전력을 완전히 차단했다가 켜는 "hard reset"입니다. PCIe 링크도 재협상됩니다.
- 26-30 행 리셋 후 모든 IP 블록을 다시 초기화합니다. 이 과정에서 펌웨어도 재로드됩니다.
- 33-38 행 VRAM 테스트로 메모리 손상 여부를 확인합니다. 실패 시 드라이버는 더 이상 복구를 시도하지 않습니다.
리셋의 영향
| 리셋 범위 | 소요 시간 | 데이터 손실 | 재개 가능성 |
|---|---|---|---|
| Ring Reset | 10-50ms | 트랜지션 잃음 | 높음 (게임 재개) |
| Engine Reset | 50-200ms | 트랜지션 및 컨텍스트 손실 | 중간 (앱 재기동) |
| Full GPU Reset | 200-1000ms | 모든 컨텍스트 손실 | 낮음 (화면 꺼짐) |
| PCIe Reset | 1-5 초 | 시스템 전체 영향 | 최소 (호스트 리붓 필요) |
cat /sys/kernel/debug/dri/0/amdgpu_reset_history로 과거 GPU 리셋 이력을 확인할 수 있습니다.
리셋이 자주 발생한다면 드라이버 버그 또는 하드웨어 고장을 의심해야 합니다.
DRM Accel 서브시스템 (비그래픽 가속기)
전통적으로 DRM(Direct Rendering Manager)은 그래픽 출력을 담당하는 GPU만을 관리했으나,
현대 SoC와 x86 플랫폼에는 그래픽 파이프라인 없이 연산만 수행하는 가속기(Accelerator)가 늘어나면서
커널 6.2에서 drivers/accel/ 트리와 /dev/accel/accel* 노드가 도입되었습니다.
DRM의 버퍼·fence·스케줄러 인프라를 재사용하되, 디스플레이와 관련된 KMS 경로를 완전히 제거한 경량 경로입니다.
/dev/dri/cardN의 master 권한 모델도 맞지 않습니다. Accel 노드는 render node와 유사한 비특권 접근을 기본으로 삼아
사용자 공간(User Space) 컨테이너(Container)·세션 관리자와 잘 어울립니다.
커널 트리에 상주하는 주요 Accel 드라이버
| 드라이버 | 하드웨어 | 머지 커널 | 특징 |
|---|---|---|---|
drivers/accel/habanalabs |
Intel(구 Habana) Gaudi / Gaudi2 / Gaudi3 | 5.18 이전(이전은 misc) | 데이터센터 AI 학습 가속기. Accel 서브시스템 1호 드라이버 |
drivers/accel/ivpu |
Intel Core Ultra 내장 NPU (Meteor Lake 이후) | 6.3 | 저전력 클라이언트 NPU. MTL/ARL/LNL/PTL 순차 확장 |
drivers/accel/qaic |
Qualcomm Cloud AI 100 | 6.5 | AI Core(AIC) PCIe 카드. 데이터센터 추론 가속 |
drivers/accel/amdxdna |
AMD Ryzen AI NPU (XDNA/Phoenix 이후) | 6.14 | 2025년 3월 머지. Ryzen 7040/8040 Phoenix·Hawk·Strix |
drivers/accel/rocket |
Rockchip RK3588 NPU (RKNN) | 6.18 (2025년 12월) | Tomeu Vizoso 가 리버스 엔지니어링한 벤더 의존 없는 오픈 드라이버. Mesa 25.3 사용자 공간 코드와 연동 |
각 Accel 드라이버의 상세 아키텍처, 컴파일러 스택, DMA-BUF 연계는 NPU 문서에서 다룹니다. 최근 몇 년간 Accel 드라이버가 꾸준히 추가되면서, NPU 하드웨어 다변화가 본격화되고 있습니다. 다만 개별 드라이버의 정확한 공개 시점과 머지 여부는 메인라인 릴리스 노트를 다시 확인하는 편이 안전합니다.
Accel UAPI와 일반 DRM UAPI의 공통·차이점
| 항목 | DRM (GPU) | Accel (NPU 등) |
|---|---|---|
| 노드 | /dev/dri/card*, /dev/dri/renderD* | /dev/accel/accel* |
| KMS 객체 | CRTC/plane/encoder/connector 있음 | 없음 (디스플레이 경로 제거) |
| Master/권한 | primary는 capability 기반 master 필요 | 기본이 비특권 render 모델 |
| BO(Buffer Object) | GEM shmem / TTM / VRAM helper | GEM shmem 또는 드라이버 고유 BO |
| 스케줄러 | drm_sched 또는 펌웨어(Firmware) 스케줄러(GuC 등) | 드라이버 자체 워크 큐 또는 drm_sched 재사용 |
| 공유 UAPI | DMA-BUF, dma_fence, sync_file, syncobj(Sync Object) | |
Rust GPU 드라이버 생태계
2025년은 Rust 언어가 리눅스 커널 GPU 서브시스템에 본격 도입된 분기점입니다. 메모리 안전 언어로 GPU 드라이버를 작성하려는 흐름은 Apple Silicon에서 시작되어, NVIDIA GSP 드라이버(Nova)가 메인라인에 합류하면서 가속됐습니다. 기존 C 기반 DRM 인프라는 그대로 유지하면서, 새 드라이버가 DRM 바인딩을 Rust로 감싸는 방식으로 확장됩니다.
Nova — NVIDIA GSP 기반 Rust DRM 드라이버 (커널 6.15+)
Nova는 NVIDIA GeForce RTX 20(Turing) 이후 세대의 GPU System Processor(GSP) 기반 GPU를 위한 새 드라이버로,
Red Hat의 Danilo Krummrich가 주도하여 2025년 5월 커널 6.15에 drivers/gpu/drm/nova/가 머지됐습니다.
기존 Nouveau를 대체하는 후속 드라이버를 목표로 하며, 현재는 초기 코어 컴포넌트와 프로젝트 문서만 머지된 실험 단계입니다.
2025년 하반기에는 NVIDIA 소속 엔지니어가 공동 메인테이너로 합류했습니다.
- GSP 우선 — GPU 내부 RISC-V 기반 마이크로컨트롤러에 펌웨어(Firmware)로 초기화·스케줄링을 위임하므로, 드라이버는 주로 RPC 래퍼가 됩니다.
- Rust 추상화 —
kernel::drm크레이트로 device/file/gem 래퍼를 제공하며, unsafe 경계는 최소화합니다. - Nouveau와 공존 — 당분간 Nouveau가 실사용 드라이버로 남고, Nova는 대체 준비가 완료될 때까지 개발 브랜치로 병행합니다.
- 문서 경로 —
Documentation/gpu/nova/와https://docs.kernel.org/gpu/nova/에 공식 설명이 존재합니다.
Asahi AGX — Apple Silicon GPU 드라이버
Apple M1/M2/M3/M4 시리즈의 내장 GPU인 AGX는 Asahi Linux 프로젝트에서 Rust로 작성한 드라이버로 리눅스에서 구동됩니다.
AGX 드라이버는 DRM 서브시스템용 Rust 바인딩을 실제 production-level로 처음 도입한 사례로,
Mesa의 Honeykrisp(Vulkan) / Gallium AGX(OpenGL 4.6, ES 3.2) 드라이버와 짝을 이룹니다.
현재 업스트림 머지 목표로 AsahiLinux/linux의 gpu/rust-wip 브랜치에서 재작성이 진행 중입니다.
Rust DRM 바인딩의 공통 기반
두 드라이버는 공통으로 다음 Rust 추상화에 의존합니다:
kernel::drm::device—drm_device래퍼와 driver registrationkernel::drm::file— 파일 ops, ioctl dispatchkernel::drm::gem— GEM Object 트레이트와 수명주기 관리kernel::drm::sched— drm_sched Rust 래퍼 (진행 중)kernel::dma_fence— dma_fence 래퍼로 동기화 프리미티브를 안전하게 다룹니다
이들 크레이트는 기존 C API를 그대로 호출하므로 ABI 변경 없이 공존하며, unsafe 블록은 FFI 경계에만 남겨 안전성을 확보합니다.
drm_panic — 커널 패닉(Kernel Panic) 화면 인프라
드라이버가 Wayland·compositor 없이 DRM 프레임버퍼만 보유한 상태에서도 커널 패닉(Kernel Panic) 시 사용자에게 읽을 수 있는 메시지를 남기는 구조가 2024년 커널 6.10에서 추가되었습니다. fbcon(Frame Buffer Console)이 없거나 VT가 꺼진 환경에서도 화면에 패닉 내용을 그려주며, Fedora와 Arch 계열처럼 이를 적극 도입한 배포판이 있습니다. 다만 2026년 4월 기준으로 배포판별 기본 활성 범위와 사용자 노출 방식은 릴리스별로 다를 수 있으므로 해당 배포판 문서를 함께 확인하는 편이 안전합니다.
동작 원리
패닉 시 드라이버가 등록한 drm_panic 콜백(Callback)이 호출되어, fence·mutex 없이 MMIO만으로 프레임버퍼를 직접 갱신합니다.
따라서 패닉 루틴은 일반 드라이버 경로와 완전히 분리된 lock-free·interrupt-safe 구현이어야 합니다.
/* include/drm/drm_panic.h */
struct drm_panic_scanout {
void *vaddr; /* 직접 접근 가능한 프레임버퍼 포인터 */
unsigned int pitch; /* 라인 당 바이트 */
unsigned int width, height;
u32 format; /* DRM_FORMAT_* */
};
struct drm_plane_helper_funcs {
...
int (*get_scanout_buffer)(struct drm_plane *, struct drm_panic_scanout *);
void (*panic_flush)(struct drm_plane *);
};
지원 드라이버 현황 (2026년 기준)
| 드라이버 | 지원 시작 커널 | 비고 |
|---|---|---|
| simpledrm, mgag200, ast | 6.10 | 최초 지원. 간단한 라이너 프레임버퍼 드라이버 |
| i915, xe | 6.12~6.14 | Intel 클라이언트 GPU 지원. Panic 시 Intel CSE와 협조 |
| amdgpu | 6.14 | DCN 기반 디스플레이에서 panic 플러시(Flush) 지원 |
| nouveau | 진행 중 | Jocelyn Falempe가 작업 중 |
get_scanout_buffer를 구현하지 않으면 패닉 화면이 뜨지 않고 이전 화면이 얼어붙은 상태로 남습니다.
또한 일부 AMD GPU에서는 DCN 상태에 따라 panic 화면이 깨져 보일 수 있는 이슈가 보고되어 지속 개선 중입니다.
GPU 가상화 (Virtualization)
가상 머신(VM)과 컨테이너에서 GPU 를 활용하려는 수요가 커지면서, 리눅스는 여러 계층의 GPU 가상화 방식을 지원합니다. 각 방식은 성능 · 격리(Isolation) · 공유 밀도의 균형점이 다릅니다. 크게 API 포워딩, DRM native context(vDRM), 하드웨어 분할(SR-IOV), 전체 패스스루(Passthrough)로 나뉩니다.
| 방식 | 매개 계층 | 대표 구현 | 특징 |
|---|---|---|---|
| API 포워딩 | 고수준 GL/Vulkan API | Virgl(OpenGL), Venus(Vulkan) — virtio-gpu + virglrenderer | 호스트가 API 호출을 번역. 이식성 높으나 CPU 오버헤드(Overhead)와 코드 복잡도 큼 |
| DRM native context (vDRM) | 커널 드라이버 UAPI(저수준) | virtio-gpu drm_native_context=on |
게스트가 호스트 GPU 를 네이티브처럼 인식. API 포워딩보다 CPU 오버헤드·코드량 적음 |
| SR-IOV 분할 | PCIe 하드웨어 VF(Virtual Function) | AMD MxGPU, Intel Xe SR-IOV | 하드웨어가 직접 다중 VF 로 분할. 높은 성능과 격리, 단 지원 GPU 한정 |
| 전체 패스스루 | PCI 디바이스 전체 | VFIO-PCI | GPU 한 장을 VM 하나에 독점 할당. 최고 성능이나 공유 불가 |
DRM native context (vDRM)
vDRM(virtual DRM)은 Virgl/Venus 처럼 고수준 그래픽 API 를 매개하는 대신,
리눅스 커널 드라이버의 UAPI(ioctl) 수준에서 매개합니다.
따라서 게스트 안의 GL/Vulkan 애플리케이션은 가상 GPU 를 실제 호스트 GPU 처럼 직접 다루며,
중간 번역 단계가 줄어 CPU 오버헤드와 구현 복잡도가 모두 낮아집니다.
QEMU 에서는 -device virtio-gpu-gl,drm_native_context=on 옵션으로 활성화합니다.
vDRM 컨텍스트는 GPU 드라이버마다 호스트·게스트 양쪽 구현이 필요합니다. 2025년 기준 업스트림 현황은 다음과 같습니다:
| 드라이버 | 하드웨어 | 상태 |
|---|---|---|
| Freedreno | Qualcomm Adreno (SoC GPU) | 완전 업스트림 |
| AMDGPU | AMD Radeon | 완전 업스트림 |
| Intel (i915) | Intel GPU | 머지 리퀘스트(Merge Request) 진행 중 |
| Asahi | Apple Silicon GPU | 부분 머지 |
SR-IOV 와 하드웨어 분할
SR-IOV(Single Root I/O Virtualization)는 PCIe 표준 기능으로, 하나의 물리 GPU(PF, Physical Function)를 여러 개의 VF(Virtual Function)로 분할하여 각 VM 에 거의 네이티브에 가까운 성능으로 할당합니다.
- AMD MxGPU — GIM(GPU-IOV Module) 커널 모듈(Kernel Module)로 VF 를 관리하며, 데이터센터 GPU 에서 다중 VM 공유를 지원합니다.
- Intel Xe SR-IOV — 커널 6.18 에서 지원 플랫폼의 PF 모드가 기본 활성화되었고, 7.0 에서 VF 마이그레이션(VM 라이브 마이그레이션 대응)과 PF MERT 가 추가되었습니다. Battlemage(Arc B 시리즈)에서도 SR-IOV 가 확장되고 있습니다.
i915 기반 vfio-mdev 매개 패스스루)는
중간 세대 Intel GPU 에서 VF 없이 가상 GPU 를 나누던 방식이었으나, 현재는 deprecated 되어 신규 플랫폼에서는 Xe SR-IOV 로 대체되었습니다.
GPU 가상화 경로는 하드웨어 세대마다 빠르게 바뀌므로 대상 GPU 의 최신 드라이버 문서를 확인하는 편이 안전합니다.
2025-2026 DRM 동향과 커널 버전 매트릭스
2025년부터 2026년 상반기까지 GPU·가속기 서브시스템에 머지된 주요 기능을 커널 버전별로 요약합니다. 배포판 선택·드라이버 포팅 계획에 참고할 수 있습니다.
관련 연혁: GPU / DRM 변경 이력
3대 하이라이트
- Rust의 본격 도입 — Nova(NVIDIA)·Asahi AGX(Apple)·Tyr(Arm) 등 Rust 기반 DRM 드라이버가 잇따라 제안되고,
kernel::drm바인딩이 안정화 경로에 들어섰습니다. - 가속기 생태계 확장 — AMDXDNA(Ryzen AI)·Rockchip Rocket(RK3588)에 이어, 2026년에도 최소 2종의 새 NPU 드라이버가 예정되어 Accel 서브시스템이 GPU 못지 않은 규모로 성장 중입니다.
- 색 파이프라인·동기화 현대화 — KMS Color Pipeline API(HDR),
linux_drm_syncobj_v1(Wayland explicit sync), DMEM cgroup(VRAM 제한), Fair DRM Scheduler 등으로 게임·HDR·컨테이너 워크로드에서의 체감 품질이 크게 개선됐습니다.
커널 버전별 주요 변경사항 (2025~2026)
| 커널 버전 | 출시 시기 | 주요 변경사항 |
|---|---|---|
| 6.15 | 2025년 3~4월 |
|
| 6.16 | 2025년 5~6월 |
|
| 6.17 | 2025년 8~9월 |
|
| 6.18 | 2025년 12월 (LTS) |
|
| 6.19 | 2026년 2월 8일 (6.x 마지막) |
|
| 7.0 | 2026년 4월 12일 |
|
| 7.1 | 병합 창 진행 중 (2026년 6월 출시 예정) |
|
참고자료
drivers/gpu/drm/— DRM 코어 + 모든 GPU 드라이버drivers/accel/— compute accelerator 드라이버include/drm/— DRM 헤더 파일include/uapi/drm/— 유저 공간 API (ioctl, 구조체)drivers/dma-buf/— DMA-BUF 프레임워크Documentation/gpu/— 커널 공식 GPU 문서Documentation/accel/— 커널 공식 accelerator 문서
- DRM UAPI — primary/render node, client capability, dumb buffer 제약
- DRM KMS — atomic modesetting, plane/connector/CRTC helper 계약
- DRM Memory Management — GEM, PRIME, VRAM helper, TTM, GPUVA
- Compute Accelerators — accel 디바이스 노드와 드라이버 모델
- GPU Driver Documentation — 드라이버별 하위 문서 색인
관련 문서
이 주제와 관련된 다른 문서를 더 깊이 이해하고 싶다면 다음을 참고하세요.