softirq 処理はいつ行われるのか

softirq に登録 (open_softirq()) した関数が raise_softirq(), raise_softirq_irqoff() で実行保留状態になった後、実行される状況は次の表のとおりです。tasklet, timer とも softirq に登録した関数の処理として実装されているので、これらが実行される状況だとも言えます。

イベント・呼び出し処理が行われる所
呼び出し元 contextirq 処理の最後ksoftirqd
割り込み発生
(irq_exit())
-×: in_interrupt()
○: (!in_interrupt())[*1][*2][*4]
×: in_interrupt()
○: (!in_interrupt())[*1][*2][*4]
do_softirq()×: in_interrupt()[*3]
○: (!in_interrupt())[*2]
-×: in_interrupt()[*3]
○: (!in_interrupt())[*2]
raise_softirq()
raise_softirq_irqoff()
×-×: in_interrupt()[*3]
○: (!in_interrupt())

○: 処理する。×: 処理しない。-: 組み合わせ上の空き。

[*1] CONFIG_IRQ_FORCED_THREADING が定義されて構築された kernel に起動パラメータ threadirqs が付加されている場合は、ksoftirqd で softirq 処理が行われます。CONFIG_IRQ_FORCED_THREADING が定義されていないか、定義されて構築された kernel に起動パラメータ threadirqs が付加されていない場合は、softirq 処理に時間が掛かった場合、処理しきれない分を ksoftirqd にて処理する。

[*2] 割り込み処理の最後または、呼び出したコンテキスト中で制限時間 MAX_SOFTIRQ_TIME または、繰り返し制限回数 MAX_SOFTIRQ_RESTART 以内に終わらなければ、ksoftirqd thread で処理されます。

[*3] in_interrupt() が真の場合、実行されません。in_interrupt()irq_count() で (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK | NMI_MASK)) という実装がされています。ハードウエア割り込みの許可禁止関数、softirq の許可禁止関数を操作した結果による許禁止状態も in_interrupt() の結果に反映されています。

[*4] irq_exit() 内で直前にある preempt_count_sub() にて割り込み処理中状態から脱出する(カウンタを減じる)処理があります。

ksoftirqd はいつ働く

softirq 処理のために ksoftirqd thread が用意されています。ksoftirqd, softirq_threads 変数で状態を保持しています。ps uaxww コマンドでプロセスリストを表示すると ksoftirqd/0, ksoftirqd/1, ... のような名前で CPU の数と同じ数だけ並ぶ kernel thread です。

fileOpenGrok(linux-4.1.27/kernel/softirq.c:747)
Expand allFold all
747
748
749
750
751
752
-
|
|
|
|
!
static struct smp_hotplug_thread softirq_threads = {
        .store                  = &ksoftirqd,
        .thread_should_run      = ksoftirqd_should_run,
        .thread_fn              = run_ksoftirqd,
        .thread_comm            = "ksoftirqd/%u",
};

softirq 処理が ksoftirqd で行われるのは threadirqs kernel parameter 未指定(または無効)で低負荷状態では稀です。ksoftirqd が使われる条件は __do_softirq() に実装されています。

割り込み処理の最後で softirq を処理する時間は 2ms (MAX_SOFTIRQ_TIME), softirq 処理中に新たに保留状態になった softirq を処理し尽くす様に繰り返す回数は 10 回 (MAX_SOFTIRQ_RESTART) になっています。これを超えた場合は、ksoftirqd thread の中で実行されることになります。コメントを読むと、経験的に決まった値のようです。割り込み応答時間を短くしたいならば、調整の余地がありそうな定数でしょう。

fileOpenGrok(linux-4.1.27/kernel/softirq.c:181)
Expand allFold all
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
-
|
|
|
|
|
|
|
|
|
|
|
!
 
 
/*
 * We restart softirq processing for at most MAX_SOFTIRQ_RESTART times,
 * but break the loop if need_resched() is set or after 2 ms.
 * The MAX_SOFTIRQ_TIME provides a nice upper bound in most cases, but in
 * certain cases, such as stop_machine(), jiffies may cease to
 * increment and so we need the MAX_SOFTIRQ_RESTART limit as
 * well to make sure we eventually return from this method.
 *
 * These limits have been established via experimentation.
 * The two things to balance is latency against fairness -
 * we want to handle softirqs as soon as possible, but they
 * should not be able to lock up the box.
 */
#define MAX_SOFTIRQ_TIME  msecs_to_jiffies(2)
#define MAX_SOFTIRQ_RESTART 10

__do_softirq() 関数中にある h->action(h); が softirq 処理関数呼び出しです。保留している softirq 処理が残り続けると restart: ラベルを回るループが回ります。ループは MAX_SOFTIRQ_TIME, MAX_SOFTIRQ_RESTART で回る回数が制限され、制限に達すると wakeup_softirqd() が呼ばれ残りは ksoftirqd thread で行われます。

fileOpenGrok(linux-4.1.27/kernel/softirq.c:230)
Expand allFold all
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
 
-
|
|
|
|
|
|
|
|
-
|
|
|
!
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-
|
|
|
|
|
|
|
|
|
|
|
|
|
-
|
|
|
|
!
|
|
!
|
|
|
|
|
-
|
|
|
|
|
!
|
|
|
|
|
|
!
asmlinkage __visible void __do_softirq(void)
{
        unsigned long end = jiffies + MAX_SOFTIRQ_TIME;
        unsigned long old_flags = current->flags;
        int max_restart = MAX_SOFTIRQ_RESTART;
        struct softirq_action *h;
        bool in_hardirq;
        __u32 pending;
        int softirq_bit;
 
        /*
         * Mask out PF_MEMALLOC s current task context is borrowed for the
         * softirq. A softirq handled such as network RX might set PF_MEMALLOC
         * again if the socket is related to swap
         */
        current->flags &= ~PF_MEMALLOC;
 
        pending = local_softirq_pending();
        account_irq_enter_time(current);
 
        __local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);
        in_hardirq = lockdep_softirq_start();
 
restart:
        /* Reset the pending bitmask before enabling irqs */
        set_softirq_pending(0);
 
        local_irq_enable();
 
        h = softirq_vec;
 
        while ((softirq_bit = ffs(pending))) {
                unsigned int vec_nr;
                int prev_count;
 
                h += softirq_bit - 1;
 
                vec_nr = h - softirq_vec;
                prev_count = preempt_count();
 
                kstat_incr_softirqs_this_cpu(vec_nr);
 
                trace_softirq_entry(vec_nr);
                h->action(h);
                trace_softirq_exit(vec_nr);
                if (unlikely(prev_count != preempt_count())) {
                        pr_err("huh, entered softirq %u %s %p with preempt_count %08x, exited with %08x?\n",
                               vec_nr, softirq_to_name[vec_nr], h->action,
                               prev_count, preempt_count());
                        preempt_count_set(prev_count);
                }
                h++;
                pending >>= softirq_bit;
        }
 
        rcu_bh_qs();
        local_irq_disable();
 
        pending = local_softirq_pending();
        if (pending) {
                if (time_before(jiffies, end) && !need_resched() &&
                    --max_restart)
                        goto restart;
 
                wakeup_softirqd();
        }
 
        lockdep_softirq_end(in_hardirq);
        account_irq_exit_time(current);
        __local_bh_enable(SOFTIRQ_OFFSET);
        WARN_ON_ONCE(in_interrupt());
        tsk_restore_flags(current, old_flags, PF_MEMALLOC);
}

トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS