개발 공부/linux kernel 공부 기록

Linux 에서 Dell workstation의 쿨링팬 속도조절을 자동화 해보기

name2965 2025. 8. 27. 16:00
728x90

 

 

 

 

얼마전에 퍼징머신을 하나 장만하려고 Dell 중고서버를 구매했습니다.

 

조금 오래된 cpu가 장착되어있긴 하지만 커널 퍼징은 vcpu가 많을수록 더 많은 vm을 돌릴수 있기 때문에 개별 코어의 성능이 조금 부족해도 별 상관 없습니다.

 

 

 

 

원래는 윈도우가 설치된 메인 컴퓨터에 리눅스 VM을 하나 띄워놓고 퍼징을 했었기 때문에 항상 아쉬운 마음이 들었었는데, 새로 사온 중고서버는 vcpu가 80개나 되기 때문에 vm 하나당 2개의 vcpu를 할당해도 최대 40개의 vm으로 퍼징을 돌릴수 있게되서 정말 만족스럽습니다.

 

하지만 한가지 아쉬운것이 있는데, Dell 자체 bios에서는 쿨링팬 속도를 세밀하게 조정할수 없다는것 입니다.

 

 

 

 

 

저의 메인 컴퓨터에서는 msi 메인보드를 사용하고 있어서 bios에서 이런식으로 쿨링팬 속도를 세밀하게 조정하는것이 가능합니다. 온도에 따라서 쿨링팬을 어느정도 빠르게 돌릴것인지도 4단계로 나눠서 조정하는것이 가능했기 때문에 저는 이런 설정이 당연히 모든 컴퓨터에서 다 가능할것이라고 생각했지만 Dell은 아니였습니다.

 

 

 

 

bios 설정을 암만 뒤져봐도 쿨링팬 속도 관련 설정이 안보여서 자세히 찾아보니 Dell 자체적으로 아예 4단계 정도로 나눠놓은것 이외에는 그 어떠한 설정도 못한다는 것이였습니다.

 

 

/*
 * Call the System Management Mode BIOS. Code provided by Jonathan Buzzard.
 */
static int i8k_smm_func(void *par)
{
	struct smm_regs *regs = par;
	unsigned char carry;

	/* SMM requires CPU 0 */
	if (smp_processor_id() != 0)
		return -EBUSY;

	asm volatile("out %%al,$0xb2\n\t"
		     "out %%al,$0x84\n\t"
		     "setc %0\n"
		     : "=mr" (carry),
		       "+a" (regs->eax),
		       "+b" (regs->ebx),
		       "+c" (regs->ecx),
		       "+d" (regs->edx),
		       "+S" (regs->esi),
		       "+D" (regs->edi));

	if (carry)
		return -EINVAL;

	return 0;
}
....



혹시나 싶어서 dell hwmon 드라이버 소스코드를 분석해봤지만, 필요한 기능은 모두다 바이오스 내부에 구현되어있다는 것을 알게되었습니다.

 

그러므로 만약 정말 msi 바이오스처럼 RPM을 세밀하게 조정하고 싶으면 dell 바이오스 자체를 뜯어고쳐야 한다는건데, 난이도가 높은것도 문제이지만 만약 혹여나 잘못 건드리면 최소 며칠동안은 저의 서버를 사용하지 못할수도 있어서 이 방법은 보류해뒀습니다.

 

 

 

 

물론 Dell 바이오스 자체적으로 온도가 높으면 그에 따라 쿨링팬 속도를 조절하는 기능은 있었지만 이건 제가 원하는 온도로 직접 설정할수 있는것이 아니다보니 퍼징을 할때마다 제대로 쿨링이 되지 않고 cpu 온도가 80도를 넘기는 상태가 계속 유지되다 보니 그다지 만족스럽진 않았습니다.

 

만약 제가 혼자 살고있고 어느정도 방음이 된다고 하면 퍼징할때는 쿨링팬을 HIGH로 설정해놔도 아무 문제가 없지만, 아쉽게도 본가에서 부모님과 같이 사는 상태이고, 이 서버는 풀로드 시 6000 RPM이 넘어가는 무지막지한 쿨링팬을 가지고 있기 때문에 계속해서 풀로드를 유지하는것이 힘든 상황입니다.

 

 

