GPU 메모리 관리 및 스케줄러

Linux GPU 서브시스템의 메모리 관리와 작업 스케줄링을 심층 분석합니다. GEM(Graphics Execution Manager)과 TTM(Translation Table Manager) 메모리 관리, DMA-BUF 기반 디바이스 간 버퍼 공유, GPU 커맨드 서브미션과 fence 동기화, drm_sched 스케줄러, GPUVM 가상 메모리, 전원·클럭 관리, GPU 리셋 및 복구, 커널 설정과 디버깅 방법을 다룹니다.

전제 조건: GPU 서브시스템 개요 페이지에서 DRM/KMS 전체 구조를 먼저 파악하세요. 디스플레이 관련 내용은 DRM 코어 및 디스플레이 (KMS) 페이지를 참고하세요.

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);
};
dumb buffer와 일반 BO를 같은 것으로 보면 안 됩니다: 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로 가속 버퍼 생성
매핑 DRM_IOCTL_MODE_MAP_DUMBmmap() 가짜 오프셋(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 핸들 테이블과 DMA-BUF 기반 버퍼 공유 Process A (GPU 클라이언트) drm_file → object_idr (핸들 테이블) handle 1 GEM A handle 2 GEM B handle 3 GEM C drm_gem A 4MB VRAM drm_gem B 8MB shmem drm_gem C 2MB GTT PRIME_HANDLE_TO_FD (handle 2 → fd 전달) DMA-BUF fd 커널 dma_buf 객체 → drm_gem B 참조 (refcount 증가) export import Process B (디스플레이 서버) drm_file → object_idr (핸들 테이블) handle 1 GEM B' handle 2 GEM D drm_gem B 동일 객체! drm_gem D 자체 버퍼 PRIME_FD_TO_HANDLE (fd → 로컬 handle 변환) 동일한 물리 메모리 (zero-copy 공유)

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  /* 디바이스 전용 비디오 메모리 */
placement 해석: ttm_place/ttm_placement는 “이번 validate에서 허용할 위치 후보”를 설명하는 정책 객체입니다. 현재 실제 위치는 ttm_buffer_object.resource가 나타내며, TTM은 이 값과 새 placement 후보를 비교해 VRAM↔TT↔SYSTEM migration을 결정합니다.
TTM 메모리 도메인과 Eviction(퇴거) 흐름 VRAM (TTM_PL_VRAM) 디바이스 전용 비디오 메모리 BO-A 활성 (사용중) BO-B 활성 (사용중) BO-C 유휴 (LRU) BO-D 유휴 (LRU) VRAM 사용량: 90% (부족!) Eviction 발동 GTT/TT (TTM_PL_TT) GART 매핑된 시스템 메모리 BO-E GPU 접근 가능 BO-C' 퇴거됨 IOMMU/GART로 GPU가 시스템 메모리에 접근 GTT 사용량: 50% System (TTM_PL_SYSTEM) 일반 시스템 RAM BO-F CPU 전용 BO-D' 스왑 가능 GPU 직접 접근 불가 CPU mmap으로 접근 System 사용량: 30% evict migrate (접근 시) evict migrate Eviction (VRAM 부족 시 LRU 기반 퇴거) Migration (GPU 접근 시 자동 복귀) ttm_device_funcs.evict_flags 콜백으로 제어
TTM Eviction (퇴거): VRAM이 부족하면 TTM은 사용 빈도가 낮은 버퍼를 시스템 메모리로 이동시킵니다. 드라이버는 ttm_device_funcs.evict_flags 콜백으로 퇴거 정책을 설정합니다. GPU가 해당 버퍼에 접근하면 다시 VRAM으로 마이그레이션됩니다.

DMA-BUF (버퍼 공유)

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

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

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

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

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

dma_fence & dma_resv (동기화)

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

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

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

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

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

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

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 파이프라인 최적화 가능
단점 불필요한 대기 발생 가능, 최적화 어려움 유저 공간 복잡도 증가
사용 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에 제출하여 대기
 * → 완전한 파이프라인 동기화 */
Wayland Explicit Sync: 최근 Wayland compositor와 클라이언트는 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 하드웨어에 전달합니다.

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

서브미션 모델 비교

모델드라이버ioctl특징
Ring Buffer i915 (레거시) I915_GEM_EXECBUFFER2 커맨드 버퍼를 링 버퍼(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 레지스터 접근 차단 */
  • 다른 프로세스의 GPU 주소 공간(Address Space) 접근 방지 */
  • 최신 GPU는 하드웨어 격리 (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로 유저에게 반환 */
VM-Bind vs Relocation: 레거시 드라이버(i915 execbuf)는 커맨드 버퍼 내 GPU 주소를 매번 패칭(relocation)했습니다. 현대 드라이버(xe, amdgpu VM)는 유저 공간이 GPU 페이지 테이블에 직접 매핑(VM-bind)하여 커맨드 버퍼가 고정 GPU 가상 주소(Virtual Address)를 사용합니다. VM-bind 모델은 relocation 오버헤드(Overhead)를 제거하고, sparse binding(일부 영역만 매핑)을 가능하게 합니다.

GPU 스케줄러 (drm_sched)

DRM GPU 스케줄러(drivers/gpu/drm/scheduler/)는 여러 프로세스의 GPU 작업을 우선순위 기반으로 공정하게 스케줄링하고, GPU 행(hang) 감지 시 타임아웃 콜백을 통해 GPU 리셋 및 복구를 처리합니다. drm_sched_entity, drm_sched_job, drm_gpu_scheduler가 핵심 구성 요소입니다.

참고: drm_sched의 Job 라이프사이클, dma_fence 동기화, TDR(Timeout Detection Recovery), i915 GuC vs amdgpu 구현 비교 등의 상세 내용은 drm_sched (GPU Job 스케줄러) 전용 페이지를 참고하세요.

GPU 가상 메모리 (GPUVM)

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

GPUVM: 프로세스별 GPU 가상 주소 공간 Process A GPU VA 0x0001_0000_0000 → Shader Code 0x0002_0000_0000 → Vertex Buffer 0x0003_0000_0000 → Uniform Buffer ... 미매핑 영역 다수 ... 프로세스 A 전용 매핑 테이블 Process B GPU VA 0x0001_0000_0000 → Shader Code 0x0002_0000_0000 → Texture Atlas 0x0004_0000_0000 → Compute Buffer ... 다른 프로세스와 독립 ... 프로세스 B 전용 매핑 테이블 GPU MMU + Page Table Walk GPU VA → 물리 주소 변환 (per-context) TLB 캐시, 권한 검사, fault 처리 VRAM (Local Memory) 고대역폭 그래픽 메모리 렌더 타깃 / 텍스처 / 캐시 라인 디바이스 로컬 우선 배치 System Memory (GEM/TTM) 공유 버퍼 / pageable backing IOMMU 경유 DMA 접근 메모리 압박 시 migration `drm_gpuvm`: VA 범위(`mm_start`, `mm_range`) + RB 트리(`rb_root_cached`)로 매핑 관리 `drm_gpuva`: `va_start`, `va_range`, `gem_obj`, `gem_offset`로 개별 매핑 표현
IOMMU vs GPU MMU: IOMMU(VT-d/SMMU)는 디바이스의 DMA 주소를 물리 주소(Physical Address)로 변환하는 시스템 레벨 하드웨어입니다. GPU MMU는 GPU 내부의 셰이더/엔진이 사용하는 가상 주소를 물리 주소로 변환합니다. 두 계층은 독립적으로 동작하며, GPU DMA 트래픽은 IOMMU도 통과합니다.
참고: CPU와 GPU가 동일한 가상 주소 공간을 공유하는 HMM(Heterogeneous Memory Management), migrate_vma(), ZONE_DEVICE, SVM(ROCm KFD) 구현은 HMM (이기종 메모리 관리) 페이지에서 자세히 다룹니다.

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 전원 상태 전환 Active (D0) DVFS: 고성능 (900MHz/1.1V) DVFS: 중간 (600MHz/0.9V) DVFS: 저전력 (300MHz) devfreq / 드라이버 Clock Gating 미사용 블록 클럭 차단 유휴 Power Gating 미사용 블록 전원 차단 더 깊은 절전 D3hot Runtime Suspend PCIe 링크 유지 전체 유휴 D3cold 전원 완전 차단 PCIe 링크 끊김 PCIe 재연결 pm_runtime_get_sync() → 복귀 Hybrid GPU (PRIME Offload) DRI_PRIME=1 → 외장 GPU 활성화 → 렌더링 → 복사 → 내장 GPU 출력 자동 절전 (autosuspend) autosuspend_delay_ms 경과 후 자동 D3 진입 활성 경로 절전 경로 복귀 경로 전력 소비: Active(D0) > Clock Gate > Power Gate > D3hot > D3cold(최저)

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));
GPU 리셋 수준 (영향 범위 증가 →) 최소 영향 최대 영향 Level 1 Per-engine 특정 엔진만 리셋 (GFX/SDMA/Video) 다른 엔진 영향 없음 Level 2 Per-context 문제 컨텍스트만 ban (HW 지원 필요) i915: context ban Level 3 Full GPU Reset 전체 GPU 재초기화 모든 엔진/컨텍스트 중단 amdgpu_device_gpu_recover Level 4 FLR (PCIe) PCIe Function Level Reset 수행 pci_reset_function Level 5 BACO Bus Active, Chip Off GPU 전원 완전 차단/재투입 AMD 전용 리셋 전략: 1. 항상 가장 세밀한 수준(Level 1)부터 시도하여 다른 작업에 미치는 영향을 최소화합니다. 2. 실패 시 더 거친 수준으로 단계적 확대(escalation)합니다. drm_sched의 timedout_job 콜백에서 이 흐름을 구현합니다. 3. Level 5(BACO)는 최후의 수단으로, GPU 전원을 물리적으로 차단했다가 다시 투입하여 하드웨어 상태를 완전히 초기화합니다.

복구 흐름

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

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

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

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

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

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

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

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

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

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

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

커널 설정 (Kconfig)

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

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

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

# DMA-BUF
CONFIG_DMA_SHARED_BUFFER=y

DRM 디버깅(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
GPU 행(hang) 디버깅:
  • 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 효율, 메모리 대역폭, 캐시 히트율 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 — GPUVM
  • include/drm/ — DRM 헤더 파일
  • Documentation/gpu/ — 커널 공식 GPU 문서
우선적으로 볼 1차 문서:
외부 참고 링크:
GPU 메모리/스케줄러 개발 시 주의사항:
  • Fence 시그널링 규칙dma_fence는 반드시 유한 시간 내에 시그널되어야 합니다. 무한 대기 fence는 시스템 전체를 교착시킬 수 있습니다
  • 메모리 매핑 주의 — GPU VRAM을 WC(Write-Combining)로 매핑할 때 캐시 일관성(Cache Coherency)에 주의. ioremap_wc() 사용
  • GPU 리셋 격리 — 하나의 컨텍스트 행이 다른 컨텍스트에 영향을 주지 않도록 per-engine 또는 per-context 리셋 구현 권장

이 주제와 관련된 다른 문서를 더 깊이 이해하고 싶다면 다음을 참고하세요.