ASPM (Active State Power Management)

PCIe 링크가 유휴 상태(Idle State)일 때 자동으로 저전력 링크 상태로 전환하는 전원 관리(Power Management) 메커니즘입니다. L0s/L1과 L1 PM Substates(L1.1/L1.2), CLKREQ# 핀과 클럭 게이팅, LTR(Latency Tolerance Reporting), Linux 커널 aspm.c 구현과 정책 설정, 드라이버 관점의 ASPM 제어·상태 확인·주의사항, 그리고 실전 진단·튜닝·트러블슈팅을 end-to-end로 다룹니다.

전제 조건: PCI / PCIe 문서에서 링크(Lane)와 링크 상태(L0/L0s/L1) 개념을 먼저 익히세요. 전원 관리 개요 문서는 ASPM이 속한 커널 전원 관리 계층(디바이스 D-State, 시스템 G/S-State, CPU C-State와의 관계)을 이해하는 데 도움이 됩니다.
일상 비유: ASPM은 심야 자동 절전 가로등과 비슷합니다. 차량(패킷(Packet))이 거의 다니지 않는 시간에는 조명(링크 전송)을 낮춰 전력을 아끼고, 차량이 진입하면 다시 밝게 켜집니다. 다만 다시 밝아지는 데 짧은 시간(복원 지연)이 걸리므로, 지연에 민감한 장비에서는 절전 혜택보다 지연 비용이 클 수 있습니다.

핵심 요약

  • ASPM — PCIe 링크가 유휴 상태일 때 하드웨어가 자동으로 저전력 링크 상태로 전환하는 표준 전원 관리 기능입니다.
  • L0s — 레인(Lane) 단위로 진입하는 저전력 상태로, 진입·복귀가 빠릅니다.
  • L1 — 링크 전체가 저전력 상태로 들어가는 것으로 전력 절감은 크지만 복귀 지연이 상대적으로 큽니다.
  • L1 PM Substates (L1.1/L1.2) — L1에서 더 깊은 절전을 제공하는 하위 상태로, CLKREQ# 핀과 LTR(Latency Tolerance Reporting) 조건이 필요합니다.
  • 정책 제어 — 커널에서 pcie_aspm= 파라미터와 sysfs 정책으로 ASPM 동작을 켜거나 끌 수 있습니다.

단계별 이해

  1. 링크 상태 구분
    L0(활성), L0s(레인 절전), L1(링크 절전)의 차이와 각 상태의 전력·지연 특성을 이해합니다.
  2. L1 하위 상태 진입 조건
    CLKREQ# 참조 클럭 제어와 LTR 메커니즘이 L1.1/L1.2 진입을 결정하는 방식을 파악합니다.
  3. 커널 정책 적용
    CONFIG_PCIEASPM, pcie_aspm= 부팅 파라미터, sysfs 정책 파일을 통해 ASPM 동작을 설정합니다.
  4. 지연 트레이드오프 검증
    NVMe·네트워크 카드처럼 지연에 민감한 장비는 lspci -vv와 카운터로 진입·복귀 지연을 확인하고 비활성화 여부를 결정합니다.

개요

ASPM(Active State Power Management)은 PCIe 링크가 트래픽이 없어 유휴(Idle) 상태일 때 하드웨어가 자동으로 링크를 저전력 상태로 전환하는 PCIe 표준 전원 관리 기능입니다. CPU가 데이터를 보내거나 받지 않는 동안 링크의 전송 레인(Lane) 단위 혹은 링크 전체를 저전력 상태로 만들어 대기 전력을 줄이는 것이 목적입니다.

ASPM은 소프트웨어(드라이버)의 개입 없이 링크 양쪽 디바이스가 협상하여 자동으로 동작합니다. 때문에 애플리케이션 코어 로직을 변경하지 않아도 노트북, 서버, 임베디드 시스템에서 유휴 전력을 상당히 줄일 수 있습니다. 다만 저전력 상태로 들어가거나 빠져나올 때 지연(Latency)이 발생하므로, 지연에 민감한 NVMe나 네트워크 카드에서는 상황에 따라 비활성화를 고려해야 합니다.

교차 참조: ASPM은 PCIe 링크 계층(Link Layer)의 전원 관리입니다. 디바이스 자체의 전원 상태(D-States), 시스템에서의 전원 관리(ACPI G/S state), CPU 절전(C-state, cpufreq)과는 다른 계층입니다. 자세한 내용은 PCI / PCIe, 전원 관리 개요, CPUIdle 문서를 참조하십시오.

ASPM vs 디바이스 전원 관리 (PCI-PM)

혼동하기 쉬운 두 메커니즘을 정리하면 다음과 같습니다.

구분ASPM (링크 레벨)PCI-PM (디바이스 레벨)
제어 대상링크의 물리적 전송 상태 (L0/L0s/L1)디바이스 펑션의 전원 상태 (D0~D3)
진입 트리거하드웨어가 유휴 감지 → 자동소프트웨어(Power Management) 콜백(Callback)이 pci_set_power_state() 호출
표준PCIe Base Spec (링크 상태)PCI PM Spec (D-states)
L1 하위 상태L1.1 (ASPM), L1.2 (ASPM)L1.1 (PCI-PM), L1.2 (PCI-PM)
전력 절감상대적으로 작음 (링크만)큼 (디바이스 로직 자체 차단)
복원 지연수 μs ~ 수십 μs수 ms (D3) 이상

실제로 이 둘은 상호 보완적으로 동작합니다. 예를 들어 디바이스가 D3hot 상태로 들어가면 링크도 L1(PCI-PM 변형) 상태로 전환될 수 있으며, 반대로 ASPM의 L1 상태는 별도의 소프트웨어 절전 없이도 링크 전력만 줄입니다. 이 문서는 ASPM(링크 레벨)에 집중합니다.

PCIe 링크는 아래와 같은 전원 상태를 가집니다. 각 상태의 전력/지연 특성과 진입 방식이 다릅니다.

상태설명진입/탈출 지연
L0완전 동작 (정상 전송)
L0s빠른 저전력, 단방향 (레인별 독립)64ns~4 μs 초과 (LNKCAP L0SEL 인코딩)
L1깊은 저전력, 양방향 링크 비활성1~64 μs (LNKCAP L1EL 인코딩, 64 μs 초과도 가능)
L1.1L1 Substate, PLL 꺼짐수십 μs (별도 광고 없음, T_POWER_ON·칩 구현 의존)
L1.2L1 Substate, 공통 모드 전압 제거수십~수백 μs (T_POWER_ON/T_COMMON_MODE 복원 추가)
L2/L3보조 전원 / 전원 차단ms 단위
주의: L0s/L1의 탈출 지연은 링크가 LnkCap의 L0SEL/L1EL(각 3비트)로 광고하며 표의 범위는 그 인코딩이 나타내는 범위입니다. L1.1/L1.2는 별도 광고가 존재하지 않아 L1SubCap의 T_POWER_ON·T_COMMON_MODE와 칩 구현에 따라 달라집니다. 정확한 환경·출처 없이 특정 수치를 절대값으로 단정하지 마십시오.
PCIe 링크 전원 상태와 L1 서브스테이트 L0 (Active) 전력 100% · 지연 0 L0s 전력 소폭 감소 · 지연 수 µs 이하 L1 (ASPM) 전력 크게 감소 · 지연 수 µs~수십 µs L1 PM Substates (Extended Capability 001Eh) L1.1 (ASPM) PLL OFF 전력 상당히 감소 · 지연 수십 µs L1.1 (PCI-PM) D3hot에 의한 진입 전력 상당히 감소 L1.2 (ASPM) PLL + 레퍼런스 클럭 OFF 전력 최대 감소 · 지연 수십~수백 µs L1.2 (PCI-PM) D3hot + 클럭 OFF 전력 최대 감소 • L1.1: PLL 정지, 전기 신호 유지. CLKREQ# 핀으로 클럭 게이팅. Common mode voltage 유지 • L1.2: PLL + 레퍼런스 클럭 모두 정지. Common mode voltage 제거. 가장 깊은 절전 (SoC 전력 도메인 차단 가능) • ASPM 변형: 하드웨어가 유휴 감지 후 자동 진입 | PCI-PM 변형: 소프트웨어(D3hot)가 트리거 • T_POWER_ON: L1.2에서 L0 복귀에 필요한 시간 (디바이스가 광고). 이 값이 워크로드 지연 예산을 초과하면 L1.2 비활성화 권장

