리눅스를 사용하는 노트북에서 전력 소모 낮추기

English version of this article is available here

윈도우와 맥북이 노트북 시장을 장악한 가운데, 리눅스를 위한 노트북을 찾기란 모래사장에서 바늘찾는 꼴입니다.

다행이도, 최근에 들어서는 대부분의 드라이버 문제들이 해결되긴 했습니다. 윈도우에 되는 기능들은 요즘 리눅스에서도 대부분 될겁니다. 생각나는 예외는 고작 몇 가지밖에 안되네요:
- NVIDIA Proprietary 드라이버
- Windows Hello 생체 인식 드라이버

전자의 경우엔 이 글과 맞먹는 길이의 글을 쓸 수 있을 정도로 쓰레기인 상황입니다.
리눅스를 주로 사용하고 싶으신 경우, NVIDIA 그래픽을 사용하는 노트북은 무조건 피해주세요.
RuntimePM이 어느정도 고쳐진다고 하더라도, iGPU만 사용하는 노트북 수준을 따라오는 경우를 본 적이 없습니다.

항상 기억하십시오:

만약 이미 dGPU를 사용하는 노트북을 구매한 실수를 저지르셨다고 하더라도, 이 글이 어느정도 도움은 되긴 할거에요. 다만, 그럴 경우엔 조금 더 구글링을 하셔서 RuntimePM을 꼭 구동 가능하게 만드시는 걸 추천드립니다.

아래 나와있는 모든 명령어들은 관리자 권한 root로 작업해주시기 바랍니다(sudo -s나 su를 통해).

그리고, 이 글은 우분투 18.04 기준으로 작성되었습니다. 다른 우분투 버전에도 방법은 똑같지만, 만약 아예 다른 배포판을 쓰시는 경우에는 과정이 조금 다를 수 있는 점 참고 바랍니다.

각 스텝별로 재부팅을 한번씩 하셔서 적용된걸 확인하시는 걸 추천드립니다.
만약 문제가 발생할 경우, 디버깅하기 편하기도 하죠.


1. 최신 커널 설치


만약 저희가 이 글에서 건드는 드라이버들 자체에 문제가 있다면 이 글 자체가 의미가 없겠죠. 따라서 드라이버들의 집합소인 커널 자체를 최신 버전으로 업그레이드하는 것은 매우 중요한 과정입니다.

윈도우에서 드라이버 업그레이드하는 것과 같다고 생각해주시면 좋을 것 같습니다.

만약 리눅스 커널이 최신 버전이 아닐 경우, 특히 노트북 자체가 최근 기종일 경우 발생할 수 있는 문제는 아주 다양합니다.
NVMe 드라이버 유휴 상태 진입 실패로 인한 전력 소모 증가 등등..

전력 소모에서 제일 중요한 것들 중에서 Package power state라는 게 있는데, 이 놈이 조금 짜증나는 점이 있다면, 만약 노트북에서 한 장치라도 유휴 상태에 진입하길 실패하면 Package power state 통채로가 유휴 상태에 진입을 못 할 수도 있는 점을 들 수 있겠습니다.

이 글의 목표는 궁극적으로 Package power state를 최적화하는 거라고도 할 수 있을 것 같습니다.

다시 커널로 돌아와서:
리눅스 커널을 업그레이드하는 방법은 배포판마다 다릅니다.

우분투의 경우, Ukuu(Ubuntu Kernel Update Utility)를 통해 손쉽게 업그레이드를 할 수 있습니다.

OMG! Ubuntu!에서 쉬운 가이드를 올렸으니 참고 바랍니다.

-rc 릴리즈들은 테스트가 덜 된 버전들로, 설치하실 경우 시스템 자체 안정성이 하락할 수도 있으니 피하는 것을 추천드립니다.

참, 이걸 여쭤보시던 분들도 계시던데, 리눅스 커널을 업그레이드 한다는 것은 우분투를 업그레이드 한다는 것과 다른 개념입니다.
말 그래도 운영체제의 커널(핵심)을 업그레이드하는 것으로, 기능이나 UI에는 바뀌는 점이 없습니다.

2. 최신 펌웨어 설치


