요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
=========================================================
Converting old watchdog drivers to the watchdog framework
=========================================================
by Wolfram Sang <wsa@kernel.org>
Before the watchdog framework came into the kernel, every driver had to
implement the API on its own. Now, as the framework factored out the common
components, those drivers can be lightened making it a user of the framework.
This document shall guide you for this task. The necessary steps are described
as well as things to look out for.
Remove the file_operations struct
---------------------------------
Old drivers define their own file_operations for actions like open(), write(),
etc... These are now handled by the framework and just call the driver when
needed. So, in general, the 'file_operations' struct and assorted functions can
go. Only very few driver-specific details have to be moved to other functions.
Here is a overview of the functions and probably needed actions:
- open: Everything dealing with resource management (file-open checks, magic
close preparations) can simply go. Device specific stuff needs to go to the
driver specific start-function. Note that for some drivers, the start-function
also serves as the ping-function. If that is the case and you need start/stop
to be balanced (clocks!), you are better off refactoring a separate start-function.
- close: Same hints as for open apply.
- write: Can simply go, all defined behaviour is taken care of by the framework,
i.e. ping on write and magic char ('V') handling.
- ioctl: While the driver is allowed to have extensions to the IOCTL interface,
the most common ones are handled by the framework, supported by some assistance
from the driver:
WDIOC_GETSUPPORT:
Returns the mandatory watchdog_info struct from the driver
WDIOC_GETSTATUS:
Needs the status-callback defined, otherwise returns 0
WDIOC_GETBOOTSTATUS:
Needs the bootstatus member properly set. Make sure it is 0 if you
don't have further support!
WDIOC_SETOPTIONS:
No preparations needed
WDIOC_KEEPALIVE:
If wanted, options in watchdog_info need to have WDIOF_KEEPALIVEPING
set
WDIOC_SETTIMEOUT:
Options in watchdog_info need to have WDIOF_SETTIMEOUT set
and a set_timeout-callback has to be defined. The core will also
do limit-checking, if min_timeout and max_timeout in the watchdog
device are set. All is optional.
WDIOC_GETTIMEOUT:
No preparations needed
WDIOC_GETTIMELEFT:
It needs get_timeleft() callback to be defined. Otherwise it
will return EOPNOTSUPP
Other IOCTLs can be served using the ioctl-callback. Note that this is mainly
intended for porting old drivers; new drivers should not invent private IOCTLs.
Private IOCTLs are processed first. When the callback returns with
-ENOIOCTLCMD, the IOCTLs of the framework will be tried, too. Any other error
is directly given to the user.
Example conversion::
-static const struct file_operations s3c2410wdt_fops = {
- .owner = THIS_MODULE,
- .write = s3c2410wdt_write,
- .unlocked_ioctl = s3c2410wdt_ioctl,
- .open = s3c2410wdt_open,
- .release = s3c2410wdt_release,
-};
Check the functions for device-specific stuff and keep it for later
refactoring. The rest can go.
Remove the miscdevice
---------------------
Since the file_operations are gone now, you can also remove the 'struct
miscdevice'. The framework will create it on watchdog_dev_register() called by
watchdog_register_device()::
-static struct miscdevice s3c2410wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &s3c2410wdt_fops,
-};
Remove obsolete includes and defines
------------------------------------
Because of the simplifications, a few defines are probably unused now. Remove
them. Includes can be removed, too. For example::
- #include <linux/fs.h>
- #include <linux/miscdevice.h> (if MODULE_ALIAS_MISCDEV is not used)
- #include <linux/uaccess.h> (if no custom IOCTLs are used)
Add the watchdog operations
---------------------------
All possible callbacks are defined in 'struct watchdog_ops'. You can find it
explained in 'watchdog-kernel-api.txt' in this directory. start() and
owner must be set, the rest are optional. You will easily find corresponding
functions in the old driver. Note that you will now get a pointer to the
watchdog_device as a parameter to these functions, so you probably have to
change the function header. Other changes are most likely not needed, because
here simply happens the direct hardware access. If you have device-specific
code left from the above steps, it should be refactored into these callbacks.
Here is a simple example::
+static struct watchdog_ops s3c2410wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = s3c2410wdt_start,
+ .stop = s3c2410wdt_stop,
+ .ping = s3c2410wdt_keepalive,
+ .set_timeout = s3c2410wdt_set_heartbeat,
+};
A typical function-header change looks like::
-static void s3c2410wdt_keepalive(void)
+static int s3c2410wdt_keepalive(struct watchdog_device *wdd)
{
...
+
+ return 0;
}
...
- s3c2410wdt_keepalive();
+ s3c2410wdt_keepalive(&s3c2410_wdd);
Add the watchdog device
-----------------------
Now we need to create a 'struct watchdog_device' and populate it with the
necessary information for the framework. The struct is also explained in detail
in 'watchdog-kernel-api.txt' in this directory. We pass it the mandatory
watchdog_info struct and the newly created watchdog_ops. Often, old drivers
have their own record-keeping for things like bootstatus and timeout using
static variables. Those have to be converted to use the members in
watchdog_device. Note that the timeout values are unsigned int. Some drivers
use signed int, so this has to be converted, too.
Here is a simple example for a watchdog device::
+static struct watchdog_device s3c2410_wdd = {
+ .info = &s3c2410_wdt_ident,
+ .ops = &s3c2410wdt_ops,
+};
Handle the 'nowayout' feature
-----------------------------
A few drivers use nowayout statically, i.e. there is no module parameter for it
and only CONFIG_WATCHDOG_NOWAYOUT determines if the feature is going to be
used. This needs to be converted by initializing the status variable of the
watchdog_device like this::
.status = WATCHDOG_NOWAYOUT_INIT_STATUS,
Most drivers, however, also allow runtime configuration of nowayout, usually
by adding a module parameter. The conversion for this would be something like::
watchdog_set_nowayout(&s3c2410_wdd, nowayout);
The module parameter itself needs to stay, everything else related to nowayout
can go, though. This will likely be some code in open(), close() or write().
Register the watchdog device
----------------------------
Replace misc_register(&miscdev) with watchdog_register_device(&watchdog_dev).
Make sure the return value gets checked and the error message, if present,
still fits. Also convert the unregister case::
- ret = misc_register(&s3c2410wdt_miscdev);
+ ret = watchdog_register_device(&s3c2410_wdd);
...
- misc_deregister(&s3c2410wdt_miscdev);
+ watchdog_unregister_device(&s3c2410_wdd);
Update the Kconfig-entry
------------------------
The entry for the driver now needs to select WATCHDOG_CORE:
+ select WATCHDOG_CORE
Create a patch and send it to upstream
--------------------------------------
Make sure you understood Documentation/process/submitting-patches.rst and send your patch to
linux-watchdog@vger.kernel.org. We are looking forward to it :)
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
공통 API로 경량화
1-13Watchdog 프레임워크가 커널에 들어오기 전에는 각 드라이버가 API 전체를 직접 구현해야 했습니다. 이제 프레임워크가 공통 요소를 분리했으므로 기존 드라이버를 프레임워크 사용자로 전환해 코드를 줄일 수 있습니다.
Wolfram Sang가 작성한 이 문서는 필요한 전환 단계와 주의점을 순서대로 안내합니다.
=========================================================
Converting old watchdog drivers to the watchdog framework
=========================================================
by Wolfram Sang <wsa@kernel.org>
Before the watchdog framework came into the kernel, every driver had to
implement the API on its own. Now, as the framework factored out the common
components, those drivers can be lightened making it a user of the framework.
This document shall guide you for this task. The necessary steps are described
as well as things to look out for.
file_operations 제거
14-87기존 드라이버의 `open()`, `write()` 같은 `file_operations`는 프레임워크가 처리하고 필요할 때 드라이버를 호출하므로 일반적으로 구조체와 관련 함수를 제거합니다. 장치 고유 동작만 다른 콜백으로 옮깁니다.
`open`의 파일 열기 검사, magic close 준비 같은 자원 관리는 제거하고 장치 고유 처리는 `start`로 옮깁니다. `start`가 `ping` 역할까지 하면서 clock처럼 start/stop 균형이 필요하면 별도 start 함수를 분리하는 편이 낫습니다. `close`에도 같은 원칙을 적용합니다.
`write`의 write 시 ping과 magic 문자 `V` 처리는 프레임워크가 모두 맡으므로 제거할 수 있습니다.
공통 ioctl은 프레임워크가 처리하되 드라이버가 정보와 콜백을 제공합니다. `WDIOC_GETSUPPORT`는 필수 `watchdog_info`, GETSTATUS는 `status` 콜백, GETBOOTSTATUS는 올바른 `bootstatus`, KEEPALIVE는 `WDIOF_KEEPALIVEPING` 옵션을 사용합니다.
SETTIMEOUT은 `WDIOF_SETTIMEOUT`과 `set_timeout` 콜백이 필요하고, `watchdog_device`의 `min_timeout`·`max_timeout`을 설정하면 core가 범위를 검사합니다. GETTIMELEFT는 `get_timeleft()`가 없으면 `EOPNOTSUPP`를 반환합니다. SETOPTIONS와 GETTIMEOUT은 별도 준비가 없습니다.
기존 드라이버의 사설 ioctl은 `ioctl` 콜백으로 유지할 수 있지만 새 드라이버가 사설 ioctl을 만들 용도는 아닙니다. 사설 ioctl을 먼저 처리하고 콜백이 `-ENOIOCTLCMD`를 반환하면 프레임워크 ioctl도 시도하며, 다른 오류는 그대로 사용자에게 반환합니다.
원문의 diff 예시는 `s3c2410wdt_fops` 전체를 제거합니다. 함수 안에 장치 고유 코드가 있는지 먼저 검사해 이후 콜백 리팩터링을 위해 남기고 나머지를 삭제합니다.
프레임워크가 처리할 때 드라이버가 제공할 항목입니다.
Remove the file_operations struct
---------------------------------
Old drivers define their own file_operations for actions like open(), write(),
etc... These are now handled by the framework and just call the driver when
needed. So, in general, the 'file_operations' struct and assorted functions can
go. Only very few driver-specific details have to be moved to other functions.
Here is a overview of the functions and probably needed actions:
- open: Everything dealing with resource management (file-open checks, magic
close preparations) can simply go. Device specific stuff needs to go to the
driver specific start-function. Note that for some drivers, the start-function
also serves as the ping-function. If that is the case and you need start/stop
to be balanced (clocks!), you are better off refactoring a separate start-function.
- close: Same hints as for open apply.
- write: Can simply go, all defined behaviour is taken care of by the framework,
i.e. ping on write and magic char ('V') handling.
- ioctl: While the driver is allowed to have extensions to the IOCTL interface,
the most common ones are handled by the framework, supported by some assistance
from the driver:
WDIOC_GETSUPPORT:
Returns the mandatory watchdog_info struct from the driver
WDIOC_GETSTATUS:
Needs the status-callback defined, otherwise returns 0
WDIOC_GETBOOTSTATUS:
Needs the bootstatus member properly set. Make sure it is 0 if you
don't have further support!
WDIOC_SETOPTIONS:
No preparations needed
WDIOC_KEEPALIVE:
If wanted, options in watchdog_info need to have WDIOF_KEEPALIVEPING
set
WDIOC_SETTIMEOUT:
Options in watchdog_info need to have WDIOF_SETTIMEOUT set
and a set_timeout-callback has to be defined. The core will also
do limit-checking, if min_timeout and max_timeout in the watchdog
device are set. All is optional.
WDIOC_GETTIMEOUT:
No preparations needed
WDIOC_GETTIMELEFT:
It needs get_timeleft() callback to be defined. Otherwise it
will return EOPNOTSUPP
Other IOCTLs can be served using the ioctl-callback. Note that this is mainly
intended for porting old drivers; new drivers should not invent private IOCTLs.
Private IOCTLs are processed first. When the callback returns with
-ENOIOCTLCMD, the IOCTLs of the framework will be tried, too. Any other error
is directly given to the user.
Example conversion::
-static const struct file_operations s3c2410wdt_fops = {
- .owner = THIS_MODULE,
- .write = s3c2410wdt_write,
- .unlocked_ioctl = s3c2410wdt_ioctl,
- .open = s3c2410wdt_open,
- .release = s3c2410wdt_release,
-};
Check the functions for device-specific stuff and keep it for later
refactoring. The rest can go.
miscdevice 제거
88-101`file_operations`를 제거했으므로 `struct miscdevice`도 삭제합니다. `watchdog_register_device()`가 내부적으로 부르는 `watchdog_dev_register()`가 miscdevice를 생성합니다.
원문 diff는 `WATCHDOG_MINOR`, 이름 `watchdog`, 제거한 fops를 가리키던 `s3c2410wdt_miscdev` 정의를 통째로 삭제합니다.
Remove the miscdevice
---------------------
Since the file_operations are gone now, you can also remove the 'struct
miscdevice'. The framework will create it on watchdog_dev_register() called by
watchdog_register_device()::
-static struct miscdevice s3c2410wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &s3c2410wdt_fops,
-};
불필요한 include·define 정리
102-112구조를 단순화한 뒤 사용하지 않는 define과 include를 제거합니다. 예시는 `linux/fs.h`, `linux/miscdevice.h`, `linux/uaccess.h`입니다.
단 `MODULE_ALIAS_MISCDEV`를 쓰면 `linux/miscdevice.h`가 필요하고, 사설 ioctl을 유지하면 `linux/uaccess.h`가 필요할 수 있으므로 실제 사용 여부를 확인합니다.
Remove obsolete includes and defines
------------------------------------
Because of the simplifications, a few defines are probably unused now. Remove
them. Includes can be removed, too. For example::
- #include <linux/fs.h>
- #include <linux/miscdevice.h> (if MODULE_ALIAS_MISCDEV is not used)
- #include <linux/uaccess.h> (if no custom IOCTLs are used)
watchdog_ops 추가
113-150가능한 모든 드라이버 콜백은 `struct watchdog_ops`에 정의되어 있습니다. `start()`와 `owner`는 필수이고 나머지는 선택입니다. 세부 설명은 같은 디렉터리의 watchdog kernel API 문서를 참조합니다.
기존 하드웨어 접근 함수를 찾아 ops에 연결합니다. 새 콜백은 `struct watchdog_device *wdd`를 인수로 받으므로 함수 원형을 바꿔야 할 수 있지만 직접 하드웨어에 접근하는 본문은 대개 그대로 쓸 수 있습니다.
앞 단계에서 남긴 장치 고유 코드는 이 콜백으로 옮깁니다. 예시는 owner, start, stop, ping, set_timeout을 등록하고 기존 void keepalive를 `int ... (struct watchdog_device *wdd)`로 바꿔 0을 반환합니다. 직접 호출하는 코드도 watchdog_device 주소를 전달하도록 바꿉니다.
s3c2410 전환 예시의 콜백 연결입니다.
Add the watchdog operations
---------------------------
All possible callbacks are defined in 'struct watchdog_ops'. You can find it
explained in 'watchdog-kernel-api.txt' in this directory. start() and
owner must be set, the rest are optional. You will easily find corresponding
functions in the old driver. Note that you will now get a pointer to the
watchdog_device as a parameter to these functions, so you probably have to
change the function header. Other changes are most likely not needed, because
here simply happens the direct hardware access. If you have device-specific
code left from the above steps, it should be refactored into these callbacks.
Here is a simple example::
+static struct watchdog_ops s3c2410wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = s3c2410wdt_start,
+ .stop = s3c2410wdt_stop,
+ .ping = s3c2410wdt_keepalive,
+ .set_timeout = s3c2410wdt_set_heartbeat,
+};
A typical function-header change looks like::
-static void s3c2410wdt_keepalive(void)
+static int s3c2410wdt_keepalive(struct watchdog_device *wdd)
{
...
+
+ return 0;
}
...
- s3c2410wdt_keepalive();
+ s3c2410wdt_keepalive(&s3c2410_wdd);
watchdog_device 추가
151-170프레임워크에 필요한 정보를 담은 `struct watchdog_device`를 만들고 필수 `watchdog_info`와 새 `watchdog_ops`를 연결합니다.
기존 드라이버가 정적 변수로 관리하던 `bootstatus`, `timeout` 같은 상태는 watchdog_device 멤버로 옮깁니다. timeout 멤버는 `unsigned int`이므로 signed int를 사용하던 드라이버는 형식도 변환합니다.
프레임워크 장치에 연결할 두 핵심 구조체입니다.
Add the watchdog device
-----------------------
Now we need to create a 'struct watchdog_device' and populate it with the
necessary information for the framework. The struct is also explained in detail
in 'watchdog-kernel-api.txt' in this directory. We pass it the mandatory
watchdog_info struct and the newly created watchdog_ops. Often, old drivers
have their own record-keeping for things like bootstatus and timeout using
static variables. Those have to be converted to use the members in
watchdog_device. Note that the timeout values are unsigned int. Some drivers
use signed int, so this has to be converted, too.
Here is a simple example for a watchdog device::
+static struct watchdog_device s3c2410_wdd = {
+ .info = &s3c2410_wdt_ident,
+ .ops = &s3c2410wdt_ops,
+};
nowayout 처리
171-189일부 드라이버는 런타임 모듈 매개변수 없이 `CONFIG_WATCHDOG_NOWAYOUT`만 따르는 정적 nowayout을 사용합니다. 이런 경우 watchdog_device의 `status`를 `WATCHDOG_NOWAYOUT_INIT_STATUS`로 초기화합니다.
대부분은 모듈 매개변수로 런타임 설정도 허용하므로 `watchdog_set_nowayout(&s3c2410_wdd, nowayout)`을 호출합니다. 모듈 매개변수는 유지하되 open·close·write에 흩어진 나머지 nowayout 코드는 제거할 수 있습니다.
Handle the 'nowayout' feature
-----------------------------
A few drivers use nowayout statically, i.e. there is no module parameter for it
and only CONFIG_WATCHDOG_NOWAYOUT determines if the feature is going to be
used. This needs to be converted by initializing the status variable of the
watchdog_device like this::
.status = WATCHDOG_NOWAYOUT_INIT_STATUS,
Most drivers, however, also allow runtime configuration of nowayout, usually
by adding a module parameter. The conversion for this would be something like::
watchdog_set_nowayout(&s3c2410_wdd, nowayout);
The module parameter itself needs to stay, everything else related to nowayout
can go, though. This will likely be some code in open(), close() or write().
장치 등록 함수 교체
190-205`misc_register(&miscdev)`를 `watchdog_register_device(&watchdog_dev)`로 바꾸고 반환값을 검사합니다. 기존 오류 메시지도 새 호출에 맞는지 확인합니다.
해제 경로의 `misc_deregister()`는 `watchdog_unregister_device()`로 바꿉니다. 원문 예시는 `s3c2410_wdd`에 대해 두 호출을 교체합니다.
miscdevice 경로를 watchdog core 등록 경로로 바꿉니다.
Register the watchdog device
----------------------------
Replace misc_register(&miscdev) with watchdog_register_device(&watchdog_dev).
Make sure the return value gets checked and the error message, if present,
still fits. Also convert the unregister case::
- ret = misc_register(&s3c2410wdt_miscdev);
+ ret = watchdog_register_device(&s3c2410_wdd);
...
- misc_deregister(&s3c2410wdt_miscdev);
+ watchdog_unregister_device(&s3c2410_wdd);
Kconfig에서 WATCHDOG_CORE 선택
206-213전환한 드라이버의 Kconfig 항목은 `select WATCHDOG_CORE`를 추가해 공통 watchdog core를 선택해야 합니다.
Update the Kconfig-entry
------------------------
The entry for the driver now needs to select WATCHDOG_CORE:
+ select WATCHDOG_CORE
패치 작성과 upstream 제출
214-218`Documentation/process/submitting-patches.rst`를 이해한 뒤 패치를 작성해 `linux-watchdog@vger.kernel.org`로 보냅니다.
Create a patch and send it to upstream
--------------------------------------
Make sure you understood Documentation/process/submitting-patches.rst and send your patch to
linux-watchdog@vger.kernel.org. We are looking forward to it :)
요약·해설
convert_drivers_to_kernel_api.rst:1-218기존 file_operations·miscdevice 구현을 watchdog_ops·watchdog_device 기반으로 전환하는 절차입니다.