GPU 메모리 관리(Memory Management) 및 스케줄러(Scheduler)
Linux GPU 서브시스템의 메모리 관리와 작업 스케줄링을 심층 분석합니다. GEM(Graphics Execution Manager)과 TTM(Translation Table Manager) 메모리 관리, DMA-BUF 기반 디바이스 간 버퍼(Buffer) 공유, GPU 커맨드 서브미션과 fence 동기화, drm_sched 스케줄러, GPUVM 가상 메모리(Virtual Memory), 전원·클럭 관리, GPU 리셋 및 복구, 커널 설정과 디버깅(Debugging) 방법을 다룹니다.
핵심 요약
- GEM(Graphics Execution Manager) — 버퍼 객체(BO) 생성·핸들·mmap·수명을 관리하는 DRM 공용 계층입니다.
- TTM(Translation Table Manager) — VRAM·GTT·시스템 메모리 사이를 자동으로 이주시키는 디스크리트 GPU용 메모리 매니저입니다.
- DMA-BUF — 서로 다른 드라이버·장치가 같은 버퍼를 공유하기 위한 커널 표준 프로토콜입니다.
- fence / syncobj — 비동기 GPU 작업의 완료를 기다리게 해주는 동기화 객체입니다.
- drm_sched — 엔트리 큐·타임슬라이스·타임아웃을 가진 공용 GPU 작업 스케줄러로, 여러 컨텍스트의 공정한 실행을 보장합니다.
단계별 이해
- BO 생성과 핸들 배정
드라이버가drm_gem_object_init으로 BO를 만들고, 사용자 공간(User Space)에는 핸들을 돌려줍니다. - 메모리 도메인 선택
시스템 RAM·GTT·VRAM 중 어디에 둘지 결정합니다. 디스크리트 GPU는 TTM이 사용 패턴에 따라 자동 이동시킵니다. - GPU 가상 주소 매핑
GPUVM/HMM을 통해 BO를 GPU 페이지(Page) 테이블에 매핑해 셰이더가 접근할 수 있게 합니다. - 커맨드 서브미션과 fence 연결
커맨드 버퍼에 서명(fence)을 붙여 제출하고, 다른 작업은 이 fence가 signal될 때까지 대기합니다. - 공유·수명·복구
DMA-BUF로 타 장치와 공유하고, drm_sched 타임아웃·엔진 리셋·dma_resv해제로 hang 복구와 자원 회수를 처리합니다.
GEM (Graphics Execution Manager) 메모리 관리
GEM은 GPU 메모리 버퍼 객체를 관리하는 프레임워크입니다. 유저 공간에서는 정수 핸들로 버퍼를 참조하고,
커널은 내부적으로 drm_gem_object로 관리합니다.
/* drm_gem_object 핵심 필드 발췌 (include/drm/drm_gem.h) */
struct drm_gem_object {
struct kref refcount; /* 참조 카운팅 */
unsigned handle_count; /* 유저 핸들 수 */
struct drm_device *dev; /* 소속 디바이스 */
struct file *filp; /* shmem backing store 또는 NULL */
struct drm_vma_offset_node vma_node; /* mmap 오프셋 관리 */
size_t size; /* 버퍼 크기 */
int name; /* flink 이름 (레거시) */
struct dma_buf *dma_buf; /* PRIME export/import 연결점 */
struct dma_buf_attachment *import_attach;
struct dma_resv *resv; /* 공유 fence 컨테이너 */
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);
};
DRM_IOCTL_MODE_CREATE_DUMB는 CPU mmap과 scanout을 위한 최소 공통 경로입니다.
반면 GBM이나 vendor UAPI가 만드는 렌더 타깃 BO는 tiling/modifier, 압축 메타데이터, 엔진별 배치 제약,
수입된 DMA-BUF attachment까지 함께 고려합니다.
GEM 버퍼 생명주기
| 단계 | ioctl / 함수 | 설명 |
|---|---|---|
| 생성 | DRM_IOCTL_MODE_CREATE_DUMB |
비가속 dumb 버퍼 생성 (KMS 전용). 드라이버별 ioctl로 가속 버퍼 생성 |
| 매핑(Mapping) | DRM_IOCTL_MODE_MAP_DUMB → mmap() |
가짜 오프셋(Offset) 반환 후 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 |
핸들 해제. 참조 카운트(Reference Count)가 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 메모리 도메인 기술자 */
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_buffer_object 핵심 필드 발췌 */
struct ttm_buffer_object {
struct drm_gem_object base; /* GEM 상속 */
struct ttm_device *bdev; /* TTM 디바이스 */
enum ttm_bo_type type;
uint32_t page_alignment;
void (*destroy)(struct ttm_buffer_object *bo);
struct kref kref;
struct ttm_resource *resource; /* 현재 메모리 위치 */
struct ttm_tt *ttm; /* 시스템 메모리 페이지 */
bool deleted;
unsigned priority;
unsigned pin_count;
struct sg_table *sg;
};
/* TTM 메모리 도메인 상수 */
#define TTM_PL_SYSTEM 0 /* 시스템 RAM (커널 관리) */
#define TTM_PL_TT 1 /* GART/GTT 매핑된 시스템 메모리 */
#define TTM_PL_VRAM 2 /* 디바이스 전용 비디오 메모리 */
ttm_place/ttm_placement는 “이번 validate에서 허용할 위치 후보”를 설명하는 정책 객체입니다.
현재 실제 위치는 ttm_buffer_object.resource가 나타내며, TTM은 이 값과 새 placement 후보를 비교해
VRAM↔TT↔SYSTEM migration을 결정합니다.
ttm_device_funcs.evict_flags 콜백(Callback)으로 퇴거 정책을 설정합니다.
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 작업은 비동기로 실행되므로, 버퍼를 공유하는 디바이스들은 작업 완료를 동기화해야 합니다.
| 구조체(Struct) | 역할 | 사용 예 |
|---|---|---|
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);
Implicit Sync vs Explicit Sync
GPU 동기화 모델은 두 가지로 나뉩니다. Linux DRM은 전통적으로 implicit sync를 사용했으나, Vulkan과 Android를 중심으로 explicit sync로 전환하고 있습니다.
| 항목 | Implicit Sync (암시적) | Explicit Sync (명시적) |
|---|---|---|
| fence 관리 | 커널이 dma_resv에 자동 등록/확인 |
유저 공간이 sync_file fd로 직접 관리 |
| KMS 인터페이스 | 커널이 프레임버퍼의 fence를 자동 대기 | IN_FENCE_FD / OUT_FENCE_PTR plane property 사용 |
| 장점 | 유저 공간 구현 단순, 레거시 호환 | 세밀한 동기화 제어, GPU 파이프라인(Pipeline) 최적화 가능 |
| 단점 | 불필요한 대기 발생 가능, 최적화 어려움 | 유저 공간 복잡도 증가 |
| 사용 API | OpenGL (EGL), GBM | Vulkan, Android HWC, Wayland explicit sync |
| 커널 구성 | dma_resv + 자동 fence 추적 |
sync_file + SYNC_IOC_MERGE / SYNC_IOC_FILE_INFO |
/* Explicit Sync: KMS IN/OUT fence 사용 */
/* 유저 공간: GPU 렌더링 완료 fence fd를 Plane에 전달 */
drmModeAtomicAddProperty(req, plane_id, IN_FENCE_FD_prop, gpu_fence_fd);
/* 유저 공간: 커밋 완료 fence fd를 받을 위치 지정 */
int64_t out_fence_fd = -1;
drmModeAtomicAddProperty(req, crtc_id, OUT_FENCE_PTR_prop,
(uint64_t)(uintptr_t)&out_fence_fd);
/* Atomic 커밋 */
drmModeAtomicCommit(fd, req, DRM_MODE_ATOMIC_NONBLOCK |
DRM_MODE_PAGE_FLIP_EVENT, NULL);
/* out_fence_fd에 유효한 fd가 설정됨
* → 다음 렌더링 시작 전에 이 fence를 GPU에 제출하여 대기
* → 완전한 파이프라인 동기화 */
wp_linux_drm_syncobj_v1 계열 프로토콜로 DRM syncobj 기반 explicit sync를 협상할 수 있습니다.
핵심은 “커널 버전 숫자”보다 사용 중인 compositor, Mesa/driver, 커널 UAPI가 모두 syncobj timeline과 KMS fence를 연결할 수 있는지입니다.
즉, explicit sync는 render node 제출 경로와 KMS atomic commit 경로가 같은 fence 언어를 공유할 때 가장 큰 효과를 냅니다.
GPU 커맨드 서브미션
GPU 렌더링 작업은 커맨드 버퍼(command buffer)를 통해 제출됩니다. 유저 공간(Mesa/Vulkan 드라이버)이 GPU 명령어를 버퍼에 기록하고, 커널 드라이버가 이를 GPU 하드웨어에 전달합니다.
서브미션 모델 비교
| 모델 | 드라이버 | ioctl | 특징 |
|---|---|---|---|
| Ring Buffer | i915 (레거시) | I915_GEM_EXECBUFFER2 |
커맨드 버퍼를 링 버퍼(Ring Buffer)에 복사. relocation으로 GPU 주소 패칭 |
| Submit Queue | amdgpu | AMDGPU_CS |
IB(Indirect Buffer) 체인 제출. 다중 엔진(GFX/SDMA/UVD) 지원 |
| Exec Queue | xe (Intel) | DRM_XE_EXEC |
VM-bind 기반. relocation 없음, GPU 페이지 테이블(Page Table) 직접 관리 |
| 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);
- 커널 드라이버의 커맨드 서브미션 검증 (보안상 중요) */
- 버퍼 오브젝트 유효성 */
- 유저가 전달한 GEM 핸들이 실제 소유인지 확인 */
- BO가 올바른 메모리 도메인에 있는지 확인 */
- 커맨드 파싱 (일부 드라이버) */
- 금지된 GPU 레지스터(Register) 접근 차단 */
- 다른 프로세스(Process)의 GPU 주소 공간(Address Space) 접근 방지 */
- 최신 GPU는 하드웨어 격리(Isolation) (per-context page table)로 대체 */
- 의존성 처리 */
- 공유 BO의 dma_resv에서 기존 fence 확인 */
- 의존 fence를 drm_sched_job의 dependency로 등록 */
- 모든 의존 fence가 시그널(Signal)된 후에만 GPU에 제출 */
- fence 등록 */
- 새 작업의 fence를 사용 BO의 dma_resv에 추가 */
- explicit sync: fence를 sync_file fd로 유저에게 반환 */
GPU 스케줄러 (drm_sched)
DRM GPU 스케줄러(drivers/gpu/drm/scheduler/)는 여러 프로세스의 GPU 작업을 우선순위(Priority) 기반으로 공정하게 스케줄링하고, GPU 행(hang) 감지 시 타임아웃 콜백을 통해 GPU 리셋 및 복구를 처리합니다. drm_sched_entity, drm_sched_job, drm_gpu_scheduler가 핵심 구성 요소입니다.
GPU 가상 메모리 (GPUVM)
현대 GPU는 자체 MMU를 가지며, 프로세스별 독립적인 GPU 가상 주소 공간을 제공합니다.
커널 DRM 프레임워크는 drm_gpuvm(drivers/gpu/drm/drm_gpuvm.c)으로 이를 추상화합니다.
Intel Xe SVM (Shared Virtual Memory) — Linux 6.15
Linux 6.15에서 Intel Xe 드라이버가 Shared Virtual Memory (SVM)를 메인라인에 추가했습니다. SVM은 CPU와 GPU가 동일한 가상 주소 공간을 공유하여 포인터 기반 프로그래밍을 가능하게 합니다.
- 동작 방식 —
drm_gpusvm구조를 활용하여 CPU와 GPU가 동일한 가상 주소에서 페이지 폴트를 공유합니다. - THP for Device Pages — Linux 7.1에서 Intel Xe는 THP(Transparent Huge Pages)를 디바이스 페이지에 적용하여 SVM 성능을 더욱 개선합니다.
- CPU/GPU 원자성 혼합 — SVM에서 GPU와 CPU 원자성을 동시에 사용할 때의 이슈를 6.15에서 수정했습니다.
- Linux 6.16+ — xe_migrate_access_memory 수정, SVM time-slicing 지원 추가
DMEM cgroup — GPU VRAM 쿼터 (Linux 6.14)
그동안 메모리 cgroup은 시스템 RAM만 제어할 수 있었고, GPU의 VRAM은 cgroup 경계 밖에 있었습니다. 게임·컴퓨팅 워크로드를 여러 컨테이너(Container)로 분리한 환경에서 한 컨테이너가 VRAM을 독점하면 다른 컨테이너의 디스플레이 compositor까지 얼어붙는 문제가 흔했습니다. 커널 6.14에서 머지된 DMEM(Device Memory) cgroup은 이 간극을 메우며, TTM 기반 드라이버라면 거의 수정 없이 VRAM에 cgroup 쿼터를 적용할 수 있습니다.
설계 개요
- 대상 자원 — 디바이스 로컬 메모리(VRAM, LLC stolen, HBM 등). 시스템 RAM은 기존
memorycgroup이 그대로 담당합니다. - 의미론(semantics) —
memorycgroup과 동일한min·low·max4쌍 제어(용량·우선 보호 포함)를 사용합니다. - 계층 구조 — cgroup 트리 계층을 따라 상속·최대값 제약이 적용되며, systemd 슬라이스 단위로 할당 가능합니다.
- EVICT 방지 —
min/low로 보호한 메모리는 TTM이 가능한 한 이주(Eviction)하지 않아 VRAM pinned 버퍼의 흔들림을 줄입니다.
cgroup v2 인터페이스
# cgroup v2 트리에서 dmem 컨트롤러 활성화 (커널 CONFIG_CGROUP_DMEM=y)
$ mount -t cgroup2 none /sys/fs/cgroup
$ echo "+dmem" > /sys/fs/cgroup/cgroup.subtree_control
# 게임 세션용 cgroup 생성 — VRAM 최대 6GB, 최소 2GB 보장
$ mkdir /sys/fs/cgroup/gaming.slice
$ echo "card0 2G" > /sys/fs/cgroup/gaming.slice/dmem.min
$ echo "card0 6G" > /sys/fs/cgroup/gaming.slice/dmem.max
# 현재 사용량·이벤트 조회
$ cat /sys/fs/cgroup/gaming.slice/dmem.current
card0 4294967296
card0-gtt 536870912
$ cat /sys/fs/cgroup/gaming.slice/dmem.events
max 0
oom 0
TTM 연동 — 드라이버 관점
TTM 기반 드라이버는 BO 생성 시 ttm_resource_manager에 charge 훅을 걸어둡니다.
ttm_resource_alloc() 성공 경로에서 dmem_cgroup_try_charge()를 호출하여 쿼터를 소모하고,
해제·이주 시 dmem_cgroup_uncharge()로 반환합니다. Intel Xe 드라이버는 커널 6.14에서 이 연동을 기본 지원하며,
amdgpu·panthor·xe는 TTM 경로만 변경하여 손쉽게 편입 가능합니다.
/* include/linux/cgroup_dmem.h — 6.14 */
struct dmem_cgroup_pool_state;
int dmem_cgroup_try_charge(struct dmem_cgroup_pool_state *pool,
u64 size,
struct dmem_cgroup_pool_state **ret_limit_pool);
void dmem_cgroup_uncharge(struct dmem_cgroup_pool_state *pool, u64 size);
/* drivers/gpu/drm/xe/xe_ttm_vram_mgr.c — 간단화 */
static int xe_ttm_vram_mgr_alloc(struct ttm_resource_manager *man,
struct ttm_buffer_object *bo,
const struct ttm_place *place,
struct ttm_resource **res)
{
struct xe_ttm_vram_mgr *mgr = to_xe_ttm_vram_mgr(man);
int ret;
ret = dmem_cgroup_try_charge(mgr->dmem_pool,
bo->base.size,
&bo->limit_pool);
if (ret)
return ret; /* -ENOSPC: cgroup 쿼터 초과 */
ret = drm_buddy_alloc_blocks(...); /* 실제 VRAM 할당 */
if (ret) {
dmem_cgroup_uncharge(bo->limit_pool, bo->base.size);
return ret;
}
return 0;
}
활용 사례
| 시나리오 | DMEM cgroup 활용 |
|---|---|
| Steam/Proton 게임 + 스트리밍 인코더 공존 | 게임 slice에 dmem.max로 VRAM 상한을 두어 인코더 OOM 회피 |
| Kubernetes GPU 워커 노드 | Pod cgroup마다 VRAM 쿼터 지정 — GPU 메모리 누수 Pod 격리 |
| Steam Deck compositor 보호 | gamescope의 compositor cgroup에 dmem.min으로 스캔아웃 버퍼 예약 |
| ML 학습 공유 서버 | 연구원별 slice로 VRAM을 공정 분배, 우선순위 프로젝트에 dmem.low |
gtt(Graphics Translation Table) 영역은 별도로 분류하므로,
통합 메모리 아키텍처(SoC iGPU)에서는 VRAM 대신 GTT pool을 정의해 쿼터화합니다.
cgroup v1은 지원하지 않고 v2에서만 노출됩니다.
GPU 전원 관리(Power Management)
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별 타이머(Timer) 설정. 만료 시 timedout_job 콜백 |
대부분의 현대 드라이버 |
| 하드웨어 워치독 | GPU 내부 워치독 타이머가 hang 감지 후 인터럽트(Interrupt) 발생 | 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 디버깅(Debugging)
디버깅 도구
| 도구 | 용도 | 사용법 |
|---|---|---|
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에서 엔진 상태 덤프(Dump) - amdgpu:
/sys/kernel/debug/dri/0/amdgpu_gpu_recover로 수동 GPU 리셋 - GPU coredump: 일부 드라이버는
devcoredump프레임워크로 GPU 상태 덤프 생성 (/sys/class/devcoredump/)
GPU 성능 프로파일링(Profiling)
| 도구 | 대상 GPU | 측정 항목 | 사용 예 |
|---|---|---|---|
intel_gpu_top |
Intel | 엔진 사용률, 주파수, 전력 | intel_gpu_top -l |
radeontop |
AMD | 파이프라인 사용률, VRAM 사용량 | radeontop -d - |
nvtop |
AMD/Intel/NVIDIA | 프로세스별 GPU 사용률 (htop 스타일) | nvtop |
gpu_metrics |
AMD (sysfs) | 온도, 전력, 주파수, 팬 속도 | cat /sys/class/drm/card0/device/gpu_metrics |
| i915 perf | Intel | EU 효율, 메모리 대역폭(Bandwidth), 캐시(Cache) 히트율 | intel_gpu_frequency -g |
| ftrace | 모든 DRM | drm_vblank, atomic_commit 이벤트 추적 | trace-cmd record -e drm |
# 프레임 드롭 디버깅 — vblank 이벤트 추적
echo 1 > /sys/kernel/debug/tracing/events/drm/drm_vblank_event/enable
echo 1 > /sys/kernel/debug/tracing/events/drm/drm_vblank_event_delivered/enable
cat /sys/kernel/debug/tracing/trace_pipe
# 출력: vblank 시퀀스 번호와 타임스탬프 → 누락된 프레임 확인
# Atomic commit 성능 추적
echo 1 > /sys/kernel/debug/tracing/events/drm/drm_atomic_state/enable
# commit 소요 시간, 포함된 CRTC/Plane 수 확인
# GPU 메모리 사용량 상세 (드라이버별 debugfs)
# amdgpu:
cat /sys/kernel/debug/dri/0/amdgpu_gem_info # 프로세스별 GEM 사용량
cat /sys/kernel/debug/dri/0/amdgpu_vram_mm # VRAM 할당 맵
cat /sys/kernel/debug/dri/0/amdgpu_vm_info # GPU 가상 메모리 통계
# i915:
cat /sys/kernel/debug/dri/0/i915_gem_objects # GEM 객체 통계
cat /sys/kernel/debug/dri/0/i915_frequency_info # GPU 주파수 정보
참고자료
drivers/gpu/drm/drm_gem*.c— GEM 프레임워크drivers/gpu/drm/ttm/— TTM (Translation Table Manager)drivers/dma-buf/— DMA-BUF 프레임워크drivers/gpu/drm/scheduler/— GPU 스케줄러drivers/gpu/drm/drm_gpuvm.c— GPUVMinclude/drm/— DRM 헤더 파일Documentation/gpu/— 커널 공식 GPU 문서
- DRM Memory Management — GEM, PRIME, VRAM helper, TTM, GPUVA
- DMA-BUF Sharing API — GPU 간 버퍼 공유 프레임워크
- DRM Internals — DRM 코어 내부 API
- Linux GPU Driver Developer's Guide — Introduction
- amdgpu Driver — AMD GPU 드라이버 내부 구조, IP 블록, 전원 관리
- i915 Driver — Intel GPU 드라이버, GuC/HuC, Xe 마이그레이션
- xe Driver — Intel Xe GPU 차세대 드라이버 아키텍처
- Mesa 3D Graphics Library — 오픈소스 GPU 유저스페이스 드라이버
- IGT GPU Tools — DRM/KMS 드라이버 테스트 스위트
- Fence 시그널링 규칙 —
dma_fence는 반드시 유한 시간 내에 시그널되어야 합니다. 무한 대기 fence는 시스템 전체를 교착시킬 수 있습니다 - 메모리 매핑 주의 — GPU VRAM을 WC(Write-Combining)로 매핑할 때 캐시 일관성(Cache Coherency)에 주의.
ioremap_wc()사용 - GPU 리셋 격리 — 하나의 컨텍스트 행이 다른 컨텍스트에 영향을 주지 않도록 per-engine 또는 per-context 리셋 구현 권장
관련 문서
이 주제와 관련된 다른 문서를 더 깊이 이해하고 싶다면 다음을 참고하세요.