리눅스 커널은 오픈소스이지만, 몇몇 제조사들은 아직 장치 드라이버 소스코드를 모두 공개하는 것을 꺼리는 경우도 있습니다. 오픈소스를 우회하기 위해 펌웨어를 사용하는데요, 커널이 로드될 때 펌웨어가 같이 로드되어 코드역할을 같이 하게됩니다. 위 1번과 마찬가지로, 펌웨어도 최신 버전으로 유지하는 것 또한 중요합니다.

대부분의 배포판들은 자체적으로 펌웨어를 배포하는 방법을 갖고 있습니다. 우분투의 경우, linux-firmware 패키지가 이를 담당합니다. 하지만 모든 배포판들이 항상 최신 커널을 사용하지 않는 점과 마찬가지로, 펌웨어도 구버전일 가능성이 매우 높죠.

공식 리눅스 펌웨어 Git 저장소에서 설치하는 법을 알아보도록 합시다.
이 방법을 따라함으로써, 전에 문제가 있던 장치(Bluetooth나 Wi-Fi 등)들이 고쳐질 수도 있습니다.

cd /lib/firmware
git init
git remote add origin https://kernel.googlesource.com/pub/scm/linux/kernel/git/firmware/linux-firmware
git fetch origin # 쬐끔 걸립니다
git reset --hard origin/master
update-initramfs -u -k all


3. i915 GuC & HuC 로딩


위 방법을 따라 최신 펌웨어를 설치하면, GuC & HuC 펌웨어까지 모두 설치되게 됩니다. 요 펌웨어들은 내장 그래픽을 담당하는 i915에서 사용됩니다.
요즘 대부분의 GPU 스케쥴링과 전원 관리 코드가 이 펌웨어들로 오프로딩되었기 때문에, i915가 이 펌웨어들을 사용할 수 있게 해주는 것이 중요합니다.

방법은 간단합니다.
/etc/modprobe.d/i915.conf 파일을 생성하고 다음 한 줄을 넣습니다:
options i915 enable_guc=3

i915가 initramfs에서 로드되기 때문에
'update-initramfs -u -k all' 를 실행하여 initramfs를 재생성합니다.

재부팅 후, 'dmesg | grep i915'를 통해 GuC & HuC 로드가 되었는지 확인해보도록 합니다:
[    0.595061] [drm] Finished loading DMC firmware i915/kbl_dmc_ver1_04.bin (v1.4)
[    0.603581] [drm] HuC: Loaded firmware i915/kbl_huc_ver02_00_1810.bin (version 2.0)
[    0.615365] [drm] GuC: Loaded firmware i915/kbl_guc_ver9_39.bin (version 9.39)
[    0.625914] i915 0000:00:02.0: GuC firmware version 9.39
[    0.625916] i915 0000:00:02.0: GuC submission enabled
[    0.625918] i915 0000:00:02.0: HuC enabled
[    0.628481] [drm] Initialized i915 1.6.0 20180308 for 0000:00:02.0 on minor 0

모험 정신이 있다면, enable_psr(Panel Self Refresh)와 같은 다른 옵션을 시도해보는 것도 좋습니다.

개인적으론 "options i915 enable_guc=3 enable_psr=2"을 사용하고 있습니다.


4. TLP 설치


기본 값으로, 문제를 피하기 위해 리눅스 커널은 전원 관리 설정에 있어서 매우 보수적인 값을 사용합니다. 시스템 유틸리티인 TLP는, 좀 더 납득 가능한 전원 관리 설정을 해주기 위한 도구이고, 다양한 커뮤니티의 피드백을 받아 계속 유지보수되고 있습니다.

TLP PPA를 추가하여 최신 버전의 TLP를 설치합니다:
add-apt-repository ppa:linrunner/tlp
apt update
apt install tlp

/etc/default/tlp에 기본 TLP 설정 파일이 설치되지만, 대부분의 사용자들에겐 기본값도 충분할 거로 예상되네요.


5. BIOS 속이기: 리눅스를 윈도우로 인식하게


