标签:openwrt linux kernel 路由器 数据
之前一篇写的不完整,重新写一篇
OpenWRT数据发送过程 这里使用的是ath9k网卡驱动,硬件平台是TP-link TL-WR841N V7.1 路由器
1. packet_sendmsg()
Linux kernel发送数据的接口函数是packet_sendmsg,本质上对应了user space的sendmsg实现。上层通过调用sendmsg实现数据的发送。将待发送的数据放入kernel space中。
在内核文件夹linux-3.3.8的子目录:/net/packet中,找到文件af_packet.c,这个文件里定义了如下一个结构:
static const struct proto_ops packet_ops = {
…
.sendmsg = packet_sendmsg,
.recvmsg = packet_recvmsg,
…
};这个结构采用了C99标准的初始化方式,基本用法是“.成员=变量值”(已经预先定义了一个proto_ops结构,里面包含sendmsg函数指针)。这个结构中把上层的sendmsg函数和下层的packet_sendmsg函数对应了起来。上层通过调用sendmsg就将数据传递给了packet_sendmsg函数。下面我们就从内核态的packet_sendmsg出发来研究一下数据的发送过程。static int packet_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len)
{
struct sock *sk = sock->sk;
struct packet_sock *po = pkt_sk(sk);
if (po->tx_ring.pg_vec)
return tpacket_snd(po, msg);
else
return packet_snd(sock, msg, len);
}static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
{
...
// 首先把数据从user space拷贝到kernel space
err = memcpy_fromiovec((void *)&vnet_hdr, msg->msg_iov, vnet_hdr_len);
...
/*
* Now send it
*/
// 然后用dev_queue_xmit()来发送skb.
err = dev_queue_xmit(skb);
if (err > 0 && (err = net_xmit_errno(err)) != 0)
goto out_unlock;
...
}int dev_queue_xmit(struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
struct netdev_queue *txq;
struct Qdisc *q;
int rc = -ENOMEM;
skb_reset_mac_header(skb);
/* Disable soft irqs for various locks below. Also
* stops preemption for RCU.
*/
rcu_read_lock_bh();
skb_update_prio(skb);
txq = netdev_pick_tx(dev, skb);
q = rcu_dereference_bh(txq->qdisc);
#ifdef CONFIG_NET_CLS_ACT
skb->tc_verd = SET_TC_AT(skb->tc_verd, AT_EGRESS);
#endif
trace_net_dev_queue(skb);
if (q->enqueue) {
rc = __dev_xmit_skb(skb, q, dev, txq);
goto out;
}
...
}static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q, struct net_device *dev, struct netdev_queue *txq)
{
...
if (unlikely(test_bit(__QDISC_STATE_DEACTIVATED, &q->state))) {
kfree_skb(skb);
rc = NET_XMIT_DROP;
} else if ((q->flags & TCQ_F_CAN_BYPASS) && !qdisc_qlen(q) &&
qdisc_run_begin(q)) {
/*
* This is a work-conserving queue; there are no old skbs
* waiting to be sent out; and the qdisc is not running -
* xmit the skb directly.
*/
if (!(dev->priv_flags & IFF_XMIT_DST_RELEASE))
skb_dst_force(skb);
qdisc_bstats_update(q, skb);
// 注意这里
if (sch_direct_xmit(skb, q, dev, txq, root_lock)) {
if (unlikely(contended)) {
spin_unlock(&q->busylock);
contended = false;
}
__qdisc_run(q);
} else
qdisc_run_end(q);
rc = NET_XMIT_SUCCESS;
}
...
}/*
* Transmit one skb, and handle the return status as required. Holding the
* __QDISC_STATE_RUNNING bit guarantees that only one CPU can execute this
* function.
*
* Returns to the caller:
* 0 - queue is empty or throttled.
* >0 - queue is not empty.
*/
int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q,
struct net_device *dev, struct netdev_queue *txq,
spinlock_t *root_lock)
{
...
if (!netif_xmit_frozen_or_stopped(txq))
ret = dev_hard_start_xmit(skb, dev, txq);
...
}int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, struct netdev_queue *txq)
{
...
do {
struct sk_buff *nskb = skb->next;
skb->next = nskb->next;
nskb->next = NULL;
/*
* If device doesn‘t need nskb->dst, release it right now while
* its hot in this cpu cache
*/
if (dev->priv_flags & IFF_XMIT_DST_RELEASE)
skb_dst_drop(nskb);
if (!list_empty(&ptype_all))
dev_queue_xmit_nit(nskb, dev);
skb_len = nskb->len;
// 调用了ndo_start_xmit
rc = ops->ndo_start_xmit(nskb, dev);
trace_net_dev_xmit(nskb, rc, dev, skb_len);
if (unlikely(rc != NETDEV_TX_OK)) {
if (rc & ~NETDEV_TX_MASK)
goto out_kfree_gso_skb;
nskb->next = skb->next;
skb->next = nskb;
return rc;
}
txq_trans_update(txq);
if (unlikely(netif_xmit_stopped(txq) && skb->next))
return NETDEV_TX_BUSY;
} while (skb->next);
...
}int ieee80211_register_hw(struct ieee80211_hw *hw)
{
...
/* add one default STA interface if supported */
if (local->hw.wiphy->interface_modes &BIT(NL80211_IFTYPE_STATION)) {
result = ieee80211_if_add(local, "wlan%d", NULL,
NL80211_IFTYPE_STATION, NULL);
if (result)
wiphy_warn(local->hw.wiphy, "Failed to add default virtual iface\n");
}
...
}
EXPORT_SYMBOL(ieee80211_register_hw);int ieee80211_if_add(struct ieee80211_local *local, const char *name, struct wireless_dev **new_wdev, enum nl80211_iftype type, struct vif_params *params)
{
struct net_device *ndev = NULL;
struct ieee80211_sub_if_data *sdata = NULL;
int ret, i;
int txqs = 1;
...
/* setup type-dependent data */
ieee80211_setup_sdata(sdata, type);
...
}static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, enum nl80211_iftype type)
{
...
/* only monitor/p2p-device differ */
if (sdata->dev) {
sdata->dev->netdev_ops = &ieee80211_dataif_ops;
sdata->dev->type = ARPHRD_ETHER;
}
skb_queue_head_init(&sdata->skb_queue);
INIT_WORK(&sdata->work, ieee80211_iface_work);
INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work);
...
}从上述代码可以看出,ieee80211_dataif_ops这一组函数指针被注册到设备中。而ieee80211_dataif_ops的定义如下(位于OpenWRT内核文件夹子目录/net/mac80211,文件iface.c中):static const struct net_device_ops ieee80211_dataif_ops = {
.ndo_open = ieee80211_open,
.ndo_stop = ieee80211_stop,
.ndo_uninit = ieee80211_uninit,
.ndo_start_xmit = ieee80211_subif_start_xmit,
.ndo_set_rx_mode = ieee80211_set_multicast_list,
.ndo_change_mtu = ieee80211_change_mtu,
.ndo_set_mac_address = ieee80211_change_mac,
.ndo_select_queue = ieee80211_netdev_select_queue,
};显然,ndo_start_xmit对应的函数是ieee80211_subif_start_xmit()
8. ieee80211_subif_start_xmit()
调用ieee80211_subif_start_xmit()(位于OpenWRT内核文件夹子目录/net/mac80211,文件tx.c中)。
netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
...
ieee80211_xmit(sdata, skb, band);
rcu_read_unlock();
return NETDEV_TX_OK;
}void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, enum ieee80211_band band)
{
...
ieee80211_set_qos_hdr(sdata, skb);
ieee80211_tx(sdata, skb, false, band);
}static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, bool txpending,
enum ieee80211_band band)
{
...
if (!invoke_tx_handlers(&tx))
result = __ieee80211_tx(local, &tx.skbs, led_len,
tx.sta, txpending);
return result;
}static bool__ieee80211_tx(struct ieee80211_local *local, struct sk_buff_head *skbs, intled_len, struct sta_info *sta, bool txpending)
{
struct ieee80211_tx_info *info;
struct ieee80211_sub_if_data *sdata;
struct ieee80211_vif *vif;
struct ieee80211_sta *pubsta;
struct sk_buff *skb;
bool result = true;
__le16 fc;
/*这里首先检查待发送的队列是否为空,如果是空队列,则直接返回,不做其他任何操作*/
if (WARN_ON(skb_queue_empty(skbs)))
return true;
/*把队列中的第一个元素取出来作为待发送的元素*/
skb = skb_peek(skbs);
fc = ((struct ieee80211_hdr*)skb->data)->frame_control;
info = IEEE80211_SKB_CB(skb);
sdata =vif_to_sdata(info->control.vif);
if (sta && !sta->uploaded) /*确定目标sta是否被正确的上载到驱动中,如果没有,则把目标站点置为NULL*/
sta = NULL;
if (sta)
pubsta = &sta->sta;
else
pubsta = NULL;
...
result = ieee80211_tx_frags(local, vif,pubsta, skbs,txpending); /*调用函数ieee80211_tx_frags向目标站点传送信息,返回标记传送成功或失败的bool型变量*/
ieee80211_tpt_led_trig_tx(local, fc,led_len);
WARN_ON_ONCE(!skb_queue_empty(skbs));
return result;
}static bool ieee80211_tx_frags(struct ieee80211_local *local, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct sk_buff_head *skbs, bool txpending)
{
struct ieee80211_tx_control control;
struct sk_buff *skb, *tmp;
unsigned long flags;
skb_queue_walk_safe(skbs, skb, tmp) {
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
int q = info->hw_queue;
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
if (local->queue_stop_reasons[q] ||(!txpending && !skb_queue_empty(&local->pending[q]))) {
if (unlikely(info->flags &IEEE80211_TX_INTFL_OFFCHAN_TX_OK)) {
if (local->queue_stop_reasons[q] &~BIT(IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL)) {
/*
* Drop off-channel frames if queues
* are stopped for any reason other
* than off-channel operation. Never
* queue them.
*/
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
ieee80211_purge_tx_queue(&local->hw,skbs);
return true;
}
} else {
/*
* Since queue is stopped, queue up frames for
* later transmission from the tx-pending
* tasklet when the queue is woken again.
*/
if (txpending)
skb_queue_splice_init(skbs,&local->pending[q]);
else
skb_queue_splice_tail_init(skbs,&local->pending[q]);
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
return false;
}
}
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
info->control.vif = vif;
control.sta = sta;
__skb_unlink(skb, skbs);
drv_tx(local, &control, skb);
}
return true;
}#define skb_queue_walk_safe(queue, skb, tmp) for (skb = (queue)->next, tmp = skb->next; skb != (struct sk_buff *)(queue); skb = tmp, tmp = skb->next)用在这里的含义就是当队列不为空时,对队列中的元素进行操作。
13. drv_tx()
调用drv_tx()(位于OpenWRT内核文件夹子目录/net/mac80211,文件driver-ops.h中)
static inline void drv_tx(struct ieee80211_local *local, struct ieee80211_tx_control *control, struct sk_buff *skb)
{
local->ops->tx(&local->hw, control, skb);
}在ieee80211_alloc_hw()函数体中有这样的代码(位于文件OpenWRT内核文件夹子目录/net/mac80211,文件main.c中):local->ops = ops;而这个ops又是从ieee80211_alloc_hw()的参数传进来的(系统启动进行初始化时执行ath_pci_probe函数调用ieee80211_alloc_hw()进行传递,ath_pci_probe函数定义于OpenWRT内核文件夹子目录/drivers/net/wireless/ath/ath9k/,文件pci.c中),也就是ath9k_ops(定义于OpenWRT内核文件夹子目录/drivers/net/wireless/ath/ath9k/,文件main.c中)。我们在驱动启动过程中提到过。
struct ieee80211_ops ath9k_ops = {
.tx = ath9k_tx,
…
}所以local->ops-tx()实际上就触发了ath9k_tx()
14. ath9k_tx()
调用ath9k_tx()(定义于OpenWRT内核文件夹子目录/drivers/net/wireless/ath/ath9k/,文件main.c中)
static void ath9k_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb)
{
…
if (ath_tx_start(hw, skb, &txctl) != 0) {
ath_dbg(common, XMIT, "TX failed\n");
TX_STAT_INC(txctl.txq->axq_qnum, txfailed);
goto exit;
}
return;
exit:
ieee80211_free_txskb(hw, skb);
}int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, struct ath_tx_control *txctl)
{
…
bf = ath_tx_setup_buffer(sc, txq, tid, skb);
if (!bf) {
ath_txq_skb_done(sc, txq, skb);
if (txctl->paprd)
dev_kfree_skb_any(skb);
else
ieee80211_free_txskb(sc->hw, skb);
goto out;
}
…
}
OpenWRT数据发送过程【Linux内核-OpenWRT】
标签:openwrt linux kernel 路由器 数据
原文地址:http://blog.csdn.net/ussam/article/details/24737265