net_device_ops的ndo_open和ndo_start_xmit函数
net_device_ops的ndo_open 函数实在dev_open->__dev_open 中打开的.
if (!ret && ops->ndo_open)
ret = ops->ndo_open(dev);
static int hns_nic_net_open(struct net_device *ndev)
{
struct hns_nic_priv *priv = netdev_priv(ndev);
struct hnae_handle *h = priv->ae_handle;
int ret;
//如果是testing 则退出
if (test_bit(NIC_STATE_TESTING, &priv->state))
return -EBUSY;
priv->link = 0;
netif_carrier_off(ndev);
//设置tx queue的个数
ret = netif_set_real_num_tx_queues(ndev, h->q_num);
if (ret < 0) {
netdev_err(ndev, "netif_set_real_num_tx_queues fail, ret=%d!\n",
ret);
return ret;
}
//设置rx queue的个数
ret = netif_set_real_num_rx_queues(ndev, h->q_num);
if (ret < 0) {
netdev_err(ndev,
"netif_set_real_num_rx_queues fail, ret=%d!\n", ret);
return ret;
}
ret = hns_nic_net_up(ndev);
if (ret) {
netdev_err(ndev,
"hns net up fail, ret=%d!\n", ret);
return ret;
}
return 0;
}
static int hns_nic_net_up(struct net_device *ndev)
{
struct hns_nic_priv *priv = netdev_priv(ndev);
struct hnae_handle *h = priv->ae_handle;
int i, j;
int ret;
//初始化中断,并设置中断函数为hns_irq_handle,每个rx和tx queue都对应一个中断
ret = hns_nic_init_irq(priv);
if (ret != 0) {
netdev_err(ndev, "hns init irq failed! ret=%d\n", ret);
return ret;
}
for (i = 0; i < h->q_num * 2; i++) {
//使能中断,使能napi
ret = hns_nic_ring_open(ndev, i);
if (ret)
goto out_has_some_queues;
}
//设置mac地址
ret = h->dev->ops->set_mac_addr(h, ndev->dev_addr);
if (ret)
goto out_set_mac_addr_err;
//hns的start函数为null
ret = h->dev->ops->start ? h->dev->ops->start(h) : 0;
if (ret)
goto out_start_err;
//启动phy
if (ndev->phydev)
phy_start(ndev->phydev);
clear_bit(NIC_STATE_DOWN, &priv->state);
修改time 每一秒到期一次
(void)mod_timer(&priv->service_timer, jiffies + SERVICE_TIMER_HZ);
return 0;
out_start_err:
netif_stop_queue(ndev);
out_set_mac_addr_err:
out_has_some_queues:
for (j = i - 1; j >= 0; j--)
hns_nic_ring_close(ndev, j);
set_bit(NIC_STATE_DOWN, &priv->state);
return ret;
}
static int hns_nic_init_irq(struct hns_nic_priv *priv)
{
struct hnae_handle *h = priv->ae_handle;
struct hns_nic_ring_data *rd;
int i;
int ret;
int cpu;
//这里的rx和tx的queue 个数都是h->q_num,因此h->q_num * 2表示为每个rx和tx都申请一个中断
for (i = 0; i < h->q_num * 2; i++) {
rd = &priv->ring_data[i];
if (rd->ring->irq_init_flag == RCB_IRQ_INITED)
break;
snprintf(rd->ring->ring_name, RCB_RING_NAME_LEN,
"%s-%s%d", priv->netdev->name,
(is_tx_ring(rd->ring) ? "tx" : "rx"), rd->queue_index);
rd->ring->ring_name[RCB_RING_NAME_LEN - 1] = '\0';
//中断的处理函数为hns_irq_handle
ret = request_irq(rd->ring->irq,
hns_irq_handle, 0, rd->ring->ring_name, rd);
if (ret) {
netdev_err(priv->netdev, "request irq(%d) fail\n",
rd->ring->irq);
return ret;
}
//为了要设置irq的affinity,暂时disable irq
disable_irq(rd->ring->irq);
cpu = hns_nic_init_affinity_mask(h->q_num, i,
rd->ring, &rd->mask);
//通过irq_set_affinity_hint 设置irq affintiy
if (cpu_online(cpu))
irq_set_affinity_hint(rd->ring->irq,
&rd->mask);
rd->ring->irq_init_flag = RCB_IRQ_INITED;
}
return 0;
}
static int hns_nic_ring_open(struct net_device *netdev, int idx)
{
struct hns_nic_priv *priv = netdev_priv(netdev);
struct hnae_handle *h = priv->ae_handle;
//先是能napi 再使能中断
napi_enable(&priv->ring_data[idx].napi);
enable_irq(priv->ring_data[idx].ring->irq);
h->dev->ops->toggle_ring_irq(priv->ring_data[idx].ring, 0);
return 0;
}
要从协议层想设备中发送数据,会调用dev_queue_xmit函数。
dev_queue_xmit->__dev_queue_xmit->dev_hard_start_xmit->xmit_one->netdev_start_xmit->__netdev_start_xmit
static inline netdev_tx_t __netdev_start_xmit(const struct net_device_ops *ops,
struct sk_buff *skb, struct net_device *dev,
bool more)
{
skb->xmit_more = more ? 1 : 0;
return ops->ndo_start_xmit(skb, dev);
}
最终调用net device自己的ndo_start_xmit函数来发送,下面这个hns_nic_netdev_ops 中的ndo_start_xmit对应的函数是hns_nic_net_xmit
static const struct net_device_ops hns_nic_netdev_ops = {
.ndo_open = hns_nic_net_open,
.ndo_start_xmit = hns_nic_net_xmit,
};
static netdev_tx_t hns_nic_net_xmit(struct sk_buff *skb,
struct net_device *ndev)
{
struct hns_nic_priv *priv = netdev_priv(ndev);
int ret;
assert(skb->queue_mapping < ndev->ae_handle->q_num);
ret = hns_nic_net_xmit_hw(ndev, skb,
&tx_ring_data(priv, skb->queue_mapping));
if (ret == NETDEV_TX_OK) {
netif_trans_update(ndev);
ndev->stats.tx_bytes += skb->len;
ndev->stats.tx_packets++;
}
return (netdev_tx_t)ret;
}
最终调用hns_nic_net_xmit_hw 发送数据
int hns_nic_net_xmit_hw(struct net_device *ndev,
struct sk_buff *skb,
struct hns_nic_ring_data *ring_data)
{
struct hns_nic_priv *priv = netdev_priv(ndev);
struct hnae_ring *ring = ring_data->ring;
struct device *dev = ring_to_dev(ring);
struct netdev_queue *dev_queue;
struct skb_frag_struct *frag;
int buf_num;
int seg_num;
dma_addr_t dma;
int size, next_to_use;
int i;
switch (priv->ops.maybe_stop_tx(&skb, &buf_num, ring)) {
case -EBUSY:
ring->stats.tx_busy++;
goto out_net_tx_busy;
case -ENOMEM:
ring->stats.sw_err_cnt++;
netdev_err(ndev, "no memory to xmit!\n");
goto out_err_tx_ok;
default:
break;
}
/* no. of segments (plus a header) */
seg_num = skb_shinfo(skb)->nr_frags + 1;
next_to_use = ring->next_to_use;
/* fill the first part */
//得到skb的size
size = skb_headlen(skb);
dma = dma_map_single(dev, skb->data, size, DMA_TO_DEVICE);
if (dma_mapping_error(dev, dma)) {
netdev_err(ndev, "TX head DMA map failed\n");
ring->stats.sw_err_cnt++;
goto out_err_tx_ok;
}
//填充需要发送的数据
priv->ops.fill_desc(ring, skb, size, dma, seg_num == 1 ? 1 : 0,
buf_num, DESC_TYPE_SKB, ndev->mtu);
/* fill the fragments */
for (i = 1; i < seg_num; i++) {
frag = &skb_shinfo(skb)->frags[i - 1];
size = skb_frag_size(frag);
dma = skb_frag_dma_map(dev, frag, 0, size, DMA_TO_DEVICE);
if (dma_mapping_error(dev, dma)) {
netdev_err(ndev, "TX frag(%d) DMA map failed\n", i);
ring->stats.sw_err_cnt++;
goto out_map_frag_fail;
}
priv->ops.fill_desc(ring, skb_frag_page(frag), size, dma,
seg_num - 1 == i ? 1 : 0, buf_num,
DESC_TYPE_PAGE, ndev->mtu);
}
/*complete translate all packets*/
dev_queue = netdev_get_tx_queue(ndev, skb->queue_mapping);
netdev_tx_sent_queue(dev_queue, skb->len);
wmb(); /* commit all data before submit */
assert(skb->queue_mapping < priv->ae_handle->q_num);
//触发硬件发送数据
hnae_queue_xmit(priv->ae_handle->qs[skb->queue_mapping], buf_num);
ring->stats.tx_pkts++;
ring->stats.tx_bytes += skb->len;
return NETDEV_TX_OK;
out_map_frag_fail:
while (ring->next_to_use != next_to_use) {
unfill_desc(ring);
if (ring->next_to_use != next_to_use)
dma_unmap_page(dev,
ring->desc_cb[ring->next_to_use].dma,
ring->desc_cb[ring->next_to_use].length,
DMA_TO_DEVICE);
else
dma_unmap_single(dev,
ring->desc_cb[next_to_use].dma,
ring->desc_cb[next_to_use].length,
DMA_TO_DEVICE);
}
out_err_tx_ok:
dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
out_net_tx_busy:
netif_stop_subqueue(ndev, skb->queue_mapping);
/* Herbert's original patch had:
* smp_mb__after_netif_stop_queue();
* but since that doesn't exist yet, just open code it.
*/
smp_mb();
return NETDEV_TX_BUSY;
}
net_device_ops的ndo_open和ndo_start_xmit函数相关推荐
- 【A113】网卡芯片Realtek RTL8201驱动调试
前言: 这一篇是对自己调试的一个过程记录,代码看的算是比较熟悉了,过程中一直没调过,没后询问原厂FAE,修改了一下dts一个地址参数,就可以了,最终没有太多成就感,修改了PHY芯片的一个状态寄存器.. ...
- 网卡驱动(hisi3536网卡驱动,以及stmmac层)
一:基本概念 应用程序最终以套接字的形式完成网络设备的接口.,对网络设备定义四个层次:网络协议接口层(数据包的发送接收,向网络层协议提供统一的数据包收发接口,舍得上层协议独立于具体的设备,(ip,ar ...
- Linux: 网络数据收发流程简析
文章目录 1. 前言 2. 背景 3. 网卡数据收发流程 3.1 网络数据接收流程 3.1.1 网卡数据接收流程 3.1.2 网卡数据向上传递给L3,L4的流程 3.2 网卡数据发送流程 1. 前言 ...
- linux dev queue xmit,dev_queue_xmi函数详解
点击(此处)折叠或打开 int dev_queue_xmit(struct sk_buff *skb) { struct net_device *dev = skb->dev; struct n ...
- Linux内核网络数据发送(六)——网络设备驱动
Linux内核网络数据发送(六)--网络设备驱动 1. 前言 2. 驱动回调函数注册 3. `ndo_start_xmit` 发送数据 4. `igb_tx_map` 1. 前言 本文主要介绍设备通过 ...
- Linux内核网络设备驱动
本文首先从宏观上介绍数据包的接收过程,然后详细介绍了Linux网络设备驱动的工作过程,最后介绍网卡监控与调优,包括网络数据包总数.丢包.错包数量的相关统计. 1. 接收数据包过程概述 介绍数据包收包过 ...
- linux内核网络协议栈--监控和调优:发送数据(三十)
译者序 本文翻译自 2017 年的一篇英文博客 Monitoring and Tuning the Linux Networking Stack: Sending Data.如果能看懂英文,建议阅读原 ...
- linux内核网络协议栈--linux bridge(十九)
1 . 前言 本文是参考附录上的资料整理而成,以帮助读者更好的理解kernel中brdige 模块代码. 2. 网桥的原理 2.1 桥接的概念 简单来说,桥接就是把一台机器上的若干个网络接口" ...
- linux内核网络协议栈--监控和调优:接收数据(十五)
译者序 本文翻译自 2016 年的一篇英文博客 Monitoring and Tuning the Linux Networking Stack: Receiving Data.如果能看懂英文,建议阅 ...
- DPDK KNI实现(二十五)
一.为什么要用kni 通常情况下dpdk用于二三层报文转发,接收到来自网卡的报文后,如果是二层报文则查找fdb表: 如果是三层报文,则进行dnat, snat处理后,查找路由表, 将报文转发给下一跳路 ...
最新文章
- windows中路径\和 linux中用/
- 独家 | 层级聚类和Python实现的初学者指南(附链接)
- PAT (Basic Level) Practice (中文)1076 Wifi密码 (15 分)
- 记录某一天安服仔的漏洞挖掘过程
- OpenGL中shader使用
- 无光驱不支持USB设备启动的笔记本,如何使用Ghost来安装系统
- jupyter notebook使用入门2——创建一个基于scikit-Learn的线性预测ipynb文件
- 01-eclipse打包运行程序总是报错java.lang.NoClassDefFoundError和ava.lang.ClassNotFoundException(打包原理)
- python中3 and not 5_python中not、and和or的优先级与详细用法介绍
- 非线性方程求根算法的C++实现
- 怎么在局域网中设置共享文件夹?
- 树的前序、中序、后序遍历 | Tree Walk | C/C++实现
- Mac获取系统版本、机型
- 2022Android各APP免费加固方案评估
- error怎么开机 fan_台式机开机出现cpu fan error怎么办
- AUTOCAD——修改坐标轴样式
- 【重磅】人民网:分布式存储打开千亿级市场,深入推动行业数字化转型
- \\ip 映射 指定的网络名不再可用
- 小散量化炒股记|股价如波浪起伏,教你用量化识别波段的极值点
- spring开发_Annotation_AOP_Before增强处理
热门文章
- PPT怎么画出好看的三维示意图
- html5 audio duration,记一次vue中获取audio媒体总时长duration遇到的问题
- 傻瓜式自制鼠标光标,超简单
- html 空格 正则表达式,正则表达式清除空格和html标签中的 空格
- python高频词汇表大全_我们用程序整理出了一份Python英语高频词汇表,拿走不谢!...
- ipad显示portal服务器获取不,苹果portal认证失败原因合集
- python第七天作业
- js字符串时间格式与中国标准时间格式相互转换
- php生成中国标准时间,中国时间标准
- GTC '19 经典回顾 | 如何编排和创造二次元中的舞蹈?