대부분의 BIOS에는 OS 감지 코드가 들어가 OS 별로 활성화되는 기능이 바뀌게 됩니다. BIOS 제조사들은 리눅스를 필터링하여 리눅스와 호환이 잘 되지 않는 기능들을 비활성화하는 짓거리를 하는데, 요즘 시대의 경우 이게 오히려 리눅스에서 제대로된 버그를 고치는데에 걸림돌로 작용하게 됩니다.

리눅스에 최적화된 것을을 제외한 요즘 노트북들은 그냥 리눅스를 윈도우로 속여 인식되게 하는게 더 낫습니다.

먼저 BIOS에서 인식 가능한 Windows 버전이 무엇인지 알아볼 필요가 있습니다.
다음 명령어를 사용하여 ACPI 테이블 덤프를 떠볼까요:

apt install acpica-tools iasl
acpidump > acpi.log
acpixtract acpi.log
iasl -d dsdt.dat

iasl이 DSDT 테이블을 읽기 가능한 텍스트 파일로 디컴파일을 해줄겁니다.
생성된 dsdt.dsl 파일을 텍스트 에디터로 열고, "Windows"를 검색해봅니다.
                If (_OSI ("Windows 2012"))
                {
                    OSYS = 0x07DC
                }

                If (_OSI ("Windows 2013"))
                {
                    OSYS = 0x07DD
                }

                If (_OSI ("Windows 2015"))
                {
                    OSYS = 0x07DF
                }
위처럼 나오게 될겁니다. 위 숫자들이 의미하는게 뭔지 궁금하신 경우 여길 참고해보시기 바랍니다.

제 8세대 노트북의 경우, BIOS가 Windows 10 RS1/2/3/4에 모두 동일하게 반응하네요.

그럼 리눅스를 Windows 2015로 속여보도록 하겠습니다.

리눅스 부팅 때 사용되는 cmdline를 건들면 되겠습니다. (현재 사용된 cmdline은 cat /proc/cmdline을 통해 볼 수 있습니다.)

/etc/default/grub을 열고
acpi_osi=\"Windows 2015\" acpi_osi=!
를 GRUB_CMDLINE_LINUX_DEFAULT에 추가하도록 합니다.


6. ASPM 활성화


cmdline 수정하는 겸 이거도 해보도록 하죠.

Phoronix article regarding Linux ASPM
리눅스 버전 2.6.38부터 BIOS에 ASPM 구현이 조금이라도 비표준으로 되어있는 경우, 무조건 비활성화하는 것이 기본 값으로 되었습니다. 기본 값으로는 납득 가능한 이유지만, 아쉽지만 제조사들이 BIOS를 발로 코딩하는게 흔한 시대에 이런 비표준을 사용하는 BIOS가 넘나 많습니다.

ASPM을 사용하지 않을 경우 Package C state가 PC2나 PC3에 고정될 수 있으며, 이에 따른 전력 소모가 ~40% 증가할 수도 있다고 하네요.

제 경우 리눅스한테 ASPM을 강제로 켜고 몇 년간 수 많은 노트북을 사용해왔고, 제 지인 노트북에서도 별 문제를 발견하지 못했습니다. HP Spectre 노트북 한 경우에만 Thunderbolt PM으로 인해 ASPM을 못 키는 경우 한 가지만 발견했습니다.

pcie_aspm=force pcie_aspm.policy=powersupersave
를 GRUB_CMDLINE_LINUX_DEFAULT에 다시 추가해보도록 하겠습니다.

만약 powersupersave에 문제가 있는 것 같으면 powersave를 시도해보셔도 좋습니다.


7. DRM vblank off delay 조절


수 년간, i915 드라이버는 이 값을 최소로 설정해두어도 괜찮다고 많이 알려져 왔습니다.

"drm.vblankoffdelay=1"도 GRUB_CMDLINE_LINUX_DEFAULT에 추가해보도록..

최종적으로 /etc/default/grub이 제 노트북에선 이렇게 생겼네요:




8. 미사용 USB 장치 제거


USB는 외부장치에만 사용되는 놈이 아닙니다. 웹켐, 블루투스와 같은 내부 장치에도 USB가 사용됩니다.

USB 표준에 전력 소모를 줄이는 autosuspend가 있긴 하지만, 이 놈도 비표준을 따르는 장치가 많기 때문에 아예 안 쓰는 걸 아는 경우엔 그냥 비활성화하는 것이 더 낫습니다.