그래서 이를 해결하기 위해 Dell Precision 7820의 쿨링팬 속도를 자동으로 조절하는 프로그램을 간단하게 작성해봤습니다.

 

https://github.com/name2965/dell-7820-auto-fan-control

 

GitHub - name2965/dell-7820-auto-fan-control

Contribute to name2965/dell-7820-auto-fan-control development by creating an account on GitHub.

github.com

 

 

 

 

이 프로그램은 기본적으로 리눅스에서 동작하며, /proc/i8k와 /sys/class/hwmon 아래에 dell_smm 이 등록되어있어야 합니다.

 

동작하는 방식은 다음과 같습니다.

 

- 최저 온도와 최대 온도를 입력하면 프로그램 자체적으로 2초마다 2개의 cpu 온도를 검사해서, 특정 cpu의 온도가 최대 온도 이상인 상태가 5번 관찰되면 쿨링팬을 HIGH로 설정해서 열을 빠르게 식힙니다.

- 그리고 열을 식혀서 특정 cpu의 온도가 최저 온도 미만인 상태가 5번 관찰되면 다시 LOW로 설정하는 로직으로 이루어져 있습니다.

 

 

물론 HIGH로 설정하게되면 생기는 소음은 dell 바이오스 특성상 어쩔수 없지만, 이 프로그램을 활용하면 꼭 온도를 내려야 할때에만 쿨링팬을 풀로드로 설정하고, 온도가 충분히 내려가면 다시 평상시 상태로 되돌리기 때문에 그나마 소음 문제를 줄일수 있습니다.

 

 

/*
 * Procfs interface
 */

static long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{
	struct dell_smm_data *data = pde_data(file_inode(fp));
	int __user *argp = (int __user *)arg;
	int speed, err;
	int val = 0;

	if (!argp)
		return -EINVAL;

	switch (cmd) {
	case I8K_BIOS_VERSION:
		if (!isdigit(data->bios_version[0]) || !isdigit(data->bios_version[1]) ||
	....
	case I8K_GET_TEMP:
		val = i8k_get_temp(data, 0);
		break;
 	....

https://elixir.bootlin.com/linux/v6.17-rc3/source/drivers/hwmon/dell-smm-hwmon.c#L603

 

 

그리고 이 프로그램을 만들다가 알게된 사실인데, v6.17-rc3 가 나온 시점에서 dell hwmon 드라이버에 구현되어있는 ioctl() 로는 CPU 0의 온도만 알수 있습니다.

 

아마 2cpu 이상의 환경을 고려하지 않았거나 CPU 0의 온도만 알수있으면 된다고 생각해서 그런거 같은데, 이로인해 ioctl로는 CPU 1의 온도를 알수 없기 때문에 dell_smm_read()를 활용해야합니다.

 

 


static int dell_smm_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
			 long *val)
{
	struct dell_smm_data *data = dev_get_drvdata(dev);
	int mult = data->i8k_fan_mult;
	int ret;

	switch (type) {
	case hwmon_temp:
		switch (attr) {
		case hwmon_temp_input:
			ret = i8k_get_temp(data, channel);
			if (ret < 0)
				return ret;

			*val = ret * 1000;

			return 0;
		default:
			break;
		}
		break;

https://elixir.bootlin.com/linux/v6.17-rc3/source/drivers/hwmon/dell-smm-hwmon.c#L881

 

 

이 함수는 모든 센서의 온도를 읽어와서 파일에 저장하기 때문에, 저는 이 프로그램을 작성할때 dell의 hwmon 경로에 있는 tempN_input 파일을 읽어오는 방식으로 구현했습니다.

 

하지만 ioctl() 에서 굳이 왜 온도를 읽어오는 기능만 저렇게 제한한것인지 이해가 안되기 때문에 fan의 정보를 읽어오는 기능 처럼 온도를 읽어올때도 함수인자로 원하는 센서의 id를 설정할수 있게 패치해서 메일을 보내볼 계획입니다.

 

물론 제 눈에는 dell_smm_read() 에서는 사용했던 방식을 ioctl() 에서도 사용해도 별 문제는 없어보이지만, 이렇게 구현해야만 하는 이유가 따로 있을수도 있기때문에 reject 당할수도 있습니다.

 

728x90