LoginSignup
5
0

More than 5 years have passed since last update.

NetBSDカーネルモジュールをロードした時にsysctl変数を生やす(?)方法

Last updated at Posted at 2018-12-12

NetBSD Advent Calendar 12日目の記事です。

昨日の記事でカーネルモジュールに ioctl() を受け取れるようにする方法を紹介しました。
ユーザランドからカーネルに対して値を取得・設定する方法には ioctl() だけでなく、 sysctl コマンドを使用する方法もあります。そこで今日の記事では、NetBSDのカーネルモジュールで sysctl を使用できるようにする手順を紹介しようと思います。

sysctlとは

sysctl(8)は、manページにもあるようにカーネルの状態を取得・設定するコマンドです。

NAME
     sysctl ? get or set kernel state

SYNOPSIS
     sysctl [-bdehiNnoRTtqx] [-B bufsize] [-f filename] name[=value] ...
     sysctl [-bdehNnoRTtqx] [-B bufsize] -a

DESCRIPTION
     The sysctl utility retrieves kernel state and allows processes with
     appropriate privilege to set kernel state.  The state to be retrieved or
     set is described using a “Management Information Base” (“MIB”) style
     name, described as a dotted set of components.

例えば以下のような感じでカーネル内の設定値を取得できます。

$ sysctl net.inet.ip.forwarding
net.inet.ip.forwarding: 0

カーネルモジュールにsysctlを実装する

さっそくカーネルモジュールにsysctlを実装してみます。
今回は kern.hello.maxcount というsysctl名をカーネルモジュール側に用意し、ユーザランドからsysctl(8)コマンドで値の取得・設定を行えるようにしてみます。

既存のsysctl実装箇所を参考にする

ioctl の実装と同じく、 sysctl についてもNetBSDカーネルソースから既存の実装箇所を参考にするのが良さそうです。
ソースツリーを探してみると /usr/src/sys/modules/lua/lua.c の実装が参考になりそうです。

sysctl変数を追加している箇所

lua_attach() でsysctl変数を生やして(?)います。 kern/kern_sysctl.c:sysctl_createev() を使用することで、sysctlの変数を追加しています。

/usr/src/sys/modules/lua/lua.c:
125 static void
126 lua_attach(device_t parent, device_t self, void *aux)
127 {
...
129         const struct sysctlnode *node;
...
146         /* Sysctl to provide some control over behaviour */
147         sysctl_createv(&sc->sc_log, 0, NULL, &node,
148             CTLFLAG_OWNDESC,
149             CTLTYPE_NODE, "lua",
150             SYSCTL_DESCR("Lua options"),
151             NULL, 0, NULL, 0,
152             CTL_KERN, CTL_CREATE, CTL_EOL);
...
191         sysctl_createv(&sc->sc_log, 0, &node, NULL,
192             CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
193             CTLTYPE_INT, "maxcount",
194             SYSCTL_DESCR("Limit maximum instruction count"),
195             NULL, 0, &lua_max_instr, 0,
196             CTL_CREATE, CTL_EOL);

kern.lua.maxcount はstatic変数を定義し、それを sysctl_createev() の引数に指定することでsysctl変数と紐づけるようです。

/usr/src/sys/modules/modules/lua/lua.c:
 75 static int      lua_max_instr;
 ...
125 static void
126 lua_attach(device_t parent, device_t self, void *aux)
127 {
...
191         sysctl_createv(&sc->sc_log, 0, &node, NULL,
192             CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
193             CTLTYPE_INT, "maxcount",
194             SYSCTL_DESCR("Limit maximum instruction count"),
195             NULL, 0, &lua_max_instr, 0,
196             CTL_CREATE, CTL_EOL);

lua_attach()CFATTACH_DECL_NEW() で生成された箇所から呼ばれるようです。

/usr/src/sys/modules/lua/lua.c:
 89 CFATTACH_DECL_NEW(lua, sizeof(struct lua_softc),
 90         lua_match, lua_attach, lua_detach, NULL);

CFATTACH_DECL_NEW() マクロは /usr/src/sys/sys/device.h で定義されており、 struct cfattach *_ca が生成されます。
この例では struct cfattach lua_ca.ca_attach = lua_attach となるようですね。

/usr/src/sys/sys/device.h:
324 #define CFATTACH_DECL3_NEW(name, ddsize, matfn, attfn, detfn, actfn, \
325         rescanfn, chdetfn, __flags) \
326 struct cfattach __CONCAT(name,_ca) = {                                  \
327         .ca_name                = ___STRING(name),                      \
328         .ca_devsize             = ddsize,                               \
329         .ca_flags               = (__flags) | DVF_PRIV_ALLOC,           \
330         .ca_match               = matfn,                                \
331         .ca_attach              = attfn,                                \
332         .ca_detach              = detfn,                                \
333         .ca_activate            = actfn,                                \
334         .ca_rescan              = rescanfn,                             \
335         .ca_childdetached       = chdetfn,                              \
336 }
337
338 #define CFATTACH_DECL2_NEW(name, ddsize, matfn, attfn, detfn, actfn,    \
339         rescanfn, chdetfn)                                              \
340         CFATTACH_DECL3_NEW(name, ddsize, matfn, attfn, detfn, actfn,    \
341             rescanfn, chdetfn, 0)
342
343 #define CFATTACH_DECL_NEW(name, ddsize, matfn, attfn, detfn, actfn)     \
344         CFATTACH_DECL2_NEW(name, ddsize, matfn, attfn, detfn, actfn, NULL, NULL)

そして、 struct cfattach lua_calua_modcmd() から MODULE_CMD_INIT (カーネルモジュールのロード)処理の時に参照され、そこから lua_attach() が呼ばれるという流れになっているようです。