'lsusb'와 'lsusb -v'를 통해 무슨 USB 장치가 사용되고 있는지 보도록 합니다. 외부 USB 장치를 빼는게 보기 더 편하겠죠?

제 경우엔 "ID 0bda:562e"이 웹캠이고, "ID 8087:0a2b"가 블루투스 어댑터네요.
둘다 화형에 처해보도록 합시다.

USB 장치를 종료/제거하기 위해선, 부팅과 절전모드 해제 시에 작동되는 스크립트를 작성해야합니다.

/etc/suspend.sh에 하나 써보도록 하죠.

#!/bin/bash

exec > /dev/kmsg 2>&1

sleep 3
find /sys -name idProduct | while read file; do
  if cat $file | grep -q '562e\|0a2b'; then
    echo Removing $(dirname $file)
    echo 1 > $(dirname $file)/remove
  fi
done

여러분이 건드셔야하는 곳은 "grep -q '562e\|0a2b'"입니다.
단순히 "\|"를 두고 계속 쭉 추가하시면 됩니다:
grep -q '0000\|1111\|2222\|3333\|4444'

위 스크립트에서 두 번째 줄은 커널 로그에 결과를 출력하란 뜻입니다. 커널 로그는 dmesg 명령어를 통해 보실 수 있습니다.

실행 권한을 주도록 합니다:
chmod 755 /etc/suspend.sh

부팅 때 실행될 수 있도록 crontab을 사용합니다:

'crontab -e' 실행 후 "@reboot /etc/suspend.sh"를 추가하도록 합니다.
crontab은 꼭 root로 실행하는게 맞는지 확인해주세요.

만약 이렇게 블랙리스트된 USB 장치가 다시 필요한 경우, 위 스크립트를 제거하고 절전모드 진입 한번 해주시면 됩니다.

인제 절전모드 해제 시에도 스크립트가 실행되기 위해 systemd 서비스를 작성해보도록 합니다.
/lib/systemd/system/laptop_suspend.service라는 새 파일을 만들고:

[Unit]
Description=Laptop suspend
Before=sleep.target
StopWhenUnneeded=yes

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStop=/etc/suspend.sh

[Install]
WantedBy=sleep.target

위 내용을 넣으시고 설치!
systemctl enable laptop_suspend

저 위 스크립트가 실행되는지는 dmesg로 확인이 가능합니다:
[10838.133311] Removing /sys/devices/pci0000:00/0000:00:14.0/usb1/1-5
[10838.296408] usb 1-5: USB disconnect, device number 2
[10838.310044] Removing /sys/devices/pci0000:00/0000:00:14.0/usb1/1-6
[10838.454298] usb 1-6: USB disconnect, device number 3

9. 안 쓰는 PCIe 장치 제거


이걸 하는 이유도 위와 동일합니다.
다만, 이번에는 package C state와 직결되므로 중요성이 강조된다고 할 수 있을거 같네요.

'lspci | grep -v 00:'를 치셔서 PCIe 장치를 보도록 합니다.
'grep -v 00:'를 하는 이유는, 00:로 시작하는 PCI 장치는 브릿지와 컨트롤러이기 때문입니다.

제 경우엔 01:00.0이랑 02:00.0이 NVMe SSD이고, 03:00.0이 microSD 리더, 04:00.0이 Wi-Fi 칩이네요.

03:00.0은 죽일 수 있겠네요. 그리고 나중에 알게 된 점인데, 이 장치가 package C state를 PC7 이상으로 진입하지 못하게 하는 원인인거 또한 알게 되었습니다.
만약 죽일만한 장치가 없는 경우(흔합니다), 그냥 이 스텝은 스킵하셔도 좋습니다.

USB보다 PCIe를 죽이는건 더 쉽습니다. 그냥 단순히 저 장치를 사용하는 드라이버 로드를 막으면 됩니다.

'lspci -v'를 통해 장치 구동에 필요한 드라이버가 무엇인지 볼 수 있습니다. 제 경우엔, rtsx_pci이네요. 'lsmod | grep rtsx_pci'를 통해 rtsx_pci가 필요로한 모듈 몇 가지를 더 볼 수 있었습니다.