L0s — 단방향 빠른 절전

L0s는 레인(Lane) 단위로, 전송 방향(Tx/Rx) 중 한쪽이 유휴하면 그 방향만 저전력 상태로 전환합니다. 모든 레인이 양방향으로 유휴해야만 다른 상태로 갈 수 있는 것과 달리, L0s는 방향 독립이라 짧은 유휴 구간에서도 자주 활용됩니다. 전력 절감은 크지 않지만 전이 지연(수 μs 이하)이 작아 고빈도 짧은 유휴에서 유리합니다.

L0s의 제약: L0s는 링크 대역폭(Bandwidth)을 절반으로 줄인 상태와 유사하게 동작하기 때문에, 양방향 트래픽이 빈번한 고성능 링크에서는 L0s 진입이 오히려 처리량(Throughput)을 해칠 수 있습니다. 커널의 performance 정책은 policy_to_aspm_state()에서 0을 반환하므로 L0s와 L1을 모두 비활성화하고 Clock PM까지 끕니다.

L1 — 양방향 깊은 절전

L1은 링크 전체가 유휴할 때 양방향 링크를 비활성화하는 상태입니다. 전력 절감이 L0s보다 크고 지연(수 μs~수십 μs)도 실무적으로 허용 가능한 수준이라, ASPM의 핵심 절전 상태로 널리 사용됩니다. L1 진입은 두 디바이스가 모두 L1을 지원하고 ASPM 링크 제어(Link Control) 레지스터(Register)에서 활성화되어 있을 때 가능합니다.

L1 PM Substates (L1.1 / L1.2)

PCIe 3.1에서 도입된 L1 PM Substates (Extended Capability ID 001Eh)는 L1 상태 안에서 미세하게 절전할 수 있는 하위 상태를 정의합니다. 두 가지가 있습니다.

L1 Substate에 진입하려면 CLKREQ# 핀이 물리적으로 연결되어 있어야 합니다. 서버 메인보드에서는 CLKREQ#가 연결되지 않는 경우가 많아 L1.2를 사용할 수 없는 경우가 흔합니다. 또한 양쪽 디바이스가 각 상태를 지원(광고)해야 하며, 커널이 LnkCtl2/L1SubCtl 레지스터를 통해 해당 비트를 활성화해야 합니다.

LTR (Latency Tolerance Reporting)

LTR(Latency Tolerance Reporting)은 디바이스가 자신이 허용할 수 있는 최대 서비스 지연(snoop/non-snoop 각각)을 루트 컴플렉스(Root Complex)에 보고하는 메커니즘입니다. 플랫폼은 이 값을 바탕으로 링크를 얼마나 깊은 저전력 상태(L1.1/L1.2)로 보낼지, 그리고 CPU C-state 깊이를 얼마나 허용할지 결정합니다.

커널은 pci_configure_ltr()로 DevCtl2의 LTR_EN을 설정하고 엔드포인트까지의 ltr_path를 기록합니다. LTR은 PCIe r4.0 sec 6.18에 따라 루트 컴플렉스와 중간 스위치가 모두 LTR을 지원할 때만 활성화하며, _OSC로 LTR 제어권(OSC_PCI_EXPRESS_LTR_CONTROL)을 받지 못하면 네이티브 LTR을 쓰지 않습니다.

L1.2의 전제 조건: ASPM L1.2는 LTR_L1.2_THRESHOLD 임계값 기반으로 진입하기 때문에, 루트 컴플렉스까지 LTR 경로가 없으면 커널이 L1.2를 아예 활성화하지 않습니다(aspm_l1ss_init()ltr_path 검사, PCIe r4.0 sec 5.5.4/6.18). lspci -vvv에서 L1SubCtl1:ASPM_L1.2+가 보이지 않는다면 L1SS 캡 미광고, LTR 경로 부재, 또는 정책 제약을 순서대로 의심하십시오.

예를 들어 NVMe SSD가 "읽기 요청 후 최대 50 μs까지 지연 허용"을 LTR로 보고하면, 링크 복원 시간이 LTR_L1.2_THRESHOLD 이하라면 플랫폼은 L1.2 진입을 허용합니다. 반대로 매우 낮은 LTR 값을 보고하는 저지연 디바이스는 L1 진입 자체가 제한됩니다.

L1.2 진입 조건과 전원 복원 타이밍 L1.2 진입 조건 (모두 충족 필요) L1SS Cap 광고 양쪽 포트 L1SubCap.ASPM_L1_2 비트 (다운스트림은 펑션 0 기준) LTR 경로 루트 컴플렉스까지 LTR 지원 구간 (커널 ltr_path 검사) CLKREQ# 연결 레퍼런스 클럭 게이팅 가능해야 클럭 완전 정지 가능 정책 허용 powersupersave 등 정책에서 L1.2가 aspm_capable 범위 내 양쪽 D0 상태 PCI-PM L1 서브스테이트는 양쪽 D0 필수 (PCIe r6.0 sec 5.5.4) L1.2 진입 판정과 복원 L0 L1 L1.1 L1.2 LTR_L1.2_THRESHOLD 결정 (aspm_calc_l12_info) 임계값 ≈ T_POWER_ON + T_CommonMode + ≤2μs(T_POWER_OFF) + ≥4μs(T_L1.2) T_POWER_ON: 두 포트 광고 값 중 큰 쪽, 스케일 2/10/100 μs 인코딩 T_CommonMode: PortCommonModeRestoreTime 중 큰 값 • 최근 LTR 값이 임계값 이상이면 L1.2 진입 (PCIe r6.0 sec 5.5.1) • 인코딩은 올림(round up) — L1.2를 과하게 자주 진입하지 않도록 • 복원 순서: L1.2 → L1 → L0 (클럭 재개 후 링크 재훈련) • 활성화 시 양쪽 포트에 동일한 파라미터 프로그래밍 • 복원 시간이 워크로드 지연 예산 초과 시 L1.2 비활성화 권장

하드웨어 프로토콜 상세

ASPM 관련 레지스터와 핀을 커널 개발 관점에서 정리합니다.

관련 레지스터

레지스터위치주요 비트역할
Link Capabilities (LnkCap)PCIe Cap 0x0CASPM Support (bits 11:10)디바이스가 지원하는 ASPM 수준(L0s/L1) 광고
Link Control (LnkCtl)PCIe Cap 0x10ASPM Control (bits 1:0), Clock PM Enable (bit 8)현재 ASPM 상태 설정 + CLKREQ# 기반 클럭 전원 관리 활성화
Link Capabilities 2 (LnkCap2)PCIe Cap 0x2CLTR SupportLTR 메커니즘 지원 여부
Device Control 2 (DevCtl2)PCIe Cap 0x28LTR EnableLTR 보고 활성화
L1 PM Substates Cap (L1SubCap)Ext Cap 001EhPCI-PM_L1.1/1.2, ASPM_L1.1/1.2, PortCommonModeRestoreTime, PortTPowerOnTime각 L1 Substates 지원 여부와 복원 시간 광고
L1 PM Substates Ctrl 1 (L1SubCtl1)Ext Cap 001EhASPM/PCI-PM L1.1/1.2 Enable, LTR1.2_Threshold각 L1 Substate 활성화 + LTR 기준값
L1 PM Substates Ctrl 2 (L1SubCtl2)Ext Cap 001EhT_POWER_ON, T_CommonMode복원 시간 파라미터 설정

CLKREQ# 핀과 클럭 게이팅

L1 Substates의 핵심은 레퍼런스 클럭 제어입니다. 오픈 드레인 방식의 CLKREQ# 핀은 다음 규칙으로 동작합니다.