/usr/src/sys/modules/lua/lua.c:
824 static int
825 lua_modcmd(modcmd_t cmd, void *opaque)
826 {
...
833         switch (cmd) {
834         case MODULE_CMD_INIT:
835 #ifdef _MODULE
836                 error = config_cfdriver_attach(&lua_cd);
...
840                 error = config_cfattach_attach(lua_cd.cd_name,
841                     &lua_ca);
...
848                 error = config_cfdata_attach(lua_cfdata, 1);
...
857                 error = devsw_attach(lua_cd.cd_name, NULL, &bmajor,
858                     &lua_cdevsw, &cmajor);
...
866                 config_attach_pseudo(lua_cfdata);
869         case MODULE_CMD_FINI:
870 #ifdef _MODULE
871                 error = config_cfdata_detach(lua_cfdata);
...
875                 config_cfattach_detach(lua_cd.cd_name, &lua_ca);
876                 config_cfdriver_detach(&lua_cd);
877                 devsw_detach(NULL, &lua_cdevsw);
878 #endif

ここまででソースコードから見えてくる手順は以下ですね。モジュールのロード時にsysctlを生やせば良いという話ではなく、別途 struct cfattach *_ca にアタッチ処理を行う関数を設定し、 config_cf*_attach() を呼び出す必要があるということのようです。

  • モジュールのロード(MODULE_CMD_INIT)時に実行する
    • config_cfdriver_attach()
    • config_cfattach_attach()
    • config_cfdata_attach()
    • devsw_attach()
    • config_attach_pseudo()
  • モジュールのアンロード(MODULE_CMD_FINI)時に実行する
    • config_cfdata_detach()
    • config_cfattach_detach()
    • config_cfdriver_detach()
    • devsw_detach()

カーネルモジュールサンプルにsysctlを実装する

サンプルのひな型を用意する

昨日の記事と同じく、NetBSDカーネルソースツリーの modules/examples ディレクトリのコードをひな型にします。

# /usr/src/sys/modules/examples
# cp -r hello/ sysctl_sample

先述した /usr/src/sys/modules/lua/lua.c の内容を元にサンプルを実装します。サンプルコードは以下のGistに置いてあります。

ビルドしてカーネルモジュールを作成します。

# cd /usr/src/sys/modules/examples/sysctl_sample
# make
#   compile  sysctl_sample/hello.o
/usr/src/tooldir.NetBSD-8.0-amd64/bin/x86_64--netbsd-gcc -O2 -g   -std=gnu99    -Wall -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith -Wno-sign-compare  -Wsystem-headers   -Wno-traditional   -Wa,--fatal-warnings  -Wreturn-type -Wswitch -Wshadow -Wcast-qual -Wwrite-strings -Wextra -Wno-unused-parameter -Wno-sign-compare -Werror   -ffreestanding  -fno-strict-aliasing -Wno-pointer-sign -mno-red-zone -mno-mmx -mno-sse -mno-avx -msoft-float -mcmodel=kernel -fno-omit-frame-pointer  -I/usr/src/common/include --sysroot=/ -I/usr/src/common/include  -nostdinc -I. -I/usr/src/sys/modules/examples/sysctl_sample -isystem /usr/src/sys -isystem /usr/src/sys/arch -isystem /usr/src/sys/../common/include -D_KERNEL -D_LKM -D_MODULE -DSYSCTL_INCLUDE_DESCR -c    hello.c
/usr/src/tooldir.NetBSD-8.0-amd64/bin/nbctfconvert -L VERSION hello.o
#      link  sysctl_sample/hello.kmod
/usr/src/tooldir.NetBSD-8.0-amd64/bin/x86_64--netbsd-gcc  --sysroot=/ -Wl,-z,relro -Wl,--warn-shared-textrel -nostdlib -r -Wl,-T,/usr/src/sys/../sys/modules/xldscripts/kmodule,-d  -Wl,-Map=hello.kmod.map  -o hello.kmod hello.o
/usr/src/tooldir.NetBSD-8.0-amd64/bin/nbctfmerge -t -L VERSION -o hello.kmod hello.o
#
# file hello.kmod
hello.kmod: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped

カーネルモジュールを動かしてみる

ビルドしたカーネルモジュールをロードしてみます。

# cd /usr/src/sys/modules/examples/sysctl_sample
# modload ./hello.kmod

無事にロードできたようです。
SnapCrab_netbsd8 (base) [実行中] - Oracle VM VirtualBox_2018-12-13_3-40-8_No-00.png

sysctl コマンドを実行してみると、 kern.hello.maxcount という変数が存在しています。

# sysctl -a | grep kern.hello
kern.hello.maxcount = 0

sysctl -w で値を書き換えてみます。

# sysctl -w kern.hello.maxcount=7
kern.hello.maxcount: 0 -> 7
#
# sysctl kern.hello.maxcount
kern.hello.maxcount = 7

モジュールをアンロードするとsysctl変数が消えることも確認できました。これでカーネルモジュールへのsysctl変数追加と一連の動作確認まで無事に完了しました。

SnapCrab_netbsd8 (base) [実行中] - Oracle VM VirtualBox_2018-12-13_3-44-10_No-00.png

まとめ

NetBSDカーネルモジュールにsysctl変数を追加する手順を紹介しました。ユーザランドからsysctlで値を設定できるようにしておくと、カーネルモジュールの設定操作だけでなく、開発中のデバッグ用途にも応用できそうです。
カーネルモジュールでioctlと同じく、こちらもサンプルコードをひな型的な形で用意しておくとちょっとした実験等で役立ちそうです。

5
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0