EN JA
ALTQ(9)
ALTQ(9) FreeBSD Kernel Developer's Manual ALTQ(9)

名称

ALTQネットワークインタフェースにおいて出力キューを操作するためのカーネルインタフェース

書式

#include < sys/types.h>
#include < sys/socket.h>
#include < net/if.h>
#include < net/if_var.h>

エンキューマクロ

IFQ_ENQUEUE( struct ifaltq *ifq, struct mbuf *m, int error);

IFQ_HANDOFF( struct ifnet *ifp, struct mbuf *m, int error);

IFQ_HANDOFF_ADJ( struct ifnet *ifp, struct mbuf *m, int adjust, int error);

デキューマクロ

IFQ_DEQUEUE( struct ifaltq *ifq, struct mbuf *m);

IFQ_POLL_NOLOCK( struct ifaltq *ifq, struct mbuf *m);

IFQ_PURGE( struct ifaltq *ifq);

IFQ_IS_EMPTY( struct ifaltq *ifq);

ドライバで管理されたデキューマクロ

IFQ_DRV_DEQUEUE( struct ifaltq *ifq, struct mbuf *m);

IFQ_DRV_PREPEND( struct ifaltq *ifq, struct mbuf *m);

IFQ_DRV_PURGE( struct ifaltq *ifq);

IFQ_DRV_IS_EMPTY( struct ifaltq *ifq);

一般的なセットアップマクロ

IFQ_SET_MAXLEN( struct ifaltq *ifq, int len);

IFQ_INC_LEN( struct ifaltq *ifq);

IFQ_DEC_LEN( struct ifaltq *ifq);

IFQ_INC_DROPS( struct ifaltq *ifq);

IFQ_SET_READY( struct ifaltq *ifq);

解説

ALTQ システムはネットワークインタフェースでキューに入れられる制御規則を管理するフレームワークです。 ALTQ は、出力キューを操作するための新しいマクロを導入します。出力キューマクロは、抽象的なキュー操作に使用され、出力キュー構造体の内部フィールドに作用しません。マクロは、 ALTQ の実装から独立していて、移行の容易さのために、伝統的な ifqueue マクロと互換性があります。

IFQ_ENQUEUE(), IFQ_HANDOFF() と IFQ_HANDOFF_ADJ() はパケット m をキュー ifq に入れます。基本的なキュー制御規則はパケットを捨てるかもしれません。 error 引数は成功すれば 0 に設定され、またはパケットが捨てられるなら、 ENOBUFS に設定されます。 m によって指されたパケットは、成功すればデバイスドライバによって解放され、または失敗すればキュー制御規則によって解放されるので、呼び出し側はキューに入れた後に m に作用するべきではありません。 IFQ_HANDOFF() と IFQ_HANDOFF_ADJ() はキューに入れる操作を統計値の生成を結び付けます、そして、実際の送信を開始するためにキューに入れることが成功すれば、 if_start() を呼び出します。

IFQ_DEQUEUE() はキューからパケットをデキュー (キューから出す) します。デキューされたパケットは m に返されます、またはデキューするパケットがなければ、 mNULL が設定されます。空でないキューがレート制限で NULL を返すかもしれないので、呼び出し側は常に m をチェックしなければなりません。

IFQ_POLL_NOLOCK() は、キューからそれを取り除かないで、次のパケットを返します。その後の呼び出しで IFQ_DEQUEUE_NOLOCK() が同じパケットをデキューすることを保証するために IFQ_POLL_NOLOCK() を呼び出すとき、呼び出し側はキューミューテックスを保持しなければなりません。

IFQ_*_NOLOCK() は、(利用可能であるなら) 常に呼び出し側がキューミューテックスを保持すると仮定します。それらは IFQ_LOCK() で取得することができ、 IFQ_UNLOCK() で解放することができます。

IFQ_PURGE() はキュー中のすべてのパケットを捨てます。仕事を保存しないキューはデキューの繰り返しによって空にすることができないので、パージ操作は必要です。

IFQ_IS_EMPTY() は、キューが空であるかどうかチェックするために使用することができます。 IFQ_DEQUEUE() はキュー制御規則が仕事を保存しないなら、まだ NULL を返するかもしれないことに注意してください。

IFQ_DRV_DEQUEUE() は、キューから“driver managed”キューに ifq->ifq_drv_maxlen パケットに繰り上げて、 m を通して最初のものを返します。 IFQ_DEQUEUE() に関して、 m は空でないキューのためさえ NULL であるかもしれません。 IFQ_DRV_DEQUEUE() へのその後の呼び出しは、キューミューテックスを取得しないで、“driver managed”キューからパケットを渡します。並列アクセスに対して保護するのは、呼び出し側の責任です。与えられたキューのために ALTQ を有効にすることは、 ifq_drv_maxlen の、より高い値のために IFQ_DRV_DEQUEUE() によって実行された“bulk dequeue”が ALTQ の内部のタイミングに不利であるように、 ifq_drv_maxlen を 0 に設定します。デフォルトマクロが mbuf リーク (漏れ) を導くかもしれない“driver managed”キューを見ないように、ドライバが IFQ_DRV_*() マクロをデフォルトのデキューマクロと混同してはならないことに注意してください。