주의할 점은 커널이 CLKREQ# 배선 여부를 직접 검사하지 않는다는 것입니다. 커널은 L1SubCap 광고와 ltr_path만으로 L1.2 활성화를 결정하므로, 보드에서 CLKREQ#가 연결되지 않은 플랫폼에서는 하드웨어가 실제로 클럭을 게이팅할 수 없어 L1.2 진입이 불안정하거나 진입하지 못합니다(서버 메인보드에서 흔한 사례이므로 BIOS/보드 스펙 확인 필요). lspci -vvvLnkCtl:ClockPM-로 표시되면 클럭 전원 관리가 비활성화된 상태입니다.

Clock PM (클럭 전원 관리)

Clock PM(Clock Power Management)은 링크가 L1/L1.1/L1.2 상태일 때 CLKREQ#를 이용해 레퍼런스 클럭(Reference Clock) 자체를 게이팅(일시 정지)하는 기능입니다. 지원 여부는 LnkCap의 ClockPM 비트(bit 18)로 광고되고, LnkCtl의 Clock PM Enable(bit 8, CLKREQ_EN)으로 활성화됩니다.

커널은 pcie_clkpm_cap_init()으로 슬롯의 모든 펑션에서 지원 여부를 확인한 뒤 pcie_set_clkpm()으로 켭니다 (clkpm_capable/clkpm_enabled/clkpm_default/clkpm_disable 상태 추적). 정책과의 관계는 policy_to_clkpm_state()에 정리되어 있습니다 — performance는 끄고, powersave/powersupersave는 켜며, default는 BIOS 상태를 유지합니다.

ASPM 협상과 활성화 절차

ASPM이 실제로 동작하기까지는 다음과 같은 협상 단계를 거칩니다.

  1. 지원 광고: 각 디바이스가 LnkCap 레지스터로 자신이 지원하는 ASPM 수준을 광고합니다.
  2. 제어권 협상: 부팅 시 루트 컴플렉스와 OS가 ACPI _OSC로 ASPM 제어권을 협상합니다. OS가 가져오면 커널이 정책을 적용하고, 펌웨어(Firmware)가 유지하면 펌웨어의 설정을 따릅니다.
  3. 정책 결정: 커널이 부트 파라미터/모듈 파라미터로 결정된 ASPM 정책에 따라 각 링크의 LnkCtl ASPM Control 비트를 설정합니다.
  4. L1 Substate 활성화: L1SubCtl1의 각 Enable 비트를 설정하고, LTR 기준값(LTR1.2_Threshold)과 복원 시간(T_POWER_ON 등)을 구성합니다.
  5. 하드웨어 자동 전환: 이후 유휴를 감지한 하드웨어가 협상된 상태로 자동 전이합니다.
ACPI _OSC와의 관계: ACPI _OSC 협상이 실패하거나 펌웨어가 제어권을 유지하면, 커널의 ASPM 정책 설정이 실제 레지스터에 반영되지 않을 수 있습니다. 이런 경우 펌웨어 설정(대개 BIOS에서 ASPM 항목)을 확인해야 합니다. ACPI 문서의 _OSC 섹션을 참조하십시오.

커널 구현

ASPM의 커널 구현은 주로 drivers/pci/pcie/aspm.c에 집중되어 있습니다. 핵심 개념부터 코드 경로까지 살펴봅니다.

Linux 커널 ASPM 초기화와 정책 적용 흐름 PCIe 열거 루트 포트/스위치 다운스트림 포트 ACPI _OSC 협상 ASPM/ClockPM 지원 선언 · 제어권 요청 OS 제어권 획득 pcie_aspm_init_link_state() 제어권 실패 OS 제어권 미획득 — BIOS 설정 유지 pcie_aspm_init_link_state() 내부 (다운스트림 포트 기준) sanity check pre-1.1(RBER) 비-PCIe 함수 cap_init L0s/L1 지원 판정 + 공통 클럭(CCC) latency check 허용 vs 탈출 지연 스위치당 +1 µs l1ss_init L1.1/L1.2 지원 + LTR 경로 검사 clkpm_cap_init Clock PM 지원/ 상태 파악 pcie_no_aspm() FADT NO_ASPM / _OSC 실패 시 경로 정책 적용: pcie_config_aspm_path() default: 초기화 즉시 적용 | powersave·supersave: pci_enable_device() 시점 런타임 재구성 policy sysfs 저장 · D-state 변화(pm_state_change) · 디바이스 제거(exit_link_state) · suspend/resume(L1SS 복원) 드라이버 연동 pci_disable_link_state()/pci_enable_link_state() aspm_disable/aspm_default 반영, 제어권 없으면 -EPERM 관찰 포인트 • dmesg: "_OSC: OS now controls [...]", "FADT indicates ASPM is unsupported", "PCIe ASPM is disabled", "disabling ASPM on pre-1.1 PCIe device" • sysfs: /sys/module/pcie_aspm/parameters/policy, /sys/bus/pci/devices/<BDF>/link/ (clkpm, l0s_aspm, l1_aspm, l1_1/1_2_aspm, ...) • lspci -vvv: LnkCap/LnkCtl의 ASPM 비트, L1SubCap/L1SubCtl1의 L1.1/L1.2 상태

커널 구성 옵션

Kconfig설명비고
CONFIG_PCIEASPMASPM 지원 여부 (bool)대부분의 배포판에서 기본 활성화
CONFIG_PCIEASPM_DEFAULT기본 정책: default플랫폼/펌웨어 기본값을 따름
CONFIG_PCIEASPM_POWERSAVE기본 정책: powersave절전 우선
CONFIG_PCIEASPM_POWERSUPERSAVE기본 정책: powersupersave최대 절전 (L1/L1.1/L1.2 모두 허용)
CONFIG_PCIEASPM_PERFORMANCE기본 정책: performance성능 우선 (L0s/L1 비활성화)

이 중 하나의 정책을 컴파일 시 기본값으로 내장할 수 있고, 부팅 시 pcie_aspm.policy=로 덮어씁니다.

ASPM 정책 (Policy)

커널은 4가지 정책을 지원하며, 이는 /sys/module/pcie_aspm/parameters/policy로 확인하고 런타임에 바꿀 수 있습니다.

정책L0sL1L1.1/L1.2용도
defaultBIOS 상태 유지BIOS 상태 유지BIOS 상태 유지BIOS/펌웨어가 켠 상태 그대로. 일반 소비자 시스템
performance비활성화비활성화비활성화저지연/고성능 필요 시 (Clock PM도 off)
powersave허용허용비활성화L0s+L1까지만. 노트북/모바일 기본 절전
powersupersave허용허용허용(L1.1/L1.2, PCI-PM 변형 포함)L1 서브스테이트까지 최대 절전
정책별 차이: performance는 L0s와 L1을 모두 비활성화합니다. L0s는 양방향 트래픽 시 링크를 절반으로 만들어 처리량을 떨어뜨릴 수 있고, L1은 지연(수 μs~수십 μs)을 추가하기 때문입니다. powersave는 L0s+L1까지만 켜며 L1 서브스테이트는 켜지 않고, powersupersave만 L1.1/L1.2(PCI-PM 변형 포함)까지 허용합니다(커널 v4.2에서 추가된 정책). NVMe SSD나 고성능 NIC 같이 지연/처리량이 중요한 디바이스에는 performance가 권장됩니다.
정책 적용 시점: 커널은 powersave/powersupersave 정책이라도 초기화 시점에는 즉시 적용하지 않습니다. 초기화 중에는 드라이버가 아직 ASPM을 끌 기회를 갖지 못하므로, 안전 수준(default)으로 두었다가 pci_enable_device()가 호출되는 시점에 pcie_aspm_powersave_config_link()로 정책을 반영합니다 (aspm.c 주석 참조).

aspm.c 핵심 코드 경로

실제 커널 소스의 핵심 함수 시그니처와 동작을 정리합니다.

/* drivers/pci/pcie/aspm.c (v6.x 기준) — 핵심 구조와 함수 */

