요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
*delay() busy-wait의 오차와 한계
delay_sleep_functions.rst:63-80ndelay, udelay와 mdelay는 calibrated loop 또는 architecture timer를 돌며 CPU를 계속 점유합니다. 아주 짧은 hardware settling time에는 필요하지만 긴 duration은 scheduler latency와 전력을 악화시킵니다.
udelay가 요청보다 일찍 끝날 수 있는 원인에는 loops_per_jiffy 계산 오차, cache behavior, CPU frequency 변화와 loop instruction 실행 시간이 있습니다. mdelay는 큰 값에서 udelay argument overflow를 피하는 wrapper이지만 긴 busy-wait를 권장한다는 뜻은 아닙니다.
usleep_range와 fsleep·msleep
delay_sleep_functions.rst:81-122usleep_range(min, max)는 scheduler가 min 이후 max 안에서 wakeup을 다른 timer와 합칠 수 있게 하여 정확도와 전력을 조정합니다. 정확한 한 점보다 hardware가 허용하는 range를 전달해야 timer coalescing 기회가 생깁니다.
msleep은 jiffy resolution 때문에 짧은 값에서 큰 상대 오차가 생길 수 있습니다. fsleep은 요청 duration에 따라 udelay, usleep_range 또는 msleep 계열을 선택해 caller가 경계값을 직접 관리하는 부담을 줄입니다.
/* 20~30 us 사이면 허용 */
usleep_range(20, 30);
/* 범위가 동적으로 크게 변할 수 있음 */
fsleep(delay_us);
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: GPL-2.0
Delay and sleep mechanisms
==========================
This document seeks to answer the common question: "What is the
RightWay (TM) to insert a delay?"
This question is most often faced by driver writers who have to
deal with hardware delays and who may not be the most intimately
familiar with the inner workings of the Linux Kernel.
The following table gives a rough overview about the existing function
'families' and their limitations. This overview table does not replace the
reading of the function description before usage!
.. list-table::
:widths: 20 20 20 20 20
:header-rows: 2
* -
- `*delay()`
- `usleep_range*()`
- `*sleep()`
- `fsleep()`
* -
- busy-wait loop
- hrtimers based
- timer list timers based
- combines the others
* - Usage in atomic Context
- yes
- no
- no
- no
* - precise on "short intervals"
- yes
- yes
- depends
- yes
* - precise on "long intervals"
- Do not use!
- yes
- max 12.5% slack
- yes
* - interruptible variant
- no
- yes
- yes
- no
A generic advice for non atomic contexts could be:
#. Use `fsleep()` whenever unsure (as it combines all the advantages of the
others)
#. Use `*sleep()` whenever possible
#. Use `usleep_range*()` whenever accuracy of `*sleep()` is not sufficient
#. Use `*delay()` for very, very short delays
Find some more detailed information about the function 'families' in the next
sections.
`*delay()` family of functions
------------------------------
These functions use the jiffy estimation of clock speed and will busy wait for
enough loop cycles to achieve the desired delay. udelay() is the basic
implementation and ndelay() as well as mdelay() are variants.
These functions are mainly used to add a delay in atomic context. Please make
sure to ask yourself before adding a delay in atomic context: Is this really
required?
.. kernel-doc:: include/asm-generic/delay.h
:identifiers: udelay ndelay
.. kernel-doc:: include/linux/delay.h
:identifiers: mdelay
`usleep_range*()` and `*sleep()` family of functions
----------------------------------------------------
These functions use hrtimers or timer list timers to provide the requested
sleeping duration. In order to decide which function is the right one to use,
take some basic information into account:
#. hrtimers are more expensive as they are using an rb-tree (instead of hashing)
#. hrtimers are more expensive when the requested sleeping duration is the first
timer which means real hardware has to be programmed
#. timer list timers always provide some sort of slack as they are jiffy based
The generic advice is repeated here:
#. Use `fsleep()` whenever unsure (as it combines all the advantages of the
others)
#. Use `*sleep()` whenever possible
#. Use `usleep_range*()` whenever accuracy of `*sleep()` is not sufficient
First check fsleep() function description and to learn more about accuracy,
please check msleep() function description.
`usleep_range*()`
~~~~~~~~~~~~~~~~~
.. kernel-doc:: include/linux/delay.h
:identifiers: usleep_range usleep_range_idle
.. kernel-doc:: kernel/time/sleep_timeout.c
:identifiers: usleep_range_state
`*sleep()`
~~~~~~~~~~
.. kernel-doc:: kernel/time/sleep_timeout.c
:identifiers: msleep msleep_interruptible
.. kernel-doc:: include/linux/delay.h
:identifiers: ssleep fsleep
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
Delay와 sleep 방법 선택
1-15이 문서는 코드에 delay를 넣는 올바른 방법이 무엇인지라는 흔한 질문에 답한다. 이 문제는 hardware delay를 처리해야 하지만 Linux kernel 내부 동작에는 익숙하지 않을 수 있는 driver 작성자가 특히 자주 마주한다.
아래 표는 사용할 수 있는 function family와 각 방식의 제약을 대략 비교한다. 이 표는 빠른 선택을 돕기 위한 것이며 실제 사용 전에 각 function 설명을 읽는 절차를 대신하지 않는다.
Function family 비교
17-50| 비교 항목 | *delay() | usleep_range*() | *sleep() | fsleep() |
|---|---|---|---|---|
| 구현 기반 | busy-wait loop | hrtimer | timer list timer | 다른 방식을 조합 |
| atomic context | 가능 | 불가능 | 불가능 | 불가능 |
| 짧은 구간의 정밀도 | 정밀함 | 정밀함 | 조건에 따라 다름 | 정밀함 |
| 긴 구간의 정밀도 | 사용 금지 | 정밀함 | 최대 12.5% slack | 정밀함 |
| interruptible variant | 없음 | 있음 | 있음 | 없음 |
Non-atomic context의 일반 선택 순서
52-61- 확신이 없다면 다른 방식의 장점을 조합한 fsleep()을 사용한다.
- 가능하면 *sleep() family를 사용한다.
- *sleep()의 정밀도가 충분하지 않을 때 usleep_range*()를 사용한다.
- 아주 짧은 delay에만 *delay()를 사용한다.
각 function family의 세부 동작과 제한은 이어지는 절에서 설명한다.
*delay() family
63-78이 function은 추정된 clock speed와 jiffy 값을 사용해 요청한 시간이 지날 만큼 loop를 반복하며 busy-wait한다. udelay()가 기본 구현이고 ndelay()와 mdelay()는 단위와 범위를 달리한 variant다.
주된 사용 목적은 sleep할 수 없는 atomic context에서 delay를 만드는 것이다. Atomic context에 delay를 추가하기 전에는 정말 필요한지 먼저 확인해야 한다.
.. kernel-doc:: include/asm-generic/delay.h
:identifiers: udelay ndelay
.. kernel-doc:: include/linux/delay.h
:identifiers: mdelay
usleep_range*()와 *sleep() family
81-101이 function은 hrtimer 또는 timer list timer를 사용해 요청한 기간 동안 task를 sleep 상태로 둔다. 적절한 function을 선택할 때는 세 가지 성질을 고려한다.
- hrtimer는 hash 기반 timer list 대신 red-black tree를 사용하므로 비용이 더 크다.
- 요청한 sleep이 가장 먼저 만료될 timer라서 실제 hardware timer를 다시 programming해야 할 때 hrtimer 비용이 더 커진다.
- timer list timer는 jiffy 기반이므로 항상 어느 정도 slack을 제공한다.
선택 순서는 다시 말해 확신이 없으면 fsleep(), 가능하면 *sleep(), *sleep()의 정밀도가 부족하면 usleep_range*()다. 먼저 fsleep()의 function 설명을 확인하고 정밀도에 관한 자세한 내용은 msleep() 설명을 확인한다.
Kernel-doc 원본 위치
104-121.. kernel-doc:: include/linux/delay.h
:identifiers: usleep_range usleep_range_idle
.. kernel-doc:: kernel/time/sleep_timeout.c
:identifiers: usleep_range_state
.. kernel-doc:: kernel/time/sleep_timeout.c
:identifiers: msleep msleep_interruptible
.. kernel-doc:: include/linux/delay.h
:identifiers: ssleep fsleep
usleep_range(), usleep_range_idle(), ssleep(), fsleep()의 선언과 설명은 include/linux/delay.h에 있고, usleep_range_state(), msleep(), msleep_interruptible()의 구현 문서는 kernel/time/sleep_timeout.c에서 가져온다.
먼저 atomic context인지 판단한다
delay_sleep_functions.rst:3-62Delay API 선택의 첫 질문은 현재 code가 sleep할 수 있는가입니다. Hardirq, softirq, spinlock 또는 raw spinlock 보유, preemption disabled 문맥에서는 scheduler에 CPU를 넘길 수 없어 busy-wait 계열만 사용할 수 있습니다. Process context라면 대부분 sleep 계열이 CPU와 전력 면에서 낫습니다.
Hardware register가 바뀔 때까지 기다린다면 고정 delay보다 read_poll_timeout 계열처럼 condition과 timeout을 함께 표현하는 방법을 우선합니다.