II)接收
在2.6.21.5linux内核中rtl8139网卡驱动的接收只采用NAPI(硬中断+轮询)的方式,所以在产生硬件中断后,对中断状态寄存器 进行了一系列的检查后,屏蔽接收中断,然后触法软中断进行接收,具体过程如下:
在中断函数开始处先调用spin_lock (&tp->lock); 来进行保护,因为网卡产生的硬件中断要很快要处理完,所以这里采用了自旋锁进行保护。
-
/* Receive packets are processed by poll routine.
-
If not running start it now. */
-
if (status & RxAckBits){
-
if (netif_rx_schedule_prep(dev)) {
-
RTL_W16_F (IntrMask, rtl8139_norx_intr_mask);
-
__netif_rx_schedule (dev);
-
}
-
}
-
static const u16 rtl8139_norx_intr_mask =
PCIErr | PCSTimeout | RxUnderrun |
TxErr | TxOK | RxErr ;
从rtl8139_norx_intr_mask变量可以看到屏蔽了接收中断。
-
void __netif_rx_schedule(struct net_device *dev)
-
{
-
unsigned long flags;
-
-
local_irq_save(flags);
-
dev_hold(dev);
-
list_add_tail(&dev->poll_list, &__get_cpu_var(softnet_data).poll_list);
-
if (dev->quota < 0)
-
dev->quota += dev->weight;
-
else
-
dev->quota = dev->weight;
-
__raise_softirq_irqoff(NET_RX_SOFTIRQ);
-
local_irq_restore(flags);
-
}
local_irq_save的调用将把当前中断状态保存到flags中,然后禁用当前处理器上的中断发送,local_irq_restore恢复中断,并恢复之前的中断状态。 从这可以看出这段代码不能被硬件中断打断。接着增加此设备的引用计数,把接收队列头挂接到软中断调度队列的最后,并触法了软中断NET_RX_SOFTIRQ。此时打开自旋锁,中断调用函数就结束了。现在就要看看软中断NET_RX_SOFTIRQ处理函数:
static int __init net_dev_init(void) 函数, 在系统启动的时候要进行一系列的初始化,使内核能正常工作,这个函数网络设备初始化函数。
-
if (dev_proc_init())
-
goto out;
-
-
if (netdev_sysfs_init())
-
goto out;
-
-
INIT_LIST_HEAD(&ptype_all);
-
for (i = 0; i < 16; i++)
-
INIT_LIST_HEAD(&ptype_base[i]);
-
-
for (i = 0; i < ARRAY_SIZE(dev_name_head); i++)
-
INIT_HLIST_HEAD(&dev_name_head[i]);
-
-
for (i = 0; i < ARRAY_SIZE(dev_index_head); i++)
-
INIT_HLIST_HEAD(&dev_index_head[i]);
在/proc 和/sysfs 目录下建立网络设备的目录。 初始化了ptype_all ptype_base[i]目录。
-
for_each_possible_cpu(i) {
-
struct softnet_data *queue;
-
-
queue = &per_cpu(softnet_data, i);
-
skb_queue_head_init(&queue->input_pkt_queue);
-
queue->completion_queue = NULL;
-
INIT_LIST_HEAD(&queue->poll_list);
-
set_bit(__LINK_STATE_START, &queue->backlog_dev.state);
-
queue->backlog_dev.weight = weight_p;
-
queue->backlog_dev.poll = process_backlog;
-
atomic_set(&queue->backlog_dev.refcnt, 1);
-
}
这段代码就对软中断数据结构进行了初始化。 每个CPU都有一个字节的软中断调度队列。
INIT_LIST_HEAD(&queue->poll_list); 在这里进行了初始化。
open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL);
open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL);
完成了接收和发送软中断处理函数的注册
-
local_irq_disable();
-
-
while (!list_empty(&queue->poll_list)) {
-
struct net_device *dev;
-
-
if (budget <= 0 || jiffies - start_time > 1)
-
goto softnet_break;
-
-
local_irq_enable();
-
-
dev = list_entry(queue->poll_list.next,
-
struct net_device, poll_list);
-
have = netpoll_poll_lock(dev);
-
-
if (dev->quota <= 0 || dev->poll(dev, &budget)) {
-
netpoll_poll_unlock(have);
-
local_irq_disable();
-
list_move_tail(&dev->poll_list, &queue->poll_list);
-
if (dev->quota < 0)
-
dev->quota += dev->weight;
-
else
-
dev->quota = dev->weight;
-
} else {
-
netpoll_poll_unlock(have);
-
dev_put(dev);
-
local_irq_disable();
-
}
-
}
-
out:
-
#ifdef CONFIG_NET_DMA
-
/*
-
* There may not be any more sk_buffs coming right now, so push
-
* any pending DMA copies to hardware
-
*/
-
if (net_dma_client) {
-
struct dma_chan *chan;
-
rcu_read_lock();
-
list_for_each_entry_rcu(chan, &net_dma_client->channels, client_node)
-
dma_async_memcpy_issue_pending(chan);
-
rcu_read_unlock();
-
}
-
#endif
-
local_irq_enable();
-
return;
在软中断接收处理函数中先关闭所有中断,然后去查找接收队列,如果不为空的话,说明有包,则打开中断调用NAPI注册的回调函数dev->poll =
rtl8139_poll进行处理。
-
static int rtl8139_poll(struct net_device *dev, int *budget)
-
{
-
struct rtl8139_private *tp = netdev_priv(dev);
-
void __iomem *ioaddr = tp->mmio_addr;
-
int orig_budget = min(*budget, dev->quota); //取配额和预算较小的那个
-
int done = 1;
-
-
spin_lock(&tp->rx_lock);
-
if (likely(RTL_R16(IntrStatus) & RxAckBits)) {
-
int work_done;
-
-
work_done = rtl8139_rx(dev, tp, orig_budget);
-
if (likely(work_done > 0)) {
-
*budget -= work_done; //预算减少
-
dev->quota -= work_done; //配额减少
-
done = (work_done < orig_budget);
-
}
-
}
-
-
if (done) {
-
unsigned long flags;
-
// 接收完数据包(此时接收的数据包的个数小于配额),把设备队列从softnet软中断调度队列中删除掉,并打开接收硬件中断,从新开始中断过程
-
/*
-
* Order is important since data can get interrupted
-
* again when we think we are done.
-
*/
-
local_irq_save(flags);
-
RTL_W16_F(IntrMask, rtl8139_intr_mask);
-
__netif_rx_complete(dev);
-
local_irq_restore(flags);
-
}
-
spin_unlock(&tp->rx_lock);
-
-
return !done;
-
}
最终调用rtl8139_rx
函数进行数据包的接收工作
-
static int rtl8139_rx(struct net_device *dev, struct rtl8139_private *tp,
-
int budget)
-
{
-
void __iomem *ioaddr = tp->mmio_addr;
-
int received = 0;
-
unsigned char *rx_ring = tp->rx_ring;
-
unsigned int cur_rx = tp->cur_rx;
-
unsigned int rx_size = 0;
-
-
DPRINTK ("%s: In rtl8139_rx(), current %4.4x BufAddr %4.4x,"
-
" free to %4.4x, Cmd %2.2x.\n", dev->name, (u16)cur_rx,
-
RTL_R16 (RxBufAddr),
-
RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd));
-
-
while (netif_running(dev) && received < budget
-
&& (RTL_R8 (ChipCmd) & RxBufEmpty) == 0) {
-
u32 ring_offset = cur_rx % RX_BUF_LEN;
-
u32 rx_status;
-
unsigned int pkt_size;
-
struct sk_buff *skb;
-
-
rmb();
-
-
/* read size+status of next frame from DMA ring buffer */
-
rx_status = le32_to_cpu (*(u32 *) (rx_ring + ring_offset));
-
rx_size = rx_status >> 16;
-
pkt_size = rx_size - 4;
-
-
if (netif_msg_rx_status(tp))
-
printk(KERN_DEBUG "%s: rtl8139_rx() status %4.4x, size %4.4x,"
-
" cur %4.4x.\n", dev->name, rx_status,
-
rx_size, cur_rx);
-
#if RTL8139_DEBUG > 2
-
{
-
int i;
-
DPRINTK ("%s: Frame contents ", dev->name);
-
for (i = 0; i < 70; i++)
-
printk (" %2.2x",
-
rx_ring[ring_offset + i]);
-
printk (".\n");
-
}
-
#endif
-
-
/* Packet copy from FIFO still in progress.
-
* Theoretically, this should never happen
-
* since EarlyRx is disabled.
-
*/
-
if (unlikely(rx_size == 0xfff0)) {
-
if (!tp->fifo_copy_timeout)
-
tp->fifo_copy_timeout = jiffies + 2;
-
else if (time_after(jiffies, tp->fifo_copy_timeout)) {
-
DPRINTK ("%s: hung FIFO. Reset.", dev->name);
-
rx_size = 0;
-
goto no_early_rx;
-
}
-
if (netif_msg_intr(tp)) {
-
printk(KERN_DEBUG "%s: fifo copy in progress.",
-
dev->name);
-
}
-
tp->xstats.early_rx++;
-
break;
-
}
-
-
no_early_rx:
-
tp->fifo_copy_timeout = 0;
-
-
/* If Rx err or invalid rx_size/rx_status received
-
* (which happens if we get lost in the ring),
-
* Rx process gets reset, so we abort any further
-
* Rx processing.
-
*/
-
if (unlikely((rx_size > (MAX_ETH_FRAME_SIZE+4)) ||
-
(rx_size < 8) ||
-
(!(rx_status & RxStatusOK)))) {
-
rtl8139_rx_err (rx_status, dev, tp, ioaddr);
-
received = -1;
-
goto out;
-
}
-
-
/* Malloc up new buffer, compatible with net-2e. */
-
/* Omit the four octet CRC from the length. */
-
-
skb = dev_alloc_skb (pkt_size + 2);
-
if (likely(skb)) {
-
skb->dev = dev;
-
skb_reserve (skb, 2); /* 16 byte align the IP fields. */
-
#if RX_BUF_IDX == 3
-
wrap_copy(skb, rx_ring, ring_offset+4, pkt_size);
-
#else
-
eth_copy_and_sum (skb, &rx_ring[ring_offset + 4], pkt_size, 0);
-
#endif
-
skb_put (skb, pkt_size);
-
-
skb->protocol = eth_type_trans (skb, dev);
-
-
dev->last_rx = jiffies;
-
tp->stats.rx_bytes += pkt_size;
-
tp->stats.rx_packets++;
-
-
netif_receive_skb (skb);
-
} else {
-
if (net_ratelimit())
-
printk (KERN_WARNING
-
"%s: Memory squeeze, dropping packet.\n",
-
dev->name);
-
tp->stats.rx_dropped++;
-
}
-
received++;
-
-
cur_rx = (cur_rx + rx_size + 4 + 3) & ~3;
-
RTL_W16 (RxBufPtr, (u16) (cur_rx - 16));
-
-
rtl8139_isr_ack(tp);
-
}
-
-
if (unlikely(!received || rx_size == 0xfff0))
-
rtl8139_isr_ack(tp);
-
-
#if RTL8139_DEBUG > 1
-
DPRINTK ("%s: Done rtl8139_rx(), current %4.4x BufAddr %4.4x,"
-
" free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx,
-
RTL_R16 (RxBufAddr),
-
RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd));
-
#endif
-
-
tp->cur_rx = cur_rx;
-
-
/*
-
* The receive buffer should be mostly empty.
-
* Tell NAPI to reenable the Rx irq.
-
*/
-
if (tp->fifo_copy_timeout)
-
received = budget;
-
-
out:
-
return received;
-
}
在rtl8139_rx
这个函数中我们看到了tp->rx_ring 这个虚拟地址。接收小于burget个数的数据包通过netif_recv_skb递交给上层网络
阅读(2804) | 评论(0) | 转发(0) |