/* 링크별 ASPM 상태 구조체: 각 상태를 7비트 비트필드로 추적 */
struct pcie_link_state {
    struct pci_dev *pdev;          /* 링크의 업스트림 컴포넌트(포트) */
    struct pci_dev *downstream;   /* 다운스트림 컴포넌트 = 펑션 0 */
    struct pcie_link_state *root;    /* 루트 포트 링크 */
    struct pcie_link_state *parent;  /* 상위 링크 (스위치 체인) */
    u32 aspm_support:7;           /* 지원 가능한 ASPM 상태 */
    u32 aspm_enabled:7;           /* 현재 활성화된 상태 */
    u32 aspm_capable:7;           /* 지연 검사 통과 후 켤 수 있는 상태 */
    u32 aspm_default:7;           /* BIOS/override 기본값 */
    u32 aspm_disable:7;           /* 드라이버/사용자가 끈 상태 */
    u32 clkpm_capable:1;          /* Clock PM 지원 여부 (LNKCAP.CLKPM) */
    u32 clkpm_enabled:1;          /* 현재 Clock PM 상태 */
};

/* 정책을 상태 비트마스크로 변환 — powersave는 L0s+L1만, */
/* powersupersave만 L1 서브스테이트(PCIE_LINK_STATE_ASPM_ALL) 포함 */
static int policy_to_aspm_state(struct pcie_link_state *link)
{
    switch (aspm_policy) {
    case POLICY_PERFORMANCE:      return 0;
    case POLICY_POWERSAVE:        return PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1;
    case POLICY_POWER_SUPERSAVE:  return PCIE_LINK_STATE_ASPM_ALL;
    case POLICY_DEFAULT:          return link->aspm_default;
    }
    return 0;
}

/* 루트 포트/스위치 다운스트림 포트에 대해 호출 (열거 직후) */
void pcie_aspm_init_link_state(struct pci_dev *pdev)
{
    /* 1. pcie_aspm_sanity_check(): pre-1.1(RBER 없음)/비-PCIe면 slot 전체 비활성 */
    /* 2. pcie_aspm_cap_init(): L0s/L1 지원 판정 + 공통 클럭 구성 */
    /* 3. aspm_l1ss_init(): L1.1/L1.2 지원 판정 (LTR 경로 검사 포함) */
    /* 4. pcie_aspm_check_latency(): 엔드포인트 허용 지연 검사 */
    /* 5. pcie_clkpm_cap_init() 후 pcie_config_aspm_path()로 정책 적용 */
}

/* L1.2 사용 조건: 양쪽 L1SS 캡 + child->ltr_path(루트까지 LTR 경로) */
static void aspm_l1ss_init(struct pcie_link_state *link)
{
    /* L1SubCap의 각 지원 비트를 양쪽 AND로 aspm_support에 반영 */
    /* LTR 경로가 없으면 ASPM_L1_2 지원 비트를 제거 (PCIe r4.0) */
    /* L1.2 지원 시 aspm_calc_l12_info()로 타이밍 파라미터 계산 */
}

/* 레지스터 반영 순서 (PCIe r6.2 sec 5.5.4/7.5.3.7): */
/* L1 먼저 비활성 → 자식부터 clear, 부모부터 set → L1SS 프로그래밍 */
static void pcie_config_aspm_link(struct pcie_link_state *link,
                              u32 state);

/* 드라이버 API: OS가 ASPM 제어권을 못 받았으면 -EPERM (경고 로그) */
int pci_disable_link_state(struct pci_dev *pdev, int state);
int pci_enable_link_state(struct pci_dev *pdev, int state);

/* 커널 파라미터: pcie_aspm=off / force, 모듈 파라미터 pcie_aspm.policy= */
static bool aspm_disabled;       /* off 또는 pcie_no_aspm() 호출 후 */
static bool aspm_force;          /* pcie_aspm=force */

드라이버는 링크 전원 관리를 원하지 않는 경우 pci_disable_link_state()를 호출해 특정 링크의 ASPM을 끌 수 있습니다. 대표적인 예가 리얼텍 r8169 드라이버입니다 — 프로브(Probe) 시 PCIE_LINK_STATE_L1을 비활성화하고(벤더가 안전하다고 플래그한 시스템 제외), Tx 타임아웃 발생 시 PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_L0S를 비활성화하며 "ASPM disabled on Tx timeout" 경고를 남깁니다.

드라이버 API와 aspm_disable/aspm_default

pci_disable_link_state()link->aspm_disable 마스크에 상태를 추가하고 정책을 재적용합니다. L1을 끄면 L1 서브스테이트도 함께 꺼집니다(pci_calc_aspm_disable_mask()가 L1SS 비트를 포함). pci_enable_link_state()aspm_disable을 건드리지 않고 aspm_default만 바꾸므로, 한 번 끈 상태는 정책 변경 외에는 되살릴 수 없습니다. OS가 ACPI _OSC로 ASPM 제어권을 얻지 못한 경우(aspm_disabled) 두 API 모두 -EPERM을 반환하고 can't disable ASPM; OS doesn't have ASPM control 경고를 출력합니다.

구현 세부: 펑션 0 제약 · 공통 클럭 · 지연 검사

펑션 0 제약: L1 PM Substates 캐퍼빌리티는 다중 펑션(Multi-function) 디바이스에서 펑션 0에만 구현된다는 규약이 있습니다. 커널의 pci_function_0()은 링크의 다운스트림 컴포넌트를 펑션 0으로 고정하며(link->downstream), 링크 상태 구조체(Struct)도 펑션 0이 제거될 때 해제됩니다(pcie_aspm_exit_link_state()). 같은 슬롯의 모든 펑션에는 동일한 ASPM 값을 프로그래밍합니다.

공통 클럭(Common Clock): pcie_aspm_configure_common_clock()은 양쪽 링크의 Slot Clock Configuration(LnkSta)을 확인해 동일하다면 LnkCtl의 Common Clock Configuration(CCC) 비트를 설정해 ASPM 탈출 지연을 줄입니다. 공통 클럭 구성은 LnkCap의 탈출 지연 값까지 바꿀 수 있으므로(PCIe r5.0 sec 7.5.3.6) 설정 후 지연을 다시 읽으며, 링크 재훈련(Retrain) 실패 시 이전 설정으로 복원합니다.

지연 예산 검사: pcie_aspm_check_latency()는 엔드포인트가 광고하는 허용 지연(LnkCap DEVCAP의 L0s/L1 필드)과 각 링크의 탈출 지연을 비교해 요구를 충족하지 못하는 방향의 상태를 aspm_capable에서 제거합니다. 스위치(Switch)를 지날 때마다 L1 탈출 지연에 1 μs가 더해지며, D0가 아닌 디바이스는 검사에서 제외됩니다.

L1.2 타이밍 파라미터와 Devicetree 속성

aspm_calc_l12_info()는 두 포트가 광고한 T_POWER_ONPortCommonModeRestoreTime 중 큰 값을 선택해 L1SubCtl2/L1SubCtl1에 프로그래밍하고, LTR_L1.2_THRESHOLD를 "T_POWER_ON + T_CommonMode + 2 μs(최대 T_POWER_OFF) + 4 μs(최소 T_L1.2)"로 계산합니다(PCIe r3.1 sec 5.5.3.3.1, Fig 5-16/5-17). 임계값 인코딩은 올림(round up)하여 L1.2에 과하게 자주 진입하지 않게 하며, 파라미터 갱신 시 L1.2 Enable 비트를 먼저 지웠다가 다시 설정하는 순서(r6.2 sec 5.5.4)를 지킵니다.

Devicetree 플랫폼에서는 aspm-no-l0s, aspm-no-l1, aspm-no-l1ss 프로퍼티(링크 포트 노드)로 펌웨어가 활성화한 기본 상태를 끌 수 있습니다. pcie_aspm_override_default_link_state()가 이를 처리하며, 하드웨어에 이미 켜져 있던 L1SS 상태를 지우고 "ASPM: default states" 로그를 남깁니다.

PCI 코어와의 상호작용

ASPM은 링크 계층 기능이므로 PCIe 포트 하위 시스템(drivers/pci/pcie/)에서 초기화됩니다. pcie_aspm_init_link_state()는 루트 포트/스위치 다운스트림 포트(링크의 업스트림 컴포넌트) 기준으로 호출되고, 디바이스 제거 시 pcie_aspm_exit_link_state()가 링크 상태를 해제하며, D-state 전환 시 pcie_aspm_pm_state_change()가 지연 검사를 다시 수행합니다. 예를 들어 디바이스가 D3hot으로 가면 링크가 L1(PCI-PM)로 전환될 수 있고, D0 복귀 시 L0로 돌아옵니다. suspend/resume 시에는 pci_save_aspm_l1ss_state()/pci_restore_aspm_l1ss_state()가 L1SS 설정을 저장·복원하며, 복원 시 L1.2를 자식부터 먼저 끄고 파라미터를 쓴 뒤 Enable 비트를 되살리는 순서를 따릅니다.

