LoginSignup
1
0

callout(9)に登録した関数のリストは誰がどのタイミングで処理しているか調べてみる

Posted at

NetBSD Advent Calendar 2023 14日目の記事です。今日はcallout(9)のcallout_reset()の実装を調べてみるで調べきれなかった、calloutに登録した関数について、関数呼び出し(関数発火)の条件判定と関数コールを行っている箇所を調べてみようと思います。

calloutに登録した関数のリストを操作している箇所

まずはcalloutに登録した関数のリストを操作している箇所を探してみます。 callout_softclock() という関数の中で struct callout_cpu->cc_todo のリストを順に処理するような実装になっています。

687 /*
688  * callout_softclock:
689  *
690  *  Soft interrupt handler, scheduled above if there is work to
691  *  be done.  Callouts are made in soft interrupt context.
692  */
693 static void
694 callout_softclock(void *v)
695 {
696     callout_impl_t *c;
697     struct callout_cpu *cc;
698     void (*func)(void *);
699     void *arg;
700     int mpsafe, count, ticks, delta;
701     lwp_t *l;
702
703     l = curlwp;
704     KASSERT(l->l_cpu == curcpu());
705     cc = l->l_cpu->ci_data.cpu_callout;
706
707     mutex_spin_enter(cc->cc_lock);
708     cc->cc_lwp = l;
709     while (!CIRCQ_EMPTY(&cc->cc_todo)) {
710         c = CIRCQ_FIRST(&cc->cc_todo);

さらに、 callout_impl_t->c_flagsCALLOUT_FIRED あるいは CALLOUT_INVOKING のビットが立っていた場合に callout_impl_t->c_func に設定された関数を呼び出す、という処理になっています。 callout_softclock() の関数名(と関数コメント)を見ると、どうやらソフトウェア割り込みでこの関数が呼ばれるようです。

728         c->c_flags = (c->c_flags & ~CALLOUT_PENDING) |
729             (CALLOUT_FIRED | CALLOUT_INVOKING);
730         mpsafe = (c->c_flags & CALLOUT_MPSAFE);
731         func = c->c_func;
732         arg = c->c_arg;
733         cc->cc_active = c;
734
735         mutex_spin_exit(cc->cc_lock);
736         KASSERT(func != NULL);
737         if (__predict_false(!mpsafe)) {
738             KERNEL_LOCK(1, NULL);
739             (*func)(arg);
740             KERNEL_UNLOCK_ONE(NULL);
741         } else
742             (*func)(arg);
743         mutex_spin_enter(cc->cc_lock);

callout_softclock() が参照されている箇所を見ると以下のようになっています。249行目のif文のelseブロック中で softint_establich() 関数を使用して callout_softclock() を登録しています。

236 void
237 callout_init_cpu(struct cpu_info *ci)
238 {
...
242     if ((cc = ci->ci_data.cpu_callout) == NULL) {
...
248     } else {
249         /* Boot CPU, one time only. */
250         callout_sih = softint_establish(SOFTINT_CLOCK | SOFTINT_MPSAFE,
251             callout_softclock, NULL);
252         if (callout_sih == NULL)
253             panic("callout_init_cpu (2)");
254     }

softint_establish(9)を見ると以下のように説明されています。 flags に指定したソフトウェア割り込みが発生すると、 func に指定した関数が呼び出されるという挙動になっています。 callout_init_cpu() 内の softint_establish() ではフラグとして SOFTINT_CLOCKSOFTINT_MPSAFE が指定されており、クロックまたはMPSAFE(カーネルのグローバルロック発生時?)の割り込み時に関数が呼び出される指定になっています。

     softint_establish(flags, func, arg)

              Register a software interrupt.  The flags value must contain one
              of the following constants, specifying the priority level for
              the soft interrupt:

              SOFTINT_CLOCK, SOFTINT_BIO, SOFTINT_NET, SOFTINT_SERIAL
...
              The constant func specifies the function to call when the soft
              interrupt is executed.  The argument arg will be passed to this
              function.

そして callout_init_cpt()/usr/src/sys/kern/kern_cpu.c で定義されている以下の関数から呼ばれています。 mi_cpu_attach() はマシン非依存(Machine Independent)な関数となっています。そのため、 callout_init_cpu() は各CPUアーキテクチャにおいてCPUがされた際に SOFTINT_CLOCK または SOFTINT_MPSAFE による割り込み時に struct callout_cpu->cc_todo のリストが処理されるという挙動になります。

150 int
151 mi_cpu_attach(struct cpu_info *ci)
152 {
...
194     callout_init_cpu(ci);

まとめ

リストとして管理されている、calloutに登録した関数がどのタイミングで処理されるかを調べてみました。てっきりカーネルスレッド等でループしながら処理しているのかなと思っていましたが、実際はソフトウェア割り込みを起因にしてリストを処理している実装となっていました。

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