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?

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

Last updated at Posted at 2023-12-15

NetBSD Advent Calendar 2023 13日目の記事です。今日はcallout(9)に呼び出してほしい関数を設定する callout_reset() の実装を見てゆこうと思います。

callout_reset()

callout_init() でcalloutに必要な構造体( callout_t )の初期化を行った後、実際に呼び出しほしい関数を callout_reset() で設定します。例えば、カーネルモジュールサンプルの executor.c では以下のような関数呼び出しとなっています。

callout_reset(&sc, mstohz(1000), callout_example, NULL);

callout_init()の解説を踏まえると、 callout_reset() の内部で行われる処理がが何となく想像できそうです。さっそく callout_reset() の実装を見てみます。

callout_reset()/usr/src/sys/kern/kern_timeout.c で以下のように定義されています。やはり callout_impl_t のメンバ変数 c_func に引数で渡した関数を設定しています。

377 /*
378  * callout_reset:
379  *
380  *  Reset a callout structure with a new function and argument, and
381  *  schedule it to run.
382  */
383 void
384 callout_reset(callout_t *cs, int to_ticks, void (*func)(void *), void *arg)
385 {
386     callout_impl_t *c = (callout_impl_t *)cs;
387     kmutex_t *lock;
388
389     KASSERT(c->c_magic == CALLOUT_MAGIC);
390     KASSERT(func != NULL);
391
392     lock = callout_lock(c);
393     c->c_func = func;
394     c->c_arg = arg;
395     callout_schedule_locked(c, lock, to_ticks);
396 }

callout_reset() 内で指定した関数が発火するまでの時間を設定しているのだろうと思っていましたが、これは callout_reset() から呼び出している callout_schedule_locked() の中で設定しているようです。

callout_schedule_locked() を見てみます。関数の頭の方で callout_impl_tc_flags から CALLOUT_FIREDCALLOUT_INVOKING のフラグが落とされています。呼び出し元の関数は callout_reset() なので、calloutが発火していない&呼び出されていない状態にするのは納得できます。

329 static void
330 callout_schedule_locked(callout_impl_t *c, kmutex_t *lock, int to_ticks)
331 {
...
338     /* Initialize the time here, it won't change. */
339     occ = c->c_cpu;
340     c->c_flags &= ~(CALLOUT_FIRED | CALLOUT_INVOKING);
...
359     cc = curcpu()->ci_data.cpu_callout;
360     if ((c->c_flags & CALLOUT_BOUND) != 0 || cc == occ ||
361         !mutex_tryenter(cc->cc_lock)) {
362         /* Leave on existing CPU. */
363         c->c_time = to_ticks + occ->cc_ticks;
364         c->c_flags |= CALLOUT_PENDING;
365         CIRCQ_INSERT(&c->c_list, &occ->cc_todo);
366     } else {

340行目の時点で、 c_flags に設定されている値は CALLOUT_BOUND のみとなります。これは今回参照しているexecutor.cでは callout_init(&sc, 0) となっており、(第2引数の) u_int flags0 であるためです。

277 void
278 callout_init(callout_t *cs, u_int flags)
279 {
...
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;

そして363行目にて、この時点での callout_impl_t->cc_ticks と(引数として渡された) int to_ticks を加算した値を callout_impl_t->c_time に設定しています。その後、 c_flagsCALLOUT_PENDING フラグを立ててから struct callout_cpu->cc_todoCIRCQ_INSERT()struct callout_cpucc_todo メンバ変数に c_list を追加します( CIRCQ_INSERT(elem, list) というマクロになっています)。

callout_reset() での関数と関数が発火するまでを指定する処理は以上になります。後は定期的にcallout周りのリスト( CIRCQ_INSERT() で操作しているリスト)を辿って関数を呼び出す(イメージ的には「コールバックする」という表現がより適切かもしれません)処理を担うものがあるはずですが、今回はそこまで追いきれませんでした。

329 static void
330 callout_schedule_locked(callout_impl_t *c, kmutex_t *lock, int to_ticks)
331 {
...
338     /* Initialize the time here, it won't change. */
339     occ = c->c_cpu;
340     c->c_flags &= ~(CALLOUT_FIRED | CALLOUT_INVOKING);
...
359     cc = curcpu()->ci_data.cpu_callout;
360     if ((c->c_flags & CALLOUT_BOUND) != 0 || cc == occ ||
361         !mutex_tryenter(cc->cc_lock)) {
362         /* Leave on existing CPU. */
363         c->c_time = to_ticks + occ->cc_ticks;
364         c->c_flags |= CALLOUT_PENDING;
365         CIRCQ_INSERT(&c->c_list, &occ->cc_todo);
366     } else {

まとめ

callout_reset() で呼び出してほしい関数と関数呼び出しまでの時間を指定する処理を追いかけてみました。登録した関数はリストで管理されているという所までは把握できたのですが、そのリストを誰が辿って関数呼び出し(関数発火)の条件判定と関数コールを行っているかまでは追いかけきれませんでした。

1
0
1

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?