C/G/S/P states

intel_idle과 관련된 내용을 다루기에 앞서 P-States와 C-States에 대해서 간단히 정리하고자 한다. Intel 아키텍처 환경에서 리눅스 커널과 CPU를 알아가다보면 P/S/G/C States에 대한 내용을 접할 수 있다. 이러한 상태 값에 대해서 간단히 설명하도록 하겠다.

P-States

P-States는 작업 부하에 따라서 CPU의 전압과 클럭주파수를 조절하는 정도를 정의 한 값으로 명령어 처리(Operation)상태를 기준으로 절전 및 성능 향상을 꾀하기 위한 기법이다. 과거에는 SpeedStep이라는 기술로 소개 되었기(정확히 같은 것은 아니다) 때문에 단순히 클럭주파수를 조절해서 에너지 절약을 위한 방안으로만 치부되었는데 CPU가 연산처리를 할 때의 상태를 반영하고 있다.

예를 들어 최근 사용되는 Intel CPU의 Turbo Boost 상태는 P-State 0(P0)를 의미한다.

C-States

C-States는 CPU 내부의 특정 부분이 활성화 되거나 낮은 성능 상태로 실행될지를 반영하는 값으로 CPU에서 사용중이 아닌 부분들을 비활성화하여 전원의 효율화를 높이기 위한 상태 값이다. P-State와 다르게 C-State는 유휴(Idle)상태를 기준으로하여 평가한다. 따라서, C0는 활성화 된 일반적인 상태를 의미하며 C0 상태에서 P0~Pn 상태로 나누어서 볼 수 있다. 네할렘부터는 C6 상태가 추가되었는데 C6는 각종 작업들을 저장하고 이미 작동을 멈춘 CPU코어에 공급되는 전원을 차단하는 상태이다. 그리고, 샌디브리지에서 C7이 추가되었고 이는 C6에서 추가로 L3캐시까지 비워버린(Flush) 상태를 의미한다. (p.s 절전도 좋지만 머리 아프다 인텔 놈들아..)

G-States

Global States를 의미하며 간단히 말해 사용자가 인지할 수 있는 상태를 반영한다. G0는 동작상태 (전원 On) G1은 잠자기모드 상태 G3는 전원 Off 상태이다.

S-States

Sleep States를 의미하며 G1에서 세부적인 잠자기모드 상태를 나타낸다.

Processor Power States

앞에서 언급한 상태들을 알아보기 쉽게 도식화 하면 아래와 같다.

powerstate.jpg 이미지 출처: Intel Core i7-4790K Haswell Refresh “Devil’s Canyon” Processor Review

intel_idle

모듈 이름처럼 Idle 상태를 관리하기 위한 것으로 C-States와 관련이 있는 모듈이다. intel_idle이 사용되기 이전에는 C-States를 OS에서 관리하기 위해서 acpi_idle이란 모듈이 사용되었다. 그리고, 그 당시에 intel_idle은 EXPERIMENTAL로 커널에 포함되어 있었는데 2.6.35 버전부터로 알고 있었기 때문에 2.6.32 기반의 RHEL6에서 별로 신경쓰고 있지 않았다. (RHEL7만 신경쓰였을 뿐이지…) 그런데, 스마트한 직장동료가 RHEL6에서도 intel_idle 때문에 C-States 영향을 받고 있다고 제보하였고 이를 해결하기 위한 설정방법 등을 공유하였다. 그래서 찾아보니 RHEL6.2 기술문서부터 intel_idle.max_cstate 파라미터에 대한 내용이 언급 되어있었다.

oops.png

왜 문제가 되는 것인가?

기존 acpi_idle 모듈의 경우 C-State latency와 관련하여 정확도도 높지 않았고 기본적으로 BIOS 설정에 따라서 주어진 환경 내에서만 상태를 변경하는 정도가 고작이었으나 intel_idle의 경우 BIOS 설정에 직접적으로 개입하여 C-State를 조절하는 모듈이기 때문에 문제가 되는 것이다.

일반적으로 서버시스템의 경우 빠른 응답속도를 목표로하기 때문에 소위 Performance 모드로 통칭되는 BIOS 설정상태를 유지하여 CPU가 잠들지 않도록 하는 편인데 C-State를 커널이 개입하여 제어해 버리면 쉬고 있던 상태에서 C0 상태로 만들기 위한 시간(Wake-up time)이 소모되어 성능 저하로 이어지게 된다. 즉, 절전보다는 빠른 반응이 필요한데 이를 intel_idle 모듈이 작업 상태에 따라서 조절해 버리는 것이다. 그것도 BIOS 설정과 무관하게…