IFQ_DRV_PREPEND() は、 mIFQ_DRV_DEQUEUE() への次の呼び出しで取得されるところからの“driver managed”キューの先頭に追加します。

IFQ_DRV_PURGE() は、“driver managed”キューのすべてのパケットをフラッシュして、後で、 IFQ_PURGE() を呼び出します。

IFQ_DRV_IS_EMPTY() はキューの“driver managed”部分のパケットをチェックします。それが空であるなら、 IFQ_IS_EMPTY() へフォワードします。

IFQ_SET_MAXLEN() は、デフォルトの FIFO キューにキューの長さの制限を設定します。 ifaltq 構造体の ifq_drv_maxlen メンバは“driver managed”キューの長さの制限を制御します。

IFQ_INC_LEN() と IFQ_DEC_LEN() は、パケットの現在のキューの長さを増加させるか、または減少させます。これは主に内部の用途のためのものです。

IFQ_INC_DROPS() はドロップカウンタを増加して、 IF_DROP() と同じです。それは、名前付けの一貫性のためだけに定義されています。

IFQ_SET_READY() は、ドライバが新しいマクロを使用するために変換されたことを示すフラグを設定します。 ALTQ は、このフラグとのインタフェースだけで有効にすることができます。

互換性

ifaltq Ss 構造体

既存のコードとの互換性を保つために、新しい出力キュー構造体 ifaltq には同じフィールドがあります。伝統的な IF_*() マクロと if_snd の中でフィールドを直接参照するコードは ifaltq でまだ動作しています。

          ##古いスタイル##                        ##新しいスタイル## 
                                       | 
 struct ifqueue {                      | struct ifaltq { 
    struct mbuf *ifq_head;             |    struct mbuf *ifq_head; 
    struct mbuf *ifq_tail;             |    struct mbuf *ifq_tail; 
    int          ifq_len;              |    int          ifq_len; 
    int          ifq_maxlen;           |    int          ifq_maxlen; 
    int          ifq_drops;            |    int          ifq_drops; 
 };                                    |    /* ドライバキューフィールド */ 
                                       |    ...... 
                                       |    /* altq 関連フィールド */ 
                                       |    ...... 
                                       | }; 
                                       |

新しい構造体は struct ifnetstruct ifqueue を置き換えます。

          ##古いスタイル##                        ##新しいスタイル## 
                                       | 
 struct ifnet {                        | struct ifnet { 
     ....                              |     .... 
                                       | 
     struct ifqueue if_snd;            |     struct ifaltq if_snd; 
                                       | 
     ....                              |     .... 
 };                                    | }; 
                                       |

