1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

NetBSDAdvent Calendar 2023

Day 12

callout(9)のcallout_init()の実装を調べてみる

Posted at

NetBSD Advent Calendar 2023 12日目の記事です。今日はcallout(9)の機能をもう少し深堀りしてみます。

callout_init()関数

executor.c では、以下のような形で callout_init() が呼ばれています。

 51 static callout_t sc;
...
 96         callout_init(&sc, 0);

callout_init()/usr/src/sys/kern/kern_timeout.c で以下のような関数定義になっています。

277 void
278 callout_init(callout_t *cs, u_int flags)

executor.c の例では第2引数の u_int flags0 が渡されているため、今回は第1引数の callout_t *cs のみを見れば良さそうです。 callout_t/usr/src/sys/sys/callout.h で以下のように定義されています。一見すると単なる void* 型の配列なので意味がわからず面食らってしまいますが、コメントを見るとcallout(9)の機能自体はカーネル側で提供するものの、パフォーマンス上の理由から、使用するデータ領域はcalloutを呼び出す側で確保する使い方であるため、このような定義になっているようです。

 38 /*
 39  * The callout implementation is private to kern_timeout.c yet uses
 40  * caller-supplied storage, as lightweight callout operations are
 41  * critical to system performance.
 42  *
 43  * The size of callout_t must remain constant in order to ensure ABI
 44  * compatibility for kernel modules: it may become smaller, but must
 45  * not grow.  If more space is required, rearrange the members of
 46  * callout_impl_t.
 47  */
 48 typedef struct callout {
 49     void    *_c_store[10];
 50 } callout_t;

callout_t がどのようなデータ型にマップされるかは、 callout_init() を見るのが良さそうです。実際は callout_impl_t 型にキャストされます。

277 void
278 callout_init(callout_t *cs, u_int flags)
279 {
280     callout_impl_t *c = (callout_impl_t *)cs;
281     struct callout_cpu *cc;

callout_impl_t 型は /usr/src/sys/sys/callout.h で以下のように定義されています。各メンバ変数の役割を一気に理解するのは大変なので、まずは void (*c_func)(void *)int c_timestruct callout_cpu * volatile c_cpu を見てみます。それぞれcallout(9)から呼び出してほしい関数と関数が発火するまでの時間となっています。

 91 typedef struct callout_impl {
 92     struct callout_circq c_list;        /* linkage on queue */
 93     void    (*c_func)(void *);      /* function to call */
 94     void    *c_arg;             /* function argument */
 95     struct callout_cpu * volatile c_cpu;    /* associated CPU */
 96     int c_time;             /* when callout fires */
 97     u_int   c_flags;            /* state of this entry */
 98     u_int   c_magic;            /* magic number */
 99 } callout_impl_t;
100 #define CALLOUT_MAGIC       0x11deeba1

ここまでを踏まえると、 callout_init() の中身でポイントとなる箇所が見えてきます。 callout_impl_tc_funcNULL を代入してcalloutされる関数が存在していない状態としておき、 c_cpu にcalloutと紐付けるCPUを設定します。
(calloutの初期化処理なのに c_time0 とか入れておかなくて大丈夫なの?という疑問はありますが...)

277 void
278 callout_init(callout_t *cs, u_int flags)
279 {
280     callout_impl_t *c = (callout_impl_t *)cs;
281     struct callout_cpu *cc;
282
283     KASSERT((flags & ~CALLOUT_FLAGMASK) == 0);
284
285     cc = curcpu()->ci_data.cpu_callout;
286     c->c_func = NULL;
287     c->c_magic = CALLOUT_MAGIC;
288     if (__predict_true((flags & CALLOUT_MPSAFE) != 0 && cc != NULL)) {
289         c->c_flags = flags;
290         c->c_cpu = cc;
291         return;
292     }
293     c->c_flags = flags | CALLOUT_BOUND;
294     c->c_cpu = &callout_cpu0;
295 }

これでとりあえず、 callout_init() で行われる主要な処理について把握できました。記事的に長くなってので、実際にcallout(9)の設定を行う部分の実装については日を改めて解説したいと思います。

まとめ

callout(9)の初期化を行う callout_init() の関数の実装について紹介しました。カーネルモジュールサンプルの executor.c から深掘りしての解説になっていますが、カーネルが提供する機能を理解するための足がかりとしては有効そうです。

1
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?