드라이버 관점: 대부분의 디바이스 드라이버는 ASPM을 직접 제어할 필요가 없습니다. 커널이 정책과 펌웨어 협상 결과를 기반으로 처리합니다. 드라이버가 개입해야 하는 경우는 ① 링크가 불안정한 하드웨어 quirk에서 ASPM을 꺼야 하거나, ② 지연/처리량이 결정적인 디바이스에서 링크 전원 관리를 명시적으로 끌 때입니다.

ASPM을 고려한 드라이버 구현

대부분의 디바이스 드라이버는 ASPM을 직접 다룰 필요가 없습니다. 커널이 정책과 펌웨어 협상 결과를 기반으로 처리하기 때문입니다. 다만 아래 세 가지 상황에서는 드라이버가 명시적으로 개입해야 합니다.

제어 원칙: 드라이버는 LnkCtl이나 L1SubCtl1 레지스터를 직접 수정하지 말고 반드시 커널 API(pci_disable_link_state() 등)를 사용해야 합니다. 커널은 링크별 상태를 aspm_enabled/aspm_disable 마스크로 추적하고 정책 재적용 시 이 값을 레지스터에 다시 쓰므로, 레지스터를 직접 바꾸면 커널의 상태 추적과 불일치해 다음 정책 적용 때 덮어써지거나 되살아납니다.

드라이버용 API와 링크 상태 상수

드라이버가 사용할 수 있는 API는 drivers/pci/pcie/aspm.c에 구현되어 있고 include/linux/pci.h에 선언되어 있습니다.

API동작비고
pci_disable_link_state(pdev, state)aspm_disable 마스크에 상태를 추가하고 해당 링크에 즉시 반영L1 포함 시 L1SS 자동 포함. 제어권 없으면 -EPERM, 링크가 없으면 -EINVAL
pci_enable_link_state(pdev, state)aspm_default를 바꿔 상태를 키는 방향으로 재적용aspm_disable에 들어간 비트는 되살리지 못함
pci_set_link_state(pdev, state)요청한 상태를 링크의 기본 ASPM 상태로 설정하고 즉시 재적용런타임 성능/절전 전환용
pcie_aspm_support_enabled()커널이 ASPM을 지원하는 환경인지 전역 판정 (bool)제어 시도 전 사전 확인용
pci_no_aspm()전역으로 ASPM이 비활성화된 상태인지 판정 (bool)aspm_disabled && !aspm_force

링크 상태 비트는 다음과 같습니다.

/* include/linux/pci.h — 드라이버가 사용하는 링크 상태 비트 */
#define PCIE_LINK_STATE_L0S    BIT(0)  /* L0s: 유휴 방향 링크 절반을 끔 */
#define PCIE_LINK_STATE_L1     BIT(1)  /* L1: 링크를 끄고 복귀 지연 발생 */
#define PCIE_LINK_STATE_CLKPM  BIT(2)  /* Clock PM: CLKREQ# 게이팅 */
#define PCIE_LINK_STATE_L1_1   BIT(3)  /* L1.1: 서브스테이트 */
#define PCIE_LINK_STATE_L1_2   BIT(4)  /* L1.2: 추가 절전 (LTR 임계값으로 진입 제어) */

#define PCIE_LINK_STATE_L1SS \
    (PCIE_LINK_STATE_L1_1 | PCIE_LINK_STATE_L1_2)
#define PCIE_LINK_STATE_ASPM_ALL \
    (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_L1SS)
L1 비활성화의 부수 효과: L1 PM Substates는 L1 위에서 동작하므로 pci_disable_link_state(pdev, PCIE_LINK_STATE_L1)만 호출해도 L1.1/L1.2가 함께 꺼집니다. pci_calc_aspm_disable_mask()가 L1SS 비트를 마스크에 포함하기 때문입니다. 반대로 "L1만 끄고 L1.2만 유지"하는 구성은 불가능합니다.

구현 패턴과 코드 예시

패턴 1 — probe에서 링크 안정성/지연 요구에 따라 해제 (r8169 스타일): 링크 전이 시 오류가 보고된 컨트롤러는 초기화 단계에서 L1을 끕니다. 리얼텔 r8169이 대표적이며, 벤더가 안전함을 인증한 시스템에서만 ASPM을 유지합니다.

/* probe: 지연/안정성 요구에 따라 ASPM 해제 */
static int mynic_probe(struct pci_dev *pdev,
                     const struct pci_device_id *id)
{
    int ret;

    ret = pci_enable_device(pdev);  /* 여기서 커널 정책(default)이 먼저 반영됨 */
    if (ret)
        return ret;

    /* L1 전이 시 Tx 타임아웃이 보고된 하드웨어: */
    /* 벤더 안전 플래그가 없으면 L1을 끈다 (L1SS도 함께 꺼짐) */
    if (!mynic_aspm_is_safe(pdev)) {
        ret = pci_disable_link_state(pdev, PCIE_LINK_STATE_L1);
        if (ret)
            dev_warn(&pdev->dev,
                     "ASPM L1 disable failed: %d (제어권 없음 또는 링크 없음)\n",
                     ret);
    }
    /* ... 초기화 계속 ... */
    return 0;
}

패턴 2 — 런타임 링크 불안정 감지 시 추가 해제: Tx 타임아웃 같은 증상이 나타나면 L0s까지 끕니다. r8169은 이 경우 "ASPM disabled on Tx timeout" 경고를 남깁니다.

/* Tx 타임아웃 발생 → 링크 불안정 의심 → L0s/L1 모두 해제 */
static void mynic_tx_timeout(struct net_device *ndev,
                           unsigned int txqueue)
{
    struct mynic_priv *priv = netdev_priv(ndev);

    if (!priv->aspm_disabled) {
        int ret = pci_disable_link_state(priv->pdev,
                PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1);
        if (!ret) {
            priv->aspm_disabled = true;
            netdev_warn(ndev, "ASPM disabled on Tx timeout\n");
        }
    }
    /* ... 재전송/링크 재설정 처리 ... */
}

패턴 3 — suspend/resume: 표준 PCI 전원 관리 경로에서는 PCI 코어가 L1SS 설정을 저장·복원합니다(pci_save_aspm_l1ss_state()/pci_restore_aspm_l1ss_state()). 자체 pm_ops를 구현하는 드라이버는 resume 콜백에서 링크가 기대한 상태인지 아래 "상태 확인" 섹션의 헬퍼로 재확인하는 것을 권장합니다.

패턴 4 — 전역 결함 하드웨어는 quirk로: 특정 드라이버에 한정되지 않고 칩 전반에 문제가 있는 경우 drivers/pci/quirks.cquirk_disable_aspm_l0s/quirk_disable_aspm_l0s_l1pcie_aspm_remove_cap() 경로로 ASPM 캡 자체를 제거합니다(인텔 82575/82598, ASMedia ASM1083/1085 등 — 트러블슈팅 섹션 참조).

드라이버 관점의 제어 흐름

probe에서의 초기 판단과 런타임에서의 재평가를 하나의 그림으로 정리하면 다음과 같습니다.

