您的位置:首页 > 运维架构 > Linux

Linux网卡驱动分析之RTL8139(五)

2014-11-02 03:02 393 查看
Linux网卡驱动分析之RTL8139(五)

deliver_skb(dev.c)

// 该函数就是调用个协议的接收函数处理该skb 包,进入第三层网络层处理

static __inline__
int deliver_skb(struct sk_buff
*skb,

struct packet_type
*pt_prev,

struct net_device
*orig_dev)

{

atomic_inc(&skb->users);

return pt_prev->func(skb, skb->dev,
pt_prev, orig_dev);

}



链路层及以下发送网络包的流程

网络层发送数据报的最后一站是ip_finish_output2,它根据skb->dst->hh 是否已被创建

来决定如何调用链路层的输出函数,hh 实际是neighbour 的hh 成员,它在ARP 解析完成,

邻居节点被更新时进行创建,对于不需要ARP 解析的设备接口(loopback 等),它在第一次发

送数据报时被创建。所以,不管网络层如何调用链路层的输出函数,链路层的第一个输出函

数始终是dev_queue_xmit。

传输网络缓存(transmit a buffer)dev_queue_xmit(dev.c)

/**

* dev_queue_xmit - transmit a buffer

* @skb: buffer to transmit

* 把一个要传送的网络缓存队列传送到一个网络接口。该函数的调用者必须

设置网络设备

* 及优先级,自己创建缓存。该函书可以被中断程序调用。

* Queue a buffer for transmission to a network device. The caller must

* have set the device and priority and built the buffer before calling

* this function. The function can be called from an interrupt.

* 成功返回并不能保证包能正确的传输,因为它也许会因为网络问题而丢弃。

* A negative errno code is returned on a failure. A success does not

* guarantee the frame will be transmitted as it may be dropped due

* to congestion or traffic shaping.

*

*

---------------------------------------------------------------------

--------------

* I notice this method can also return errors from the queue

disciplines,

* including NET_XMIT_DROP, which is a positive value. So, errors

can also

* be positive.

*

* Regardless of the return value, the skb is consumed, so it is

currently

* difficult to retry a send to this method. (You can bump the ref

count

* before sending to hold a reference for retry if you are careful.)

*

* When calling this method, interrupts MUST be enabled. This is

because

* the BH enable code must have IRQs enabled so that it will not

deadlock.

* --BLG

*/

int dev_queue_xmit(struct sk_buff
*skb)

{

struct net_device
*dev = skb->dev;

struct Qdisc *q;

int rc =
-ENOMEM;

/* GSO will handle the following emulations directly. */

//???????

if (netif_needs_gso(dev, skb))

goto gso;

//检查skb_shinfo(skb)->frag_list 是否有值,如果有,但是网络设备接口不

支持skb 的碎片

//列表(NETIF_F_FRAGLIST),则需要把这些碎片重组到一个完整的skb 中

//(通过函数__skb_linearize)。

if (skb_shinfo(skb)->frag_list
&&

!(dev->features
& NETIF_F_FRAGLIST)
&&

__skb_linearize(skb))

goto out_kfree_skb;

/* Fragmented skb is linearized if device does not support SG,

* or if at least one of fragments is in highmem and device

* does not support DMA from it.

*/

//检查skb_shinfo(skb)->nr_frags,如果不为0,表示这个skb 使用了分散/聚

焦IO,

//如果网络设备接口不支持(NETIF_F_SG),同样需要重新线性化(通过函数

__skb_linearize)。

if (skb_shinfo(skb)->nr_frags
&&

(!(dev->features
& NETIF_F_SG)
|| illegal_highdma(dev, skb))
&&

__skb_linearize(skb))

goto out_kfree_skb;

/* If packet is not checksummed and device does not support

* checksumming for this protocol, complete checksumming here.

*/

//检查是关于校验和的,需要注意的是这个校验和不是IP 首部的首部校验和,

//IP 首部校验和在每个IP 数据报中是必需的,由软件来完成,对IP 首部以16bit

为

//段进行反码求和得到,只覆盖到IP 首部,而未覆盖到IP 数据。

//而这里的校验和是其上层协议(比如UDP)的校验和,它覆盖到上层协议的首部

和数据。

//struc sk_buff 有一个成员ip_summed,表示校验和的执行策略,其可能的取

值有三种,

􀁺 CHECKSUM_HW 表示由硬件来执行校验和,

􀁺 CHECKSUM_NONE 表示完全由软件来执行校验和,

