标签: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