드라이버 관점의 ASPM 제어 흐름 ① 초기화 (probe) — 좌에서 우로 순차 진행 → ② 런타임 — 판단 분기 probe() 진입 pci_register_driver() 드라이버 매칭 pci_enable_device() 커널 ASPM 정책(default)이 이 시점에 먼저 반영됨 드라이버 요구 분석 안정성 · 지연 · 전력 판단 근거 수집 드라이버가 ASPM을 꺼야 하나? 불안정 · 지연 예산 초과 · 성능 우선? 런타임 링크 상태 재평가 Yes · 리스크 No · 정상 ASPM 해제 pci_disable_link_state(pdev, L0S|L1) L1 비활성 → L1.1/L1.2 함께 해제 LnkCtl ASPM 비트 클리어 한 번 끄면 정책 변경 전까지 유지 ASPM 유지 커널 정책(powersave 등)에 따름 유휴 시 L1 · L1.1/L1.2 절전 지속 드라이버 제어 없이 커널이 처리 CLKREQ# · LTR 경로 조건 충족 시 성능 · 안정성 우선 경로 L0/L0s 유지 — 링크 전이 지연 제거 실시간 · 고처리량 워크로드 절전 우선 경로 유휴 절전 지속 — 배터리 이득 모바일 · 유휴 우선 워크로드
호출 순서: 커널은 초기화 중에는 정책을 즉시 적용하지 않고(안전 수준 유지), pci_enable_device() 시점에 정책을 반영합니다. 드라이버가 ASPM을 끄려면 pci_enable_device() 이후에 호출해야 커널 정책 반영과 순서 충돌이 없습니다.

드라이버에서 ASPM 상태 확인

드라이버가 "지금 이 링크의 ASPM이 켜져 있는가"를 확인하는 방법은 커널 API, 레지스터 직접 읽기, 사용자 공간 도구로 나뉩니다. 각각 확인하는 대상이 다르므로 목적에 맞게 선택해야 합니다.

확인 방법 비교

방법확인 대상특징권장 용도
pcie_aspm_enabled(pdev)커널 추적 값 link->aspm_enabledbool 반환, EXPORT_SYMBOL_GPL드라이버 코드에서 빠른 판정
pcie_aspm_support_enabled()커널 전역 지원 여부bool 반환링크 제어 시도 전 사전 확인
pci_no_aspm()전역 비활성 판정aspm_disabled && !aspm_force대체 경로 결정 (quirk 등)
LnkCtl 직접 읽기하드웨어 레지스터 (L0s/L1/CLKREQ/CCC 비트)pci_read_config_word() 사용커널 북키핑과 펌웨어 개입 비교
L1SubCtl1 직접 읽기L1SS 확장 캡 (L1.1/L1.2/PM 비트)L1SS 캡 오프셋(Offset) 필요 (pdev->l1ss)L1 PM Substates 세부 확인
/sys/bus/pci/devices/<BDF>/link/*링크별 enabled/disabled 문자열사용자 공간, 드라이버 불필요관리·스크립트·원격 진단
lspci -vv / setpciLnkCap/LnkCtl/L1SubCtl1 원시 값권한 없이 대부분 읽기 가능휴먼 진단, 레지스터 덤프(Dump)
dmesg커널 로그 (비활성 사유 포함)초기화 시점에만 기록원인 분석의 첫 단계

북키핑과 실측의 차이: pcie_aspm_enabled()는 커널이 링크별로 추적하는 aspm_enabled 값을 반환하고, LnkCtl 직접 읽기는 하드웨어 레지스터의 현재 값을 반환합니다. 두 값이 다르면 펌웨어(SMI/BIOS가 _OSC 제어권을 유지한 경우)나 다른 에이전트가 레지스터를 바꾼 것입니다. 이때 링크 제어 API는 -EPERM으로 실패합니다.

구현 상세 예시

아래는 드라이버에 넣을 수 있는 ASPM 상태 덤프 헬퍼입니다. pdev->exp_cappdev->l1ss는 PCI 코어가 열거 시점에 캐시(Cache)한 캐퍼빌리티 오프셋입니다.

#include <linux/pci.h>
#include <linux/pci_regs.h>

/* 링크의 ASPM 상태를 로그에 남기는 드라이버 유틸리티 */
static void mydev_log_aspm_state(struct pci_dev *pdev)
{
    u16 lnkctl, lnkcap, devctl2;
    u32 l1ss_ctl1 = 0;
    int l1ss_off;

    if (!pci_is_pcie(pdev))
        return;

    /* 1) 커널 북키핑 값 */
    dev_info(&pdev->dev,
             "ASPM: kernel support=%d, this link enabled=%d\n",
             pcie_aspm_support_enabled(), pcie_aspm_enabled(pdev));

    /* 2) LnkCtl/LnkCap 직접 읽기 (하드웨어 실측) */
    pci_read_config_word(pdev, pdev->exp_cap + PCI_EXP_LNKCTL, &lnkctl);
    pci_read_config_word(pdev, pdev->exp_cap + PCI_EXP_LNKCAP, &lnkcap);
    dev_info(&pdev->dev,
             "LnkCtl=0x%04x LnkCap=0x%04x: L0s=%d L1=%d CLKREQ_EN=%d CCC=%d\n",
             lnkctl, lnkcap,
             (lnkctl & PCI_EXP_LNKCTL_ASPM_L0S) ? 1 : 0,
             (lnkctl & PCI_EXP_LNKCTL_ASPM_L1) ? 1 : 0,
             (lnkctl & PCI_EXP_LNKCTL_CLKREQ_EN) ? 1 : 0,
             (lnkctl & PCI_EXP_LNKCTL_CCC) ? 1 : 0);

    /* 3) L1 PM Substates 제어 레지스터 (L1SS 확장 캡) */
    l1ss_off = pdev->l1ss;
    if (l1ss_off) {
        pci_read_config_dword(pdev, l1ss_off + PCI_L1SS_CTL1, &l1ss_ctl1);
        dev_info(&pdev->dev,
                 "L1SS ctl1=0x%08x: ASPM_L1.1=%d ASPM_L1.2=%d"
                 " PM_L1.1=%d PM_L1.2=%d\n",
                 l1ss_ctl1,
                 (l1ss_ctl1 & PCI_L1SS_CTL1_ASPM_L1_1) ? 1 : 0,
                 (l1ss_ctl1 & PCI_L1SS_CTL1_ASPM_L1_2) ? 1 : 0,
                 (l1ss_ctl1 & PCI_L1SS_CTL1_PM_L1_1) ? 1 : 0,
                 (l1ss_ctl1 & PCI_L1SS_CTL1_PM_L1_2) ? 1 : 0);
    }

    /* 4) LTR 활성 여부 — L1.2 진입 조건 중 하나 */
    pci_read_config_word(pdev, pdev->exp_cap + PCI_EXP_DEVCTL2, &devctl2);
    dev_info(&pdev->dev, "LTR_EN=%d\n",
             (devctl2 & PCI_EXP_DEVCTL2_LTR_EN) ? 1 : 0);
}

상태를 읽은 뒤 조건부로 동작하는 예시입니다. 비활성화 성공 후 레지스터를 다시 읽어 하드웨어 반영을 확인합니다.

/* probe: 지연 예산이 부족한 워크로드면 L1을 끄고 레지스터로 재확인 */
if (mydev_requires_low_latency(pdev)) {
    int ret = pci_disable_link_state(pdev, PCIE_LINK_STATE_L1);
    u16 lnkctl;

    if (ret) {
        dev_warn(&pdev->dev, "L1 disable failed: %d\n", ret);
    } else {
        pci_read_config_word(pdev, pdev->exp_cap + PCI_EXP_LNKCTL,
                             &lnkctl);
        if (lnkctl & PCI_EXP_LNKCTL_ASPM_L1)
            dev_warn(&pdev->dev,
                     "L1 is still enabled — 펌웨어 개입 가능성!\n");
    }
}

sysfs와 사용자 공간 도구

드라이버 코드가 아닌 관리자 관점에서는 다음 방법을 사용합니다.

# 링크별 ASPM 상태 (드라이버 수정 없이)
for f in l0s_aspm l1_aspm l1_1_aspm l1_2_aspm clkpm; do
  printf "%s=%s\n" "$f" "$(cat /sys/bus/pci/devices/0000:03:00.0/link/$f)"
done

# LnkCtl의 ASPM/CLKREQ 비트와 L1SS 상태 확인
lspci -vv -s 03:00.0 | grep -E "LnkCap:|LnkCtl:|L1Sub"

# setpci로 원시 레지스터 확인 (CAP_EXP+0x10 = LnkCtl, bit0=L0s bit1=L1)
setpci -s 03:00.0 CAP_EXP+10.w
# L1SubCtl1은 lspci -vvv로 확인한 L1SS 확장 캡 오프셋 + 0x08