􀁺 CHECKSUM_UNNECESSARY 表示没有必要执行校验和。

//对于新分配的一个skb,总是默认由软件来执行校验和,如果网络设备接口拥

有以下三个标志之一,并满足其它一些相关条件,就由硬件执行校验和:

NETIF_F_IP_CSUM(硬件只能执行IPv4 上的TCP/UDP 协议的校验和),

NETIF_F_NO_CSUM(硬件不需要执行校验和,比如环回设备),

NETIF_F_HW_CSUM(硬件能执行所有数据报的校验和)。如果校验和由软件执行,

则在ip_generic_getfrag 拷贝应用数据的时候执行,计算得到的校验和存放在

skb->csum,由上层协议填写自己的协议首部时填入。否则,如果校验和由硬件

执行,则上层协议在填写自己的协议首部时,为skb->csum 填上自己首部中校

验和所处的位置,以备硬件生成校验和时可以找到这个位置填入。

//dev_queue_xmit 检查校验和,只是为了作一个补救措施,即:如果

skb->ip_summed==CHECKSUM_HW(由硬件执行校验和,即当前还未生成校验和),

但是网络设备接口的成员features 上没有标志NETIF_F_HW_CSUM,

NETIF_F_NO_CSUM 或NETIF_F_IP_CSUM,即网络设备接口既没有表示不需要执行

校验和,也说明自己没有执行校验和的能力,或者,如果features 上有

NETIF_F_IP_CSUM,但是数据报又不是IP 协议的。这时候,还需要执行软件校

验和,dev_queue_xmit 就调用skb_checksum_help 补上这个校验和,并把

skb->ip_summed 设为CHECKSUM_NONE。

if (skb->ip_summed
== CHECKSUM_PARTIAL)
{

skb_set_transport_header(skb, skb->csum_start
-

skb_headroom(skb));

if (!(dev->features
& NETIF_F_GEN_CSUM)
&&

(!(dev->features
& NETIF_F_IP_CSUM)
||

skb->protocol
!=
htons(ETH_P_IP)))

if (skb_checksum_help(skb))

goto out_kfree_skb;

}

gso:

spin_lock_prefetch(&dev->queue_lock);

/* Disable soft irqs for various locks below. Also

* stops preemption for RCU.

*/

rcu_read_lock_bh();

/* Updates of qdisc are serialized by queue_lock.

* The struct Qdisc which is pointed to by qdisc is now a

* rcu structure - it may be accessed without acquiring

* a lock (but the structure may be stale.) The freeing of the

* qdisc will be deferred until it's known that there are no

* more references to it.

*

* If the qdisc has an enqueue function, we still need to

* hold the queue_lock before calling it, since queue_lock

* also serializes access to the device queue.

*/

//struct net_device 的成员qdisc 是一个发送队列, 缓冲等待网络设备进

行发送的skb

//如果网络设备设置了这个队列,则把skb 加到这个队列中,并启动队列的

发送。

q = rcu_dereference(dev->qdisc);

#ifdef CONFIG_NET_CLS_ACT

skb->tc_verd
= SET_TC_AT(skb->tc_verd,AT_EGRESS);

#endif

if (q->enqueue)
{

/* Grab device queue */

spin_lock(&dev->queue_lock);

q = dev->qdisc;

if (q->enqueue)
{

rc = q->enqueue(skb, q);

qdisc_run(dev);

spin_unlock(&dev->queue_lock);

rc = rc == NET_XMIT_BYPASS
? NET_XMIT_SUCCESS
: rc;

goto out;

}

spin_unlock(&dev->queue_lock);

}

//否则如果网络设备没有设置了这个队列,则

/* The device has no queue. Common case for software devices:

loopback, all the sorts of tunnels...

Really, it is unlikely that netif_tx_lock protection is necessary

here. (f.e. loopback and IP tunnels are clean ignoring statistics

counters.)

However, it is possible, that they rely on protection made by us

here.

Check this and shot the lock. It is not prone from deadlocks.

Either shot noqueue qdisc, it is even simpler 8)

*/

//如果网络设备处于启用状态,则直接调用网络设备的输出函数进行发送,

////但在发送前,还需要做一件事情,就是,如果有ETH_P_ALL 数据报类型被添

加

//到ptype_all 中来,则需要把数据报复制一份给这个数据报类型的接收函数,

//因为该类型需要接收到所有的数据报,包括输出的数据报。