해결방법

친절하게도 Red Hat Enterprise Linux Technical Note에 아래와 같이 소개하고 있다.

intel_idle.max_cstate

A new kernel parameter, intel_idle.max_cstate, has been added to specify the maximum depth of a C-state, or to disable intel_idle and fall back to acpi_idle. For more information, refer to the /usr/share/doc/kernel-doc-<version>/Documentation/kernel-parameters.txt file.

즉, 커널 부팅 파라미터에 intel_idle.max_cstate=0 값을 설정하면 acpi_idle을 이용하도록 부팅하게 된다는 것이다.

온라인 해결방법

직장동료 Alden이 커널파라미터의 경우 리부팅의 부담이 있기 때문에 기존에 운영하는 장비도 적용 할 수 있도록 tuned의 프로파일을 이용한 설정 방법을 제안하였다.

$ tuned-adm profile latency-performance

tuned의 프로파일 중에서 latency-performance라는 프로파일이 있으며 이를 적용하면 아래 그림과 같이 기존에 C7 상태까지 떨어지던 idle 상태가 C1이하로 내려가지 않는 모습을 볼 수 있다.

  • latency-performance 적용 전 cstate-c7.png

  • latency-performance 적용 후 cstate-c1.png

예외사항 (1)

실제 장비들을 샘플링하여 테스트 해 본 결과 tuned-adm을 설정하더라도 E3/E5 계열(샌디브리지)의 CPU는 바로 적용이 되었으나 C6까지있는 네할렘 CPU들은 여전히 말을 듣지 않았다.

cstate-c6.png

그래서, /dev/cpu_dma_latency 장치(4바이트 값을 갖는 장치이다)에 직접 latency 값을 100으로 설정해 보았다.

$ exec 3> /dev/cpu_dma_latency
$ echo -ne '\0144\000\000\000' >&3

cstate-c3.png

그랬더니 C3 상태이하로 내려가지 않았다. 조금더 욕심을 내서 8로 설정했더니 C1상태 이하로 내려가지 않는 것을 확인 할 수 있었다.

cstate-c1-nehal.png

그래서 설정이 가능함에도 불구하고 왜 tuned에서 처리가 안되는지 확인해보니 cpu_dma_latency 값을 조절하는 /usr/libexec/tuned/pmqos-static.py 파일이 설치되지 않는 것을 확인 할 수 있었다.

결론은 tuned-0.2.19-13.el6 이상의 패키지를 사용해야만 cpu_dma_latency 장치를 조절하는 PMQOS스크립트 패치가 반영되어 Python 스크립트가 포함된다. 이 패키지는 RHEL6.5 이상 저장소에서 제공하고 있다.

한줄요약: RHEL6.5에 있는 tuned-0.2.19-13.el6 버전 이상을 사용해라. RHEL6.3에도 아무 문제 없이 잘 설치된다.

예외사항 (2)

tuned가 제공하는 latency-performance 프로파일에는 IO스케줄러를 deadline으로 설정하도록 되어 있다. 만약, CPU C-State 값 때문에 적용하는 것이라면 그리고 IO스케줄러를 바꿀 생각이 없다면 /etc/tune-profiles/latency-performance/ktune.sysconfig 파일을 열어 ELEVATOR 항목을 원하는 스케줄러로 수정하고 적용하도록 하자.

# ktune service configuration

# This is the ktune sysctl file.  You can comment this out to prevent ktune
# from applying its sysctl settings.
#SYSCTL="/etc/sysctl.ktune"

# Use *.conf files in the ktune configuration directory /etc/ktune.d.
#   Value: yes|no,  default: yes
# It is useful if you want to load settings from additional files. Set this to
# no if you to prevent ktune from using these additional files.
USE_KTUNE_D="yes"

# This is the custom sysctl configuration file.  Any settings in this file will
# be applied after the ktune settings, overriding them.  Comment this out to
# use only the ktune settings.
SYSCTL_POST="/etc/sysctl.conf"

# This is the I/O scheduler ktune will use.  This will *not* override anything
# explicitly set on the kernel command line, nor will it change the scheduler
# for any block device that is using a non-default scheduler when ktune starts.
# You should probably leave this on "deadline", but "as", "cfq", and "noop" are
# also legal values.  Comment this out to prevent ktune from changing I/O
# scheduler settings.
ELEVATOR="deadline"

# These are the devices, that should be tuned with the ELEVATOR
ELEVATOR_TUNE_DEVS="/sys/block/{sd,cciss,dm-,vd}*/queue/scheduler"

Thanks to Alden