저기서 나온 모든 모듈들을 /etc/modprobe.d/blacklist-rtsx.conf에 추가하여 로드를 방지할 수 있습니다.

/etc/modprobe.d에 파일을 만들고(이름 상관 없습니다), 앞에 "blacklist "라는 키워드만 붙이고 모든 모듈 명을 적어줍니다.
제 경우엔:
blacklist rtsx_pci_ms
blacklist rtsx_pci_sdmmc
blacklist rtsx_pci
를 /etc/modprobe.d/blacklist-rtsx.conf에 저장하였습니다.

저 과정 이후, initramfs를 재생성해주도록 합니다:
update-initramfs -u -k all


10. 언더볼트


따라하기 쉬운 가이드는 여기 있습니다.

CPU/GPU 전력 소모에 직결되는 놈으로, 며칠 동안 최적의 값을 찾아 보시는 것을 강력히 추천드립니다.

제가 보았을 때 대부분의 노트북 평균 값은 -0.080 mV 정도 되는 것 같습니다만
제 경우와 같이 운이 좋을 경우엔 -0.180 mV 까지도 갈 수 있습니다 :)

따라하는 법은 쉽지만 직접 시행착오로 최적의 값을 찾아야하기 때문에, 어쩌면 시간이 제일 오래걸리는 부분일 수도 있을 것 같습니다.

최적의 값을 찾은 뒤, wrmsr 명령어들은 앞서 만든 /etc/suspend.sh 스크립트에 추가해주도록 합니다(MSR 값도 절전모드 진입 시 리셋되게 됩니다).


11. PowerTOP으로 결과 확인


PowerTOP은 인텔이 제공하는 유틸리티로, 리눅스 시스템에서 전력소모를 디버깅(?)해줄 수 있게 해줍니다. Package/core C state와 총 전력 소모를 볼 수 있습니다.

PowerTOP을 설치하고 실행해보도록 합니다:
apt install powertop
powertop

PowerTOP을 통해 커널 뿐만 아니라 유저스페이스에서 어떠한 프로세스가 제일 전력을 많이 소모하는지 또한 볼 수 있습니다.

"Idle stats" 탭을 통해 package/core C state를 볼 수 있습니다.

만약 PC8(Package C state 8)에 진입을 하지 않는 경우, 힝 뭐가 잘못 되었는지 더 구글링해보셔야 할 것 같네요.

"Device stats" 탭에선 총 전력 소모를 볼 수 있습니다. 노트북을 유휴 상태로 몇 초 냅두고, 숫자를 봐주세요.

"Tunables" 탭은 TLP가 관리함으로, 가만히 냅두는 것을 추천드립니다.


12. 결과를 공유해주세요!


dGPU가 없는 경우엔 이 정도만 가지고 충분히 전력 소모를 아낄 수 있을 거 같습니다.



제 경우엔, 이 일렬의 과정 전엔 PC3까지만 진입 가능하였지만 인제 PC8까지 진입 가능해졌네요.
유휴 시 전력 소모도 5.9W에서 3.9W까지 내려갔습니다.


총 34% 개선이네요. 또한, 윈도우처럼 이상한 프로세스가 백그라운드에서 돌지 않는 리눅스이기에 윈도우보다 더 배터리가 오래가는건 보나-스 되겠습니다.

여러분의 결과도 궁금하니 댓글로 남겨주세요 :)




















Comments

  1. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. 감사합니다. 그램 13인치를 사고 이 글을 보면서 해보니 4w에서 2.6으로 절감했습니다.

      그런데 저처럼 초보일 경우에 모를 수 있겠다는 것이 grub파일을 수정한 다음에는 꼭 update-grub 해야한다는 것이네요.

      Delete
  2. 와 진짜 한줄 한줄 알아듣기 쉽고 친절하게 적어주셨네요. 가이드대로 적용해버거 시간이 나면 결과 공유 드리겠습니다. 값진 포스팅글 읽게 되어서 감사드려요~!!

    ReplyDelete

Post a Comment

Popular posts from this blog

Saving power consumption on laptops with Linux

Experimenting around btrfs on Android