if (dev->flags
& IFF_UP)
{

int cpu = smp_processor_id();
/* ok because BHs are off */

if (dev->xmit_lock_owner
!= cpu)
{

HARD_TX_LOCK(dev, cpu);

if (!netif_queue_stopped(dev))
{

rc = 0;

if (!dev_hard_start_xmit(skb, dev))
{

HARD_TX_UNLOCK(dev);

goto out;

}

}

HARD_TX_UNLOCK(dev);

if (net_ratelimit())

printk(KERN_CRIT "Virtual device %s asks to "

"queue packet!\n", dev->name);

} else
{

/* Recursion is detected! It is possible,

* unfortunately */

if (net_ratelimit())

printk(KERN_CRIT "Dead loop on virtual device "

"%s, fix it urgently!\n", dev->name);

}

}

rc = -ENETDOWN;

rcu_read_unlock_bh();

out_kfree_skb:

kfree_skb(skb);

return rc;

out:

rcu_read_unlock_bh();

return rc;

}

如果有发送缓存队列qDisk 则

qdisc_run(src/include/net/pkt_sched.h)

static
inline void qdisc_run(struct net_device
*dev)

{

if (!netif_queue_stopped(dev)
&&

!test_and_set_bit(__LINK_STATE_QDISC_RUNNING,
&dev->state))

__qdisc_run(dev);

}

__qdisc_run(src/include/net/pkt_sched.h)

void __qdisc_run(struct net_device
*dev)

{

do {

if (!qdisc_restart(dev))

break;

} while
(!netif_queue_stopped(dev));

clear_bit(__LINK_STATE_QDISC_RUNNING,
&dev->state);

}

qdisc_restart(src/include/net/pkt_sched.h)

启动设备,发送数据.把数据出列。

/* Kick device.

Returns: 0 - queue is empty or throttled.

>0 - queue is not empty.

NOTE: Called under dev->queue_lock with locally disabled BH.

*/

static inline
int qdisc_restart(struct net_device
*dev)