(簡易型) の新しい IFQ_*() マクロは次に似ています:

 #define IFQ_DEQUEUE(ifq, m)   \ 
  if (ALTQ_IS_ENABLED((ifq))  \ 
   ALTQ_DEQUEUE((ifq), (m)); \ 
  else     \ 
   IF_DEQUEUE((ifq), (m));

エンキュー操作

エンキュー (キューに入れる) 操作の動作を変更します。新しいスタイルでは、エンキューとパケットドロップは、多くのキュー制御規則で容易に分離することができないので、結合されます。新しいエンキュー操作は古いマクロで書かれている次のマクロに対応しています。

#define IFQ_ENQUEUE(ifq, m, error)                      \ 
do {                                                    \ 
        if (IF_QFULL((ifq))) {                          \ 
                m_freem((m));                           \ 
                (error) = ENOBUFS;                      \ 
                IF_DROP(ifq);                           \ 
        } else {                                        \ 
                IF_ENQUEUE((ifq), (m));                 \ 
                (error) = 0;                            \ 
        }                                               \ 
} while (0)

IFQ_ENQUEUE() は次を行います:

  • パケットをキューに入れる。
  • エンキュー操作が失敗するなら、パケットを落し (て解放し) ます。

エンキュー操作が失敗するなら、 errorENOBUFS に設定されます。 m mbuf はキュー制御規則によって解放されます。呼び出し側が、統計のためにあらかじめ m_pkthdr.len または m_flags フィールドをコピーする必要があるように、呼び出し側は、 IFQ_ENQUEUE() を呼び出した後に mbuf に作用するべきではありません。デフォルトのインタフェース統計と if_start() への迅速な呼び出しだけが要求されるなら、 IFQ_HANDOFF() と IFQ_HANDOFF_ADJ() を使用することができます。 mbuf が既に解放されているので、呼び出し側は senderr() を使用するべきではありません。

新しいスタイルの概観は if_output() は次のようです:

          ##古いスタイル##                        ##新しいスタイル## 
                                       | 
 int                                   | int 
 ether_output(ifp, m0, dst, rt0)       | ether_output(ifp, m0, dst, rt0) 
 {                                     | { 
     ......                            |     ...... 
                                       | 
                                       |     mflags = m->m_flags; 
                                       |     len = m->m_pkthdr.len; 
     s = splimp();                     |     s = splimp(); 
     if (IF_QFULL(&ifp->if_snd)) {     |     IFQ_ENQUEUE(&ifp->if_snd, m, 
                                       |                 error); 
         IF_DROP(&ifp->if_snd);        |     if (error != 0) { 
         splx(s);                      |         splx(s); 
         senderr(ENOBUFS);             |         return (error); 
     }                                 |     } 
     IF_ENQUEUE(&ifp->if_snd, m);      | 
     ifp->if_obytes +=                 |     ifp->if_obytes += len; 
                    m->m_pkthdr.len;   | 
     if (m->m_flags & M_MCAST)         |     if (mflags & M_MCAST) 
         ifp->if_omcasts++;            |         ifp->if_omcasts++; 
                                       | 
     if ((ifp->if_flags & IFF_OACTIVE) |     if ((ifp->if_flags & IFF_OACTIVE) 
         == 0)                         |         == 0) 
         (*ifp->if_start)(ifp);        |         (*ifp->if_start)(ifp); 
     splx(s);                          |     splx(s); 
     return (error);                   |     return (error); 
                                       | 
 bad:                                  | bad: 
     if (m)                            |     if (m) 
         m_freem(m);                   |         m_freem(m); 
     return (error);                   |     return (error); 
 }                                     | } 
                                       |

既存のドライバをどのように変換するか

最初に、対応する if_output() が既に新しいスタイルに変換されることを確実にします。

ドライバで if_snd を検索します。たぶん、利用者は、 if_snd を含んでいる行に変更する必要があります。

空のチェック操作

コードが、キューが空であるかどうか調べるために ifq_head をチェックするなら、 IFQ_IS_EMPTY() を使用します。

          ##古いスタイル##                        ##新しいスタイル## 
                                       | 
 if (ifp->if_snd.ifq_head != NULL)     | if (!IFQ_IS_EMPTY(&ifp->if_snd)) 
                                       |

IFQ_IS_EMPTY() は、いくつかのパケットがキューに格納されているかどうかだけチェックします。 IFQ_IS_EMPTY() が FALSE であるときでさえ、キューがレート制限であるなら、 IFQ_DEQUEUE() がまだ NULL を返すかもしれないことに注意してください。

デキュー操作

IF_DEQUEUE() を IFQ_DEQUEUE() に取り替えます。つねに、デキューされた mbuf が NULL であるかどうかチェックします。 IFQ_IS_EMPTY() が FALSE, であるときでさえ、レート制限のために IFQ_DEQUEUE() が NULL を返すかもしれないことに注意してください。

          ##古いスタイル##                        ##新しいスタイル## 
                                       | 
 IF_DEQUEUE(&ifp->if_snd, m);          | IFQ_DEQUEUE(&ifp->if_snd, m); 
                                       | if (m == NULL) 
                                       |     return; 
                                       |

ドライバは、次のデキューの引き金のために転送完了の割り込みから if_start() を呼び出すはずです。

ポーリングとデキュー操作

コードがキューの先頭でパケットをポーリングし、実際にデキューする前にパケットを使用するなら、 IFQ_POLL_NOLOCK() と IFQ_DEQUEUE_NOLOCK() を使用します。

          ##古いスタイル##                        ##新しいスタイル## 
                                       | 
                                       | IFQ_LOCK(&ifp->if_snd); 
 m = ifp->if_snd.ifq_head;             | IFQ_POLL_NOLOCK(&ifp->if_snd, m); 
 if (m != NULL) {                      | if (m != NULL) { 
                                       | 
     /* リソースを取得するために */    |     /* リソースを取得するために */ 
     /* m を使用 */                    |     /* m を使用 */ 
     if (something goes wrong)         |     if (something goes wrong) 
                                       |         IFQ_UNLOCK(&ifp->if_snd); 
         return;                       |         return; 
                                       | 
     IF_DEQUEUE(&ifp->if_snd, m);      |     IFQ_DEQUEUE_NOLOCK(&ifp->if_snd, m); 
                                       |     IFQ_UNLOCK(&ifp->if_snd); 
                                       | 
     /* ハードウェアをキックする */    |     /* ハードウェアをキックする */ 
 }                                     | } 
                                       |

前の IFQ_POLL_NOLOCK() のように同じロックの下で IFQ_DEQUEUE_NOLOCK() が同じパケットを返すことが保証されます。それらは IFQ_LOCK() によって保護される必要があることに注意してください。

IF_PREPEND() の排除

コードが IF_PREPEND() を使用するなら、代わりに IFQ_DRV_PREPEND() の使用を許す、“driver managed”キューを使用することができないなら、利用者はそれを削除しなければなりません。 IF_PREPEND() の共通的な使用法は、前のデキュー操作をキャンセルすることです。利用者は論理 (ロジック) をポーリングとデキューすることに変換しなければなりません。

          ##古いスタイル##                        ##新しいスタイル## 
                                       | 
                                       | IFQ_LOCK(&ifp->if_snd); 
 IF_DEQUEUE(&ifp->if_snd, m);          | IFQ_POLL_NOLOCK(&ifp->if_snd, m); 
 if (m != NULL) {                      | if (m != NULL) { 
                                       | 
     if (something_goes_wrong) {       |     if (something_goes_wrong) { 
         IF_PREPEND(&ifp->if_snd, m);  |         IFQ_UNLOCK(&ifp->if_snd); 
         return;                       |         return; 
     }                                 |     } 
                                       | 
                                       |     /* 現時点では、ドライバは、 
                                       |      * このパケットを送信する 
                                       |      * ためにコミットされる. 
                                       |      */ 
                                       |     IFQ_DEQUEUE_NOLOCK(&ifp->if_snd, m); 
                                       |     IFQ_UNLOCK(&ifp->if_snd); 
                                       | 
     /* ハードウェアをキックする */    |     /* ハードウェアをキックする */ 
 }                                     | } 
                                       |

パージ (消去) 操作

キューを空にするために、 IFQ_PURGE() を使用します。キューを出す繰り返しによって、仕事を保存しないキューを空にすることができないことに注意してください。

          ##古いスタイル##                        ##新しいスタイル## 
                                       | 
 while (ifp->if_snd.ifq_head != NULL) {|  IFQ_PURGE(&ifp->if_snd); 
     IF_DEQUEUE(&ifp->if_snd, m);      | 
     m_freem(m);                       | 
 }                                     | 
                                       |

ドライバで管理されたキューを使用する変換

IF_*() マクロをそれらと同等な IFQ_DRV_*() に変換して、適切なところで IFQ_DRV_IS_EMPTY() を使用します。

          ##古いスタイル##                        ##新しいスタイル## 
                                       | 
 if (ifp->if_snd.ifq_head != NULL)     | if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 
                                       |

IFQ_DRV_DEQUEUE(), IFQ_DRV_PREPEND() と IFQ_DRV_PURGE() への呼び出しがある種のミューテックスで保護されることを確認します。

アタッチルーチン

ifq_maxlenlen に設定するために IFQ_SET_MAXLEN() を使用します。 IFQ_DRV_*() マクロを使用する計画があるなら、目的にふさわしい値で ifq_drv_maxlen を初期化します。このドライバが新しいスタイルに変換されることを示すために IFQ_SET_READY() を追加します。 (これは新しいスタイルのドライバを識別するために使用されます。)

          ##古いスタイル##                        ##新しいスタイル## 
                                       | 
 ifp->if_snd.ifq_maxlen = qsize;       | IFQ_SET_MAXLEN(&ifp->if_snd, qsize); 
                                       | ifp->if_snd.ifq_drv_maxlen = qsize; 
                                       | IFQ_SET_READY(&ifp->if_snd); 
 if_attach(ifp);                       | if_attach(ifp); 
                                       |

他の問題

統計のための新しいマクロは次の通りです:

          ##古いスタイル##                        ##新しいスタイル## 
                                       | 
 IF_DROP(&ifp->if_snd);                | IFQ_INC_DROPS(&ifp->if_snd); 
                                       | 
 ifp->if_snd.ifq_len++;                | IFQ_INC_LEN(&ifp->if_snd); 
                                       | 
 ifp->if_snd.ifq_len--;                | IFQ_DEC_LEN(&ifp->if_snd); 
                                       |

キュー制御規則

キュー制御規則は、 ( IFQ_IS_EMPTY() によって使用される) ifq_len を維持する必要があります。また、キュー制御規則は、 IFQ_DEQUEUE() が IFQ_POLL() の直後に呼び出されるなら、同じ mbuf が返されることを保証する必要があります。

関連項目

pf(4), pf.conf(5), pfctl(8)

歴史

ALTQ システムは、1997 年 3 月にはじめて登場しました。
August 25, 2004 FreeBSD