상태 비트 지도

드라이버가 읽는 두 레지스터의 비트 배치는 다음과 같습니다.

드라이버가 읽는 ASPM 제어 레지스터 비트 커널 북키핑이 아닌 하드웨어 실측 값 — bit 상태로 실제 ASPM 활성 여부 판독 LnkCtl — PCIe 캡 +0x10 L0s/L1 진입 허용 스위치 bit0 · L0s Enable 유휴 시 L0s 진입 허용 bit1 · L1 Enable 유휴 시 L1 진입 허용 bit8 · CLKREQ# Enable CLKREQ# 기반 클럭 제거 bit9 · CCC (공통 클럭) 고정 공통 클럭 구성 L1SubCtl1 — L1SS 확장 캡 +0x08 L1 서브상태(1.1/1.2) 세부 허용 + 복원 타이밍 bit0 · ASPM L1.1 ASPM 방식 L1.1 bit1 · ASPM L1.2 ASPM 방식 L1.2 (최심도) bit2 · PCI-PM L1.1 PCI-PM 방식 L1.1 bit3 · PCI-PM L1.2 PCI-PM 방식 L1.2 bit8~15 · CM restore time (t_cm) L1.2 공통 모드 복원 시 L1SubCtl1.bit[15:8] — 값이 클수록 L1.2 진입 지연 경계 여유 8비트 필드 • L1.2 진입 조건: LnkCtl L1 + L1SubCtl1 bit1 + DevCtl2 LTR_EN(bit10) + 양단 L1SS 캡 + LTR 경로 • pcie_aspm_enabled()는 커널 북키핑 값, 위 레지스터는 하드웨어 실측 — 둘이 다르면 펌웨어 개입 의심 • LnkCap(캡 +0x0C)의 L0s/L1 허용 지연 필드는 초기 지원 판정에 사용

ASPM 주의할 점

커널 API 사용 시 주의

플랫폼·하드웨어 주의

런타임·전원 관리 상호작용

설정과 진단

현재 정책 확인

# 현재 ASPM 정책 확인 ([]가 현재 값)
cat /sys/module/pcie_aspm/parameters/policy
# [default] performance powersave powersupersave

# 런타임에 정책 변경 (변경 즉시 모든 링크에 반영)
echo powersave > /sys/module/pcie_aspm/parameters/policy

정책 변경은 pcie_aspm_set_policy()가 전역 aspm_lock을 잡고 link_list의 모든 링크에 pcie_config_aspm_link()/pcie_set_clkpm()을 적용합니다. 단, pcie_aspm=off 또는 FADT NO_ASPM으로 aspm_disabled가 설정된 상태라면 -EPERM으로 거부됩니다(aspm_disabled 검사). "정책을 바꿔도 안 바뀐다"면 먼저 dmesg에서 비활성 사유를 확인하십시오.

전역 정책과 별개로 각 링크의 하위 컴포넌트 디바이스에는 link/ 속성 그룹이 노출됩니다(aspm_ctrl_attr_group, 그룹명 link). 해당 링크가 특정 상태를 지원(aspm_capable)할 때만 파일이 보이며, 값 1/0으로 링크 단위 aspm_disable 마스크를 조정합니다.

ls /sys/bus/pci/devices/0000:03:00.0/link/
# clkpm  l0s_aspm  l1_aspm  l1_1_aspm  l1_2_aspm  l1_1_pcipm  l1_2_pcipm

# 이 링크에서 L1만 끄기 (L1SS도 함께 꺼짐)
echo 0 > /sys/bus/pci/devices/0000:03:00.0/link/l1_aspm

# 다시 켜기 (정책이 허용하는 범위 내에서)
echo 1 > /sys/bus/pci/devices/0000:03:00.0/link/l1_aspm
속성의미
clkpmClock PM 활성 여부
l0s_aspmL0s
l1_aspmL1
l1_1_aspm / l1_2_aspmASPM 기반 L1.1 / L1.2
l1_1_pcipm / l1_2_pcipmPCI-PM 기반 L1.1 / L1.2 (디바이스 D-state와 연계)
L1SS 의존 관계: aspm_attr_store_common()은 L1을 끄면 L1SS(l1_1_aspm 등)도 함께 끄고, L1SS를 켜면 L1도 함께 켭니다. pci_calc_aspm_enable_mask()/pci_calc_aspm_disable_mask()의 "L1 PM substates require L1" 규칙 때문입니다.

부트 파라미터

# ASPM 완전 비활성화 (저지연 요구 시)
pcie_aspm=off

# ASPM 정책 지정
pcie_aspm.policy=performance
pcie_aspm.policy=powersave
pcie_aspm.policy=powersupersave
pcie_aspm.policy=default

# pre-1.1 디바이스나 FADT 비지원 환경에서도 ASPM 강제 적용
# (aspm_force가 설정되면 pcie_no_aspm()이 비활성화하지 않음)
pcie_aspm=force

# 커널 cmdline 예제 (GRUB)
# GRUB_CMDLINE_LINUX="pcie_aspm=off"
pcie_aspm=force 사용 주의: force는 펌웨어가 ASPM을 지원하지 않는 플랫폼에서도 강제로 활성화합니다. 링크가 불안정해질 수 있으며, 일부 하드웨어에서는 데이터 오류나 디바이스 무응답을 유발할 수 있습니다. 신중하게 사용하십시오.

lspci로 상태 읽기

lspci -vv는 각 링크의 ASPM 지원과 현재 상태를 보여줍니다.

# 특정 디바이스의 ASPM 관련 상태
lspci -vv -s 03:00.0 | grep -i -A 3 "LnkCap\|LnkCtl"

# 예시 출력 해석
# LnkCap: Port #0, Speed 16GT/s, Width x4, ASPM L0s L1, ...
#             → 이 디바이스는 L0s와 L1을 모두 지원
# LnkCtl: ASPM L1 Enabled; RCB 64 bytes, Disabled-CommClk+
#             → 현재 L1이 활성화되어 있음

# L1 PM Substates 상태
lspci -vvv -s 03:00.0 | grep -A 10 "L1 PM Substates"
# L1SubCap: PCI-PM_L1.2+ PCI-PM_L1.1+ ASPM_L1.2+ ASPM_L1.1+ L1_PM_Substates+
#           PortCommonModeRestoreTime=60us PortTPowerOnTime=10us
# L1SubCtl1: PCI-PM_L1.2+ PCI-PM_L1.1+ ASPM_L1.2+ ASPM_L1.1+
#            T_CommonMode=0us LTR1.2_Threshold=163840ns

# LTR 지원/활성 여부 (ASPM L1.2의 전제 조건)
lspci -vvv -s 03:00.0 | grep -E "LTR|LnkCtl2"
# DevCap2: Completion Timeout: Range AB, TimeoutDis+ LTR+ OBFF Via message/WAKE#
# DevCtl2: Completion Timeout: 50ms to 50ms, TimeoutDis- LTR+ OBFF Disabled
# Capabilities: [220 v1] Latency Tolerance Reporting
#     Max snoop latency: 3145728ns
#     Max no snoop latency: 3145728ns

LnkCtlASPM 필드는 현재 활성 상태를 표시합니다: ASPM Disabled(비활성), ASPM L0s Enabled, ASPM L1 Enabled, ASPM L0s L1 Enabled(둘 다 활성). LnkCapASPM L0s L1은 하드웨어가 광고한 지원 범위이므로, "지원(LnkCap) vs 활성(LnkCtl)"을 구분해서 읽어야 합니다. 함께 보면 유용한 항목: ClockPM+/ClockPM-(LnkCtl bit 8), CommClk+/Disabled-CommClk+(Common Clock 여부). L1SubCtl1T_CommonMode/LTR1.2_Threshold는 커널 aspm_calc_l12_info()가 양단의 L1SubCap 값을 조합해 프로그래밍한 결과입니다.

커널 로그에서 ASPM 확인

# 부팅 시 ASPM 관련 메시지
dmesg | grep -i aspm