{

struct Qdisc *q
= dev->qdisc;

struct sk_buff *skb;

/* Dequeue packet */

if (((skb
= dev->gso_skb))
||
((skb = q->dequeue(q))))
{

unsigned nolock =
(dev->features
& NETIF_F_LLTX);

dev->gso_skb
= NULL;

/*

* When the driver has LLTX set it does its own locking

* in start_xmit. No need to add additional overhead by

* locking again. These checks are worth it because

* even uncongested locks can be quite expensive.

* The driver can do trylock like here too, in case

* of lock congestion it should return -1 and the packet

* will be requeued.

*/

if (!nolock)
{

if (!netif_tx_trylock(dev))
{

collision:

/* So, someone grabbed the driver. */

/* It may be transient configuration error,

when hard_start_xmit() recurses. We detect

it by checking xmit owner and drop the

packet when deadloop is detected.

*/

if (dev->xmit_lock_owner
== smp_processor_id())
{

kfree_skb(skb);

if (net_ratelimit())

printk(KERN_DEBUG
"Dead loop on netdevice %s, fix

it urgently!\n", dev->name);

goto out;

}

__get_cpu_var(netdev_rx_stat).cpu_collision++;

goto requeue;

}

}

{

/* And release queue */

spin_unlock(&dev->queue_lock);

if (!netif_queue_stopped(dev))
{

int ret;

ret = dev_hard_start_xmit(skb, dev);

if (ret
== NETDEV_TX_OK)
{

if (!nolock)
{

netif_tx_unlock(dev);

}

spin_lock(&dev->queue_lock);

q = dev->qdisc;

goto out;

}

if (ret
== NETDEV_TX_LOCKED
&& nolock)
{

spin_lock(&dev->queue_lock);

q = dev->qdisc;

goto collision;

}

}

/* NETDEV_TX_BUSY - we need to requeue */

/* Release the driver */

if (!nolock)
{

netif_tx_unlock(dev);

}

spin_lock(&dev->queue_lock);

q = dev->qdisc;

}

/* Device kicked us out :(

This is possible in three cases:

0. driver is locked

1. fastroute is enabled

2. device cannot determine busy state

before start of transmission (f.e. dialout)

3. device is buggy (ppp)

*/

requeue:

if (unlikely(q
==
&noop_qdisc))

kfree_skb(skb);

else if
(skb->next)

dev->gso_skb
= skb;

else

q->ops->requeue(skb, q);

netif_schedule(dev);

}

return 0;

out:

BUG_ON((int) q->q.qlen
< 0);

return q->q.qlen;

}



调用dev_hard_start_xmit(src/net/core/dev.c)

如果没有发送缓存队列qDisk 则直接dev_hard_start_xmit 发送

所有设备的驱动程序的传输调用函数的调用函数接口

int dev_hard_start_xmit(struct sk_buff
*skb,
struct net_device *dev)

{

if (likely(!skb->next))
{

if (!list_empty(&ptype_all))

//这也只是一个包装函数,它首先看有没有注册的sniffer,要是存在的话

(netdev_nit 不等于0),

//便将一个副本通过dev_queue_xmit_nit(skb, dev)发送给它

dev_queue_xmit_nit(skb, dev);

if (netif_needs_gso(dev, skb))
{

if (unlikely(dev_gso_segment(skb)))

goto out_kfree_skb;

if (skb->next)

goto gso;

}

//调用驱动程序的hard_start_xmit 完成最后的发送工作了:

return dev->hard_start_xmit(skb, dev);

}

gso:

do {

struct sk_buff *nskb
= skb->next;

int rc;

skb->next
= nskb->next;

nskb->next
= NULL;

//调用驱动程序的hard_start_xmit 完成最后的发送工作了:

rc = dev->hard_start_xmit(nskb, dev);

if (unlikely(rc))
{

nskb->next
= skb->next;

skb->next
= nskb;

return rc;

}

if (unlikely(netif_queue_stopped(dev)
&& skb->next))

return NETDEV_TX_BUSY;

} while
(skb->next);

skb->destructor
= DEV_GSO_CB(skb)->destructor;

out_kfree_skb:

kfree_skb(skb);

return 0;

}

网卡驱动中包的传输函数(dev. hard_start_xmit)(rtl8139_start_xmit)

Dev‐>hard_start_xmit()只要是跟硬件打交道,一般是通知DMA完成数据的发送工作。

① 检查这个要发送的数据包的长度,如果它达不到以太网帧的长度,必须采取措施进行填

充。

② 把包的数据拷贝到我们已经建立好的发送缓存中。

memcpy (tp->tx_buf[entry], skb->data, skb->len);

③ 光有了地址和数据还不行,我们要让网卡知道这个包的长度,才能保证数据不多不少精

确的从缓存中截取出来搬运到网卡中去,这是靠写发送状态寄存器(TSD)来完成的。

④ 判断发送缓存是否已经满了,如果满了在发就覆盖数据了,要停发。

static
int rtl8139_start_xmit
(struct sk_buff
*skb, struct net_device

*dev)

{

struct rtl8139_private
*tp = dev->priv;

void *ioaddr
= tp->mmio_addr;

unsigned int entry;

unsigned int len
= skb->len;

/* Calculate the next Tx descriptor entry. */

///* Number of Tx descriptor registers. */

//#define NUM_TX_DESC 4

//8139 支援四个传送缓冲区,你必须挑出下一个要用的缓冲区。接下来,

//你必须把缓冲区记忆体的实体表址 (physical address) 设定到 8139 的暂存器

中。

entry = tp->cur_tx
% NUM_TX_DESC;

/* Note: the chip doesn't have auto-pad! */

//这一段程序用来设定封包的长度,一个正确的 ethernet 封包必须至少有 64 位元组

长。

//不幸的,8139 不管这件事,你设定多长它就送多少。上面这一行程序就在确定封包的

长度至少有 ETH_ZLEN。

if (likely(len
< TX_BUF_SIZE))
{

if (len
< ETH_ZLEN)

memset(tp->tx_buf[entry],
0, ETH_ZLEN);

//把待发送的数据拷贝到发送缓冲区中

skb_copy_and_csum_dev(skb, tp->tx_buf[entry]);

dev_kfree_skb(skb);

} else
{

dev_kfree_skb(skb);

tp->stats.tx_dropped++;

return 0;

}

spin_lock_irq(&tp->lock);

//把发送缓冲区的地址写入发送缓冲区地址寄存器,之后网卡芯片就会把数

据发送出去,

//发送结束的时候会发出中断请求。

RTL_W32_F (TxStatus0
+ (entry *
sizeof (u32)),

tp->tx_flag
| max(len,
(unsigned
int)ETH_ZLEN));

dev->trans_start
= jiffies;

tp->cur_tx++;

wmb();

//当缓冲区用完时必须通知上层不要再送封包下来了。

if ((tp->cur_tx
- NUM_TX_DESC)
== tp->dirty_tx)

netif_stop_queue (dev);

spin_unlock_irq(&tp->lock);

if (netif_msg_tx_queued(tp))

printk (KERN_DEBUG
"%s: Queued Tx packet size %u to slot %d.\n",

dev->name, len, entry);

return 0;

}

发送完毕中断函数(rtl8139_tx_interrupt)

static
void rtl8139_tx_interrupt
(struct net_device
*dev,

struct rtl8139_private
*tp,

void *ioaddr)

{

unsigned long dirty_tx, tx_left;

assert (dev
!=
NULL);

assert (tp
!= NULL);

assert (ioaddr
!=
NULL);

dirty_tx = tp->dirty_tx;

tx_left = tp->cur_tx
- dirty_tx;

while (tx_left
> 0)
{

int entry = dirty_tx
% NUM_TX_DESC;

int txstatus;

txstatus = RTL_R32
(TxStatus0 +
(entry * sizeof
(u32)));

if (!(txstatus
& (TxStatOK
| TxUnderrun | TxAborted)))

break;
/* It still hasn't been Txed */

/* Note: TxCarrierLost is always asserted at 100mbps. */

if (txstatus
& (TxOutOfWindow
| TxAborted))
{

/* There was an major error, log it. */

if (netif_msg_tx_err(tp))

printk(KERN_DEBUG
"%s: Transmit error, Tx

status %8.8x.\n",

dev->name, txstatus);

tp->stats.tx_errors++;

if (txstatus
& TxAborted)
{

tp->stats.tx_aborted_errors++;

RTL_W32 (TxConfig, TxClearAbt);

RTL_W16 (IntrStatus, TxErr);

wmb();

}

if (txstatus
& TxCarrierLost)

tp->stats.tx_carrier_errors++;

if (txstatus
& TxOutOfWindow)

tp->stats.tx_window_errors++;

} else
{

if (txstatus
& TxUnderrun)
{

/* Add 64 to the Tx FIFO threshold. */

if (tp->tx_flag
< 0x00300000)

tp->tx_flag
+= 0x00020000;

tp->stats.tx_fifo_errors++;

}

tp->stats.collisions
+=
(txstatus >> 24)
& 15;

tp->stats.tx_bytes
+= txstatus
& 0x7ff;

tp->stats.tx_packets++;

}

dirty_tx++;

tx_left--;

}

#ifndef RTL8139_NDEBUG

if (tp->cur_tx
- dirty_tx > NUM_TX_DESC)
{

printk (KERN_ERR "%s: Out-of-sync dirty pointer, %ld vs. %ld.\n",

dev->name, dirty_tx, tp->cur_tx);

dirty_tx += NUM_TX_DESC;

}

#endif
/* RTL8139_NDEBUG */

/* only wake the queue if we did work, and the queue is stopped */

if (tp->dirty_tx
!= dirty_tx)
{

tp->dirty_tx
= dirty_tx;

mb();

netif_wake_queue (dev);

}

}

当上层协议驱动要发送数据的时候,最终会调用到hard_start_xmit 指定的函数。发送

过层很简单,只需要把待发数据拷贝到缓冲区里面,然后在把缓冲区的地址写如发送地址寄

存器就可以了。

发生错误的中断处理(rtl8139_weird_interrupt)

static
void rtl8139_weird_interrupt
(struct net_device
*dev,

struct rtl8139_private
*tp,

void __iomem *ioaddr,

int status,
int link_changed)

{

DPRINTK ("%s: Abnormal interrupt, status %8.8x.\n",

dev->name, status);

assert (dev
!=
NULL);

assert (tp
!= NULL);

assert (ioaddr
!=
NULL);

/* Update the error count. */

tp->stats.rx_missed_errors
+= RTL_R32
(RxMissed);

RTL_W32 (RxMissed, 0);

if ((status
& RxUnderrun)
&& link_changed
&&

(tp->drv_flags
& HAS_LNK_CHNG))
{

rtl_check_media(dev, 0);

status &=
~RxUnderrun;

}

if (status
& (RxUnderrun
| RxErr))

tp->stats.rx_errors++;

if (status
& PCSTimeout)

tp->stats.rx_length_errors++;

if (status
& RxUnderrun)

tp->stats.rx_fifo_errors++;

if (status
& PCIErr) {

u16 pci_cmd_status;

pci_read_config_word (tp->pci_dev, PCI_STATUS,

&pci_cmd_status);

pci_write_config_word (tp->pci_dev, PCI_STATUS,

pci_cmd_status);

printk (KERN_ERR "%s: PCI Bus error %4.4x.\n",

dev->name, pci_cmd_status);

}

}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: