# hrtimer: 늦게 처리된 주기 타이머의 다음 기한 계산하기

v6.18.37 / kernel/time/hrtimer.c

주기 타이머가 늦게 실행되었을 때 현재 시각에서 무조건 한 주기를 더하면 원래 주기가 계속 밀릴 수 있습니다. hrtimer_forward는 기존 만료 시각을 기준으로 필요한 주기 수만큼 앞으로 옮깁니다. 콜백을 밀린 횟수만큼 실행하는 함수와는 다릅니다.

## hrtimer_forward

```c

u64 hrtimer_forward(struct hrtimer *timer, ktime_t now, ktime_t interval)
{
	u64 orun = 1;
	ktime_t delta;

	delta = ktime_sub(now, hrtimer_get_expires(timer));

	if (delta < 0)
		return 0;

	if (WARN_ON(timer->state & HRTIMER_STATE_ENQUEUED))
		return 0;

	if (interval < hrtimer_resolution)
		interval = hrtimer_resolution;

	if (unlikely(delta >= interval)) {
		s64 incr = ktime_to_ns(interval);

		orun = ktime_divns(delta, incr);
		hrtimer_add_expires_ns(timer, incr * orun);
		if (hrtimer_get_expires_tv64(timer) > now)
			return orun;
		/*
		 * This (and the ktime_add() below) is the
		 * correction for exact:
		 */
		orun++;
	}
	hrtimer_add_expires(timer, interval);

	return orun;
}

```

### 1028행

```c

u64 hrtimer_forward(struct hrtimer *timer, ktime_t now, ktime_t interval)

```

타이머의 기존 만료 시각을 now보다 뒤로 전진시킵니다. interval은 반복 간격이고 반환값은 전진한 주기 수입니다.

### 1030행

```c

	u64 orun = 1;

```

이미 만료한 정상 경로에서 적어도 한 주기 전진할 기본값을 둡니다.

### 1031행

```c

	ktime_t delta;

```

현재 시각과 기존 만료 시각의 차이를 저장합니다.

### 1033행

```c

	delta = ktime_sub(now, hrtimer_get_expires(timer));

```

now에서 기존 만료 시각을 빼 지연량을 구합니다. 음수이면 아직 기한 전입니다.

### 1035행

```c

	if (delta < 0)

```

기한이 아직 오지 않았는지 확인합니다.

### 1036행

```c

		return 0;

```

아직 이르면 상태를 바꾸지 않고 0을 반환합니다.

### 1038행

```c

	if (WARN_ON(timer->state & HRTIMER_STATE_ENQUEUED))

```

정렬 큐에 등록된 타이머의 기한을 이 방식으로 바꾸려는 잘못된 사용인지 확인하고 경고합니다.

### 1039행

```c

		return 0;

```

enqueue된 상태라면 기한을 변경하지 않고 반환합니다.

### 1041행

```c

	if (interval < hrtimer_resolution)

```

요청 간격이 현재 hrtimer 해상도보다 짧은지 확인합니다.

### 1042행

```c

		interval = hrtimer_resolution;

```

표현 가능한 최소 해상도에 맞춰 interval을 올립니다.

### 1044행

```c

	if (unlikely(delta >= interval)) {

```

한 주기 이상 밀린 경우 한 번에 여러 주기를 더하는 경로로 들어갑니다. unlikely는 예상 빈도 힌트이며 분기 의미는 바뀌지 않습니다.

### 1045행

```c

		s64 incr = ktime_to_ns(interval);

```

주기를 나노초 정수로 변환해 나눗셈과 곱셈에 사용합니다.

### 1047행

```c

		orun = ktime_divns(delta, incr);

```

지연량을 주기로 나눈 정수 몫으로 지나간 주기 수를 구합니다.

### 1048행

```c

		hrtimer_add_expires_ns(timer, incr * orun);

```

그 수만큼 기존 만료 시각에 더합니다. 현재 now를 새 기준으로 덮어쓰지 않습니다.

### 1049행

```c

		if (hrtimer_get_expires_tv64(timer) > now)

```

전진한 기한이 이미 now보다 뒤인지 확인합니다. 필요한 보정 여부를 결정합니다.

### 1050행

```c

			return orun;

```

충분히 앞으로 갔다면 현재 주기 수를 반환합니다.

### 1055행

```c

		orun++;

```

기한이 아직 now 이하라면 한 주기를 더 전진할 것이므로 반환할 횟수도 하나 늘립니다.

### 1057행

```c

	hrtimer_add_expires(timer, interval);

```

한 주기를 더해 최종 만료 시각을 now보다 뒤로 옮깁니다. 지연이 한 주기 미만인 경우에도 이 줄을 사용합니다.

### 1059행

```c

	return orun;

```

전진한 주기 수를 반환합니다. 호출자는 필요하면 overrun 계상 등에 사용할 수 있습니다.

