Ethernet device polling(2)

内容が難しいのでコメントをでたらめに翻訳する事くらいしか出来なかった。
要するに、プロセススケジューリング全体にも、通信パフォーマンス的にも最適な間隔・長さでpollingを行う為に必要な計算を行っているらしい。

前回も書いたが、netisr_poll()は単に定期的に実行されるのではなく、この仕組みによって動的にスケジュールされている。

/*
 * netisr pollmoreは他のnetisrの後に呼ばれ、NETISR_POLLコールをスケジュールするか
 * 次のサイクルのバーストサイズを調整します。
 *
 * 大きなパケット群を一度に一つのカードからフェッチするのはとても悪いことです、
 * なぜならそれは完了するのにとても時間がかかる可能性があるし、
 * キューを飽和状態にしてしまいパケットロスやアンフェアなキューイングを生む可能性があるからです。
 * この問題を低減させるには、また、ネットワーク関連の処理に費やす時間を効率よくするためには、
 * バーストを短い固定長の塊へ区切り、塊の間のコントロールを他のnetisrへ譲ります。
 * これはフェアさを向上させ、livelockとローレベルハンドリング・フォワーディングにかかる計算を
 * 低減させる。
 */

static struct timeval poll_start_t;

void
netisr_pollmore()
{
        struct timeval t;
        int kern_load;

        mtx_lock(&poll_mtx);
        phase = 5;
        if (residual_burst > 0) {
                schednetisrbits(1 << NETISR_POLL | 1 << NETISR_POLLMORE);
                mtx_unlock(&poll_mtx);
                /* return直後に実行される? */
                return;
        }
        /* netisrにかかった時間を計算する事が出来る */
        microuptime(&t);
        kern_load = (t.tv_usec - poll_start_t.tv_usec) +
                (t.tv_sec - poll_start_t.tv_sec)*1000000;       /* us */
        kern_load = (kern_load * hz) / 10000;                   /* 0..100 */
        if (kern_load > (100 - user_frac)) { /* try decrease ticks */
                if (poll_burst > 1)
                        poll_burst--;
        } else {
                if (poll_burst < poll_burst_max)
                        poll_burst++;
        }

        pending_polls--;
        if (pending_polls == 0) /* 処理が終了した */
                phase = 0;
        else {
                /*
                 * 前回のサイクルが長く一つ以上のhardclock tickを逃している。
                 * 処理を再開するが、バーストサイズを下げて同じ事が起きないようにする */
                poll_burst -= (poll_burst / 8);
                if (poll_burst < 1)
                        poll_burst = 1;
                schednetisrbits(1 << NETISR_POLL | 1 << NETISR_POLLMORE);
                phase = 6;
        }
        mtx_unlock(&poll_mtx);
}