パケット受信シリーズ-前半部実装(カーネルインタフェース)
8237 ワード
本文の主な内容:ネットワークパケット受信の上半部実現、主にカーネルインタフェース関連部分を分析する.
カーネルバージョン:2.6.37
Author:zhangskd @ csdn blog
上半部の実装
受信パケットの前半部の処理フローは次のとおりです.
el_interrupt()/NIC駆動
|--> el_receive()/NIC駆動
|--> netif_rx()/カーネルインタフェース
|--> enqueue_to_backlog()/カーネルインタフェース
NICドライバに関する部分を分析しましたが、カーネルインタフェースに関する部分を見てみましょう.)
netif_rx
netif_rx()はカーネルがネットワークパケットを受信するエントリである(現在、多くのNICは新しいインタフェースNAPIをサポートしており、後述する).
netif_rx()プライマリコールenqueue_to_backlog()は後続処理を行う.
softnet_data
各cpuにはソフトウェアが1つあります.dataインスタンス.パケットの送受信に使用されます.
定義#テイギ#
初期化
enqueue_to_backlog
netif_rx()呼び出しenqueue_to_backlog()で処理します.
まず、現在のcpuのソフトウェアを取得します.dataインスタンスsd、そして:
1.受信キューsd->input_pkt_Queueは空ではなく、パケットの処理にソフトブレークがあったことを示しています.
ソフト割り込みを再度トリガする必要がなく、受信キューの末尾にパケットを直接追加すればよい.
2.受信キューsd->input_pkt_Queueは空で、現在ソフトブレークがパケットを処理していないことを示しています.
仮想デバイスbacklogをsd->poll_に追加Listでポーリングを行い、最後にNET_を設定します.RX_SOFTIRQ
フラグはソフトブレークをトリガーします.
3.受信キューsd->input_pkt_Queueがいっぱいになったら、パケットを直接破棄します.
napi_structは、非NAPIの駆動に対応する仮想デバイスを表す.
カーネルバージョン:2.6.37
Author:zhangskd @ csdn blog
上半部の実装
受信パケットの前半部の処理フローは次のとおりです.
el_interrupt()/NIC駆動
|--> el_receive()/NIC駆動
|--> netif_rx()/カーネルインタフェース
|--> enqueue_to_backlog()/カーネルインタフェース
NICドライバに関する部分を分析しましたが、カーネルインタフェースに関する部分を見てみましょう.)
netif_rx
netif_rx()はカーネルがネットワークパケットを受信するエントリである(現在、多くのNICは新しいインタフェースNAPIをサポートしており、後述する).
netif_rx()プライマリコールenqueue_to_backlog()は後続処理を行う.
/**
* netif_rx - post buffer to the network code
* @skb: buffer to post
* This function receives a packet from a device and queues it for the upper (protocol)
* levels to process. It always succeeds. The buffer may be dropped during processing
* for congestion control or by the protocol layers.
* return values:
* NET_RX_SUCCESS (no congestion)
* NET_RX_DROP (packet was dropped)
*/
int netif_rx(struct sk_buff *skb)
{
int ret;
/* if netpoll wants it, pretend we never saw it */
if (netpoll_rx(skb))
return NET_RX_DROP;
/* skb->tstamp */
if (netdev_tstamp_prequeue)
net_timestamp_check(skb);
trace_netif_rx(skb);
#ifdef CONFIG_RPS
/* RPS, */
...
#else
{
unsigned int qtail;
ret = enqueue_to_backlog(skb, get_cpu(), &qtail);
put_cpu();
}
#endif
return ret;
}
softnet_data
各cpuにはソフトウェアが1つあります.dataインスタンス.パケットの送受信に使用されます.
/* Incoming packets are placed on per-cpu queues */
struct softnet_data {
struct Qdisc *output_queue; /* */
struct Qdisc **output_queue_tailp;
/* , */
struct list_head poll_list;
struct sk_buff *completion_queue; /* */
/* , input_pkt_queue */
struct sk_buff_head process_queue;
/* stats */
unsigned int processed; /* */
unsigned int time_squeeze; /* poll */
unsigned int cpu_collision;
unsigned int received_rps;
#ifdef CONFIG_RPS
/* RPS */
...
#endif
unsigned dropped; /* */
/* , 。
* NAPI , NAPI 。
*/
struct sk_buff_head input_pkt_queue;
struct napi_struct backlog; /* , NAPI */
};
定義#テイギ#
/* Device drivers call our routines to queue packets here.
* We empty the queue in the local softnet handler.
*/
DEFINE_PER_CPU_ALIGNED(struct softnet_data, softnet_data);
EXPORT_PER_CPU_SYMBOL(softnet_data);
初期化
/* Initialize the DEV module. At boot time this walks the device list and
* unhooks any devices that fail to initialise (normally hardware not present)
* and leaves us with a valid list of present and active devices.
*
* This is called single threaded during boot, so no need to take the rtnl semaphore.
*/
static int __init net_dev_init(void)
{
...
/* Initialise the packet receive queues.
* cpu softnet_data 。
*/
for_each_possible_cpu(i) {
struct softnet_data *sd = &per_cpu(softnet_data, i);
memset(sd, 0, sizeof(*sd));
skb_queue_head_init(&sd->input_pkt_queue);
skb_queue_head_init(&sd->process_queue);
sd->completion_queue = NULL;
INIT_LIST_HEAD(&sd->poll_list);
sd->output_queue = NULL;
sd->output_queue_tailp = &sd->output_queue;
#ifdef CONFIG_RPS
...
#endif
sd->backlog.poll = process_backlog; /* NAPI */
sd->backlog.weight = weight_p; /* 64, */
sd->backlog.gro_list = NULL;
sd->backlog.gro_count = 0;
}
...
/* */
open_softirq(NET_TX_SOFTIRQ, net_tx_action);
open_softirq(NET_RX_SOFTIRQ, net_rx_action);
...
}
enqueue_to_backlog
netif_rx()呼び出しenqueue_to_backlog()で処理します.
まず、現在のcpuのソフトウェアを取得します.dataインスタンスsd、そして:
1.受信キューsd->input_pkt_Queueは空ではなく、パケットの処理にソフトブレークがあったことを示しています.
ソフト割り込みを再度トリガする必要がなく、受信キューの末尾にパケットを直接追加すればよい.
2.受信キューsd->input_pkt_Queueは空で、現在ソフトブレークがパケットを処理していないことを示しています.
仮想デバイスbacklogをsd->poll_に追加Listでポーリングを行い、最後にNET_を設定します.RX_SOFTIRQ
フラグはソフトブレークをトリガーします.
3.受信キューsd->input_pkt_Queueがいっぱいになったら、パケットを直接破棄します.
/* queue an skb to a per CPU backlog queue (may be a remote CPU queue). */
static int enqueue_to_backlog(struct sk_buff *skb, int cpu, unsigned int *qtail)
{
struct softnet_data *sd;
unsigned long flags;
sd = &per_cpu(softnet_data, cpu); /* cpu softnet_data */
local_irq_save(flags); /* */
rps_lock(sd);
if (skb_queue_len(&sd->input_pkt_queue) <= netdev_max_backlog) {
/* , ,
* , 。
*/
if (skb_queue_len(&sd->input_pkt_queue)) {
enqueue:
__skb_queue_tail(&sd->input_pkt_queue, skb); /* */
input_queue_tail_incr_save(sd, qtail);
rps_unlock(sd);
local_irq_restore(flags); /* */
return NET_RX_SUCCESS;
}
/* Schedule NAPI for backlog device.
* , ,
* backlog sd->poll_list ,
* NET_RX_SOFTIRQ 。
*/
if (! __test_and_set_bit(NAPT_STATE_SCHED, &sd->backlog.state)) {
if (! rps_ipi_queued(sd))
____napi_schedule(sd, &sd->backlog);
}
goto enqueue;
}
sd->dropped++; /* */
rps_unlock(sd);
local_irq_restore(flags); /* */
atomic_long_inc(&skb->dev->rx_dropped);
kfree_skb(skb); /* */
return NET_RX_DROP;
}
int netdev_tstamp_prequeue = 1; /* */
int netdev_max_backlog = 1000; /* */
napi_structは、非NAPIの駆動に対応する仮想デバイスを表す.
/* Structure for NAPI scheduling similar to tasklet but with weighting */
struct napi_struct {
/* The poll_list must only be managed by the entity which changes the
* state of the NAPI_STATE_SCHED bit. This means whoever atomically
* sets that bit can add this napi_struct to the per-cpu poll_list, and
* whoever clears that bit can remove from the list right before clearing the bit.
*/
struct list_head poll_list; /* */
unsigned long state; /* */
int weight; /* , NAPI weight_p, 64 */
int (*poll) (struct napi_struct *, int); /* , process_backlog() */
#ifdef CONFIG_NETPOLL
...
#endif
unsigned int gro_count;
struct net_device *dev;
struct list_head dev_list;
struct sk_buff *gro_list;
struct sk_buff *skb;
};
static inline void ____napi_schedule(struct softnet_data *sd, struct napi_struct *napi)
{
/* napi_struct softnet_data poll_list */
list_add_tail(&napi->poll_list, &sd->poll_list);
__raise_softirq_irqoff(NET_RX_SOFTIRQ); /* */
}