# 출력 예시 (문구는 커널 버전에 따라 다를 수 있음)
# PCIe ASPM is disabled                        ← pcie_aspm=off 적용
# PCIe ASPM is forcibly enabled                ← pcie_aspm=force 적용
# pci 0000:00:1c.4: ASPM: Link Capabilities L0s L1 treated as
#   unsupported to avoid device defect         ← quirks.c 헤더 픽스업 적용
# acpi PNP0A08:00: _OSC: OS supports [ExtendedConfig ASPM ClockPM Segments MSI HPX-Type3]
#   → OS가 _OSC에서 ASPM/ClockPM 지원을 선언
# acpi PNP0A08:00: FADT indicates ASPM is unsupported, using BIOS configuration
#   → FADT(NO_ASPM)가 비지원 선언, 커널은 BIOS 설정을 유지하고 제어 포기
# acpi PNP0A08:00: _OSC: platform retains control of PCIe features (AE_...)  ← 플랫폼 제어

커널이 ASPM을 비활성화하는 경로는 셋입니다: ① 부트 파라미터 pcie_aspm=off, ② ACPI FADT NO_ASPM 비트, ③ _OSC 협상 실패(플랫폼이 제어권 유지). ②③은 pcie_no_aspm()을 호출하며, 이 함수는 pcie_aspm=force(aspm_force)가 설정된 경우 비활성화를 건너뜁니다. 또한 pcie_no_aspm()은 정책을 default로 되돌리고 사용자 공간(User Space)의 정책 변경도 막습니다.

튜닝과 성능

ASPM 설정은 전력 절감레이턴시/처리량 사이의 절충입니다. 워크로드에 따라 적절한 정책을 선택해야 합니다.

저지연 워크로드 (NVMe / 고성능 NIC)

NVMe SSD, RDMA, 고성능 이더넷 카드처럼 요청-응답 왕복 시간이 중요한 디바이스에서는 L1 진입 시 추가되는 지연이 성능에 영향을 줄 수 있습니다. L1 Exit Latency는 링크가 LnkCap(L1EL)으로 광고하며 디바이스에 따라 1 μs부터 64 μs(초과 인코딩 포함)까지 크게 다릅니다. 커널 pcie_aspm_check_latency()는 경로상 스위치마다 1 μs를 추가해 엔드포인트의 허용값(DevCap L1)과 비교합니다. L1.2는 별도의 Exit Latency 광고가 없어 여기에 T_POWER_ON·T_COMMON_MODE 복원 시간(수십 μs)이 더해지므로, 유휴-활성 전환이 잦은 워크로드에서는 수십~수백 μs의 추가 왕복 지연이 발생할 수 있습니다. 다음과 같이 해당 경로의 ASPM을 끄는 것이 일반적입니다.

# 방법 1: 전역 정책을 performance로 (모든 링크)
pcie_aspm.policy=performance        # 부트 파라미터
echo performance > /sys/module/pcie_aspm/parameters/policy  # 런타임

# 방법 2: 드라이버가 pci_disable_link_state() 호출 (예: r8169는 probe 시 L1 비활성화)
# 방법 3: 링크 단위 sysfs로 해제 (드라이버 수정 없이, 재부팅 시 초기화됨)
echo 0 > /sys/bus/pci/devices/0000:03:00.0/link/l1_aspm

# 확인: 해당 링크의 LnkCtl이 ASPM Disabled가 되었는지
lspci -vv -s 03:00.0 | grep "LnkCtl:"
성능 수치 표기 원칙: "ASPM L1이 NVMe 성능을 N% 저하시킨다" 같은 정확한 수치는 측정 환경(디바이스 모델, 워크로드, 링크 속도, 커널 버전)에 크게 의존합니다. 실제 측정 없이 단정하지 말고, 자신의 환경에서 fio나 벤치마크 도구로 직접 검증하는 것을 권장합니다.

절전 우선 워크로드 (노트북 / 모바일)

유휴 시간이 긴 모바일/노트북 워크로드는 절전이 우선입니다. powersupersave 정책으로 L1.1/L1.2까지 허용해 대기 전력을 최소화합니다(powersave는 L0s/L1까지만 활성화 — policy_to_aspm_state() 참조).

# 절전 최대화 (배터리 극대화)
echo powersupersave > /sys/module/pcie_aspm/parameters/policy

# L1.2 실제 동작 조건: 양단 L1SS 지원 + LTR 경로(ltr_path) + CLKREQ# 배선 + powersupersave
# L1.2 활성 여부는 lspci -vvv의 L1SubCtl1 (ASPM_L1.2+ 등)에서 확인

레이턴시 영향 측정

ASPM이 실제로 성능에 미치는 영향을 측정하려면 아래 도구를 활용할 수 있습니다.

권장 절차: ① 현재 상태 확인(lspci -vv, policy) → ② 정책 변경 → ③ 벤치마크 측정 → ④ 결과 비교로 최적 정책을 결정합니다. 전역 변경보다는 영향받는 워크로드에 맞춰 신중하게 적용하십시오.

트러블슈팅

ASPM이 활성화되지 않음

증상원인조치
lspci에서 ASPM 항목 없음펌웨어/CPU가 ASPM 미지원, 또는 정책이 performancedmesg 확인 후 pcie_aspm=force/정책 변경 검토
L1.2가 활성화 안 됨양단 L1SS 미지원(캡 미광고), LTR 경로 부재(ltr_path), 정책이 powersupersave가 아님, 펌웨어 제어 유지, CLKREQ# 미배선(배선은 커널이 검사 불가)lspci -vvv의 L1SubCap/DevCtl2(LTR)/L1SubCtl1 확인, 정책 변경, 보드 CLKREQ 배선 확인
dmesg에 "FADT indicates ASPM is unsupported, using BIOS configuration"ACPI FADT(NO_ASPM)가 ASPM 비지원 선언BIOS 설정 확인 또는 pcie_aspm=force (force면 pcie_no_aspm()이 비활성화하지 않음)
정책 쓰기가 -EPERM으로 거부됨aspm_disabled 상태 (pcie_aspm=off 또는 FADT NO_ASPM)dmesg로 비활성 사유 확인, force 옵션 또는 BIOS 설정 검토

ASPM으로 인한 링크 불안정

일부 하드웨어는 ASPM 진입 시 링크가 불안정해져 Uncorrectable Error나 디바이스 무응답이 발생할 수 있습니다. 커널 소스에서 확인된 실제 사례:

이때는 해당 링크의 ASPM을 끕니다.

# 전역으로 ASPM 완전 비활성화
pcie_aspm=off

# 또는 특정 디바이스 기준으로 정책 조정
pcie_aspm.policy=performance

# AER 오류가 난다면 함께 확인
dmesg | grep -i "aer\|aspm"

커널에는 이런 하드웨어를 위해 drivers/pci/quirks.c에 헤더 픽스업이 준비되어 있습니다. quirk_disable_aspm_l0s는 인텔 82575/82598 이더넷 컨트롤러(L0s 전이 시 데이터 손상)와 리얼텍 RTS525A에, quirk_disable_aspm_l0s_l1은 ASMedia 0x1080(ASM1083/1085), Freescale 0x0451, PASEMI 0xa002, Huawei 0x1105에 적용됩니다(L1 단독용 quirk는 존재하지 않습니다). 또한 인텔 DG2(Arc, 0x4f8x·0x569x·0x56ax)는 aspm_l1_acceptable_latency quirk로 L1 허용 지연을 unlimited(0x7)로 올려 L1 ASPM을 허용합니다. 이들은 pcie_aspm_remove_cap()을 통해 동작하며 dmesg에 "ASPM: Link Capabilities ... treated as unsupported to avoid device defect" 로그를 남깁니다. 자세한 예는 네트워크 디바이스 드라이버 문서의 ASPM 섹션을 참조하십시오.

NVMe 성능 저하

NVMe 디바이스에서 ASPM(특히 L1.2) 활성 시 대기 전력은 줄지만, 요청 간 유휴 구간에서 L1 진입/복귀 지연이 추가되어 IOPS/지연이 저하될 수 있습니다. 고성능 NVMe 배열에서는 performance 정책이나 드라이버의 ASPM 제어를 사용하십시오.

전력이 줄지 않음

정책을 powersave로 바꿔도 전력이 줄지 않는 경우가 있습니다.

참고자료

다음 학습: