ixgbe 驱动 为xxx驱动做准备1
网卡都是pci设备,因此这里每个网卡驱动其实就是一个pci驱动。并且intel这里是把好几个万兆网卡(82599/82598/x540)的驱动做在一起的。V4L2 一样几个类型摄像头合并在一起
先说一下 驱动总线平台;实际上就是platform_device(设备)与platform_driver(驱动)在platform(虚拟总线)上的注册、匹配,相互绑定。 但是最核心的还是cdev file_opterations 等
- :struct bus_type-->它包含的最关键的函数:match() (要注意的是,这块由内核完成,我们不参与)
- :struct platform_device-->注册:platform_device_register(unregister)
- :struct platform_driver-->注册:platform_driver_register(unregister)
设备(或驱动)注册的时候,都会引发总线调用自己的match函数来寻找目前platform总线是否挂载有与该设备(或驱动)名字匹配的驱动(或设备),如果存在则将双方绑定;
platform_device:提供设备的硬件资源 比如 中断号 寄存器地址 DMA
platform_driver : 提供 match id 绑定驱动和device资源的回调probe
详细 就不说了 很简单。。。。 之前在csdn上写过。。后续慢慢找回吧!!!!!!
假设:device先注册, 后续注册driver时,根据platform_match 匹配到了设备;
那么就会调用probe 实现资源的绑定!!! 一般都是 创建设备 注册中断 初始化 file_opteriton 等回调函数
看下 ixgbe_probe 就知道:
static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct net_device *netdev;
struct ixgbe_adapter *adapter = NULL;
struct ixgbe_hw *hw;
/*
static const struct ixgbe_info *ixgbe_info_tbl[] = {
[board_82598] = &ixgbe_82598_info,
[board_82599] = &ixgbe_82599_info,
[board_X540] = &ixgbe_X540_info,
[board_X550] = &ixgbe_X550_info,
[board_X550EM_x] = &ixgbe_X550EM_x_info,
[board_x550em_a] = &ixgbe_x550em_a_info,
};有很多网卡,根据mactch的记过选择对用的设备信息
*/
const struct ixgbe_info *ii = ixgbe_info_tbl[ent->driver_data];
根据硬件的参数来取得对应的devices info
通过对应的netdev的结构取得adapter,然后所有的核心操作都是保存在adapter中:
netdev = alloc_etherdev_mq(sizeof(struct ixgbe_adapter), indices);
// 分配net_device和ixgbe_adapter,发送队列数为MAX_TX_QUEUE
if (!netdev) {
err = -ENOMEM;
goto err_alloc_etherdev;
} SET_NETDEV_DEV(netdev, &pdev->dev); adapter = netdev_priv(netdev);// 得到ixgbe_adapter的指针 adapter->netdev = netdev;
adapter->pdev = pdev;
hw = &adapter->hw;// 得到ixgbe_hw的指针
hw->back = adapter;
adapter->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE);
// 将BAR0中的总线地址映射成内存地址,赋给hw->hw_addr,允许网卡驱动通过hw->hw_addr访问网卡的BAR0对应的Memory空间
hw->hw_addr = ioremap(pci_resource_start(pdev, 0),
pci_resource_len(pdev, 0));
adapter->io_addr = hw->hw_addr;
netdev->netdev_ops = &ixgbe_netdev_ops;// 注册ixgbe_netdev_ops
ixgbe_set_ethtool_ops(netdev);
netdev->watchdog_timeo = 5 * HZ;
strlcpy(netdev->name, pci_name(pdev), sizeof(netdev->name)); /* Setup hw api */
hw->mac.ops = *ii->mac_ops;
hw->mac.type = ii->mac;
hw->mvals = ii->mvals; /* EEPROM */
hw->eeprom.ops = *ii->eeprom_ops;
eec = IXGBE_READ_REG(hw, IXGBE_EEC(hw));
if (ixgbe_removed(hw->hw_addr)) {
err = -EIO;
goto err_ioremap;
}
/* If EEPROM is valid (bit 8 = 1), use default otherwise use bit bang */
if (!(eec & BIT(8)))
hw->eeprom.ops.read = &ixgbe_read_eeprom_bit_bang_generic; /* PHY 硬件资源的初始化。。。。。。。。*/
hw->phy.ops = *ii->phy_ops;
hw->phy.sfp_type = ixgbe_sfp_type_unknown;
/* ixgbe_identify_phy_generic will set prtad and mmds properly */
hw->phy.mdio.prtad = MDIO_PRTAD_NONE;
hw->phy.mdio.mmds = 0;
hw->phy.mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22;
hw->phy.mdio.dev = netdev;
hw->phy.mdio.mdio_read = ixgbe_mdio_read;
hw->phy.mdio.mdio_write = ixgbe_mdio_write; ii->get_invariants(hw);
设置网卡属性 ethtool 可以查看
#define IXGBE_GSO_PARTIAL_FEATURES (NETIF_F_GSO_GRE | \
NETIF_F_GSO_GRE_CSUM | \
NETIF_F_GSO_IPXIP4 | \
NETIF_F_GSO_IPXIP6 | \
NETIF_F_GSO_UDP_TUNNEL | \
NETIF_F_GSO_UDP_TUNNEL_CSUM) netdev->gso_partial_features = IXGBE_GSO_PARTIAL_FEATURES;
netdev->features |= NETIF_F_GSO_PARTIAL |
IXGBE_GSO_PARTIAL_FEATURES; if (hw->mac.type >= ixgbe_mac_82599EB)
netdev->features |= NETIF_F_SCTP_CRC;ixgbe_msix_clean_rings /* copy netdev features into list of user selectable features */
netdev->hw_features |= netdev->features |
NETIF_F_HW_VLAN_CTAG_RX |
NETIF_F_HW_VLAN_CTAG_TX |
NETIF_F_RXALL |
NETIF_F_HW_L2FW_DOFFLOAD; if (hw->mac.type >= ixgbe_mac_82599EB)
netdev->hw_features |= NETIF_F_NTUPLE |
NETIF_F_HW_TC; if (pci_using_dac)
netdev->features |= NETIF_F_HIGHDMA; netdev->vlan_features |= netdev->features | NETIF_F_TSO_MANGLEID;
netdev->hw_enc_features |= netdev->vlan_features;
还有注册中断
在enable 设备时 就会ixgbe_request_irq
err = request_irq(entry->vector, &ixgbe_msix_clean_rings, 0,
q_vector->name, q_vector);
static irqreturn_t ixgbe_msix_clean_rings(int irq, void *data)
{
struct ixgbe_q_vector *q_vector = data;
/* EIAM disabled interrupts (on this vector) for us */
if (q_vector->rx.ring || q_vector->tx.ring)
napi_schedule_irqoff(&q_vector->napi);
return IRQ_HANDLED;
}
也就是 注册了napi 回调函数。。。 最后执行其对应的poll
/* initialize NAPI */
netif_napi_add(adapter->netdev, &q_vector->napi,
ixgbe_poll, 64);
void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
int (*poll)(struct napi_struct *, int), int weight)
{
INIT_LIST_HEAD(&napi->poll_list);
hrtimer_init(&napi->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
napi->timer.function = napi_watchdog;
napi->gro_count = 0;
napi->gro_list = NULL;
napi->skb = NULL;
napi->poll = poll;
if (weight > NAPI_POLL_WEIGHT)
pr_err_once("netif_napi_add() called with weight %d on device %s\n",
weight, dev->name);
napi->weight = weight;
list_add(&napi->dev_list, &dev->napi_list);
napi->dev = dev;
#ifdef CONFIG_NETPOLL
spin_lock_init(&napi->poll_lock);
napi->poll_owner = -1;
#endif
set_bit(NAPI_STATE_SCHED, &napi->state);
napi_hash_add(napi);
}
注册网络设备dev
strcpy(netdev->name, "eth%d");
err = register_netdev(netdev);----->if (dev->netdev_ops->ndo_init) {ret = dev->netdev_ops->ndo_init(dev)
netdev->netdev_ops = &ixgbe_netdev_ops;
poll:NAPI驱动最终是会调用网卡驱动挂载的poll回调,在ixgbe中,对应的回调就是ixgbe_poll
/**
* ixgbe_poll - NAPI Rx polling callback
* @napi: structure for representing this polling device
* @budget: how many packets driver is allowed to clean
*
* This function is used for legacy and MSI, NAPI mode
**/
int ixgbe_poll(struct napi_struct *napi, int budget)
{
struct ixgbe_q_vector *q_vector =
container_of(napi, struct ixgbe_q_vector, napi);
struct ixgbe_adapter *adapter = q_vector->adapter;
struct ixgbe_ring *ring;
int per_ring_budget, work_done = 0;
bool clean_complete = true; #ifdef CONFIG_IXGBE_DCA
if (adapter->flags & IXGBE_FLAG_DCA_ENABLED)
ixgbe_update_dca(q_vector);
#endif ixgbe_for_each_ring(ring, q_vector->tx) { TX
if (!ixgbe_clean_tx_irq(q_vector, ring, budget))
clean_complete = false;
} /* Exit if we are called by netpoll or busy polling is active */
if ((budget <= 0) || !ixgbe_qv_lock_napi(q_vector))
return budget; /* attempt to distribute budget to each queue fairly, but don't allow
* the budget to go below 1 because we'll exit polling */
if (q_vector->rx.count > 1)
per_ring_budget = max(budget/q_vector->rx.count, 1);
else
per_ring_budget = budget; ixgbe_for_each_ring(ring, q_vector->rx) {RX
int cleaned = ixgbe_clean_rx_irq(q_vector, ring,
per_ring_budget); work_done += cleaned;
if (cleaned >= per_ring_budget)
clean_complete = false;
} ixgbe_qv_unlock_napi(q_vector);
/* If all work not completed, return budget and keep polling */
if (!clean_complete)
return budget; /* all work done, exit the polling mode */
napi_complete_done(napi, work_done);
if (adapter->rx_itr_setting & 1)
ixgbe_set_itr(q_vector);
if (!test_bit(__IXGBE_DOWN, &adapter->state))
ixgbe_irq_enable_queues(adapter, BIT_ULL(q_vector->v_idx)); return min(work_done, budget - 1);
}
ndo_start_xmit---->ixgbe_xmit_frame,这个回调就是驱动提供给协议栈的发送回调接口。我们来看这个函数.
它的实现很简单,就是选取对应的队列,然后调用ixgbe_xmit_frame_ring来发送数据。
static netdev_tx_t __ixgbe_xmit_frame(struct sk_buff *skb,
struct net_device *netdev,
struct ixgbe_ring *ring)
{
struct ixgbe_adapter *adapter = netdev_priv(netdev);
struct ixgbe_ring *tx_ring; /*
* The minimum packet size for olinfo paylen is 17 so pad the skb
* in order to meet this minimum size requirement.
*/
if (skb_put_padto(skb, 17))
return NETDEV_TX_OK; tx_ring = ring ? ring : adapter->tx_ring[skb->queue_mapping]; return ixgbe_xmit_frame_ring(skb, adapter, tx_ring);
}
用ixgbe_tx_map来发送数据。而ixgbe_tx_map所做的最主要是两步,第一步请求DMA,第二步写寄存器,通知网卡发送数据.
netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *skb,
struct ixgbe_adapter *adapter,
struct ixgbe_ring *tx_ring)
{
struct ixgbe_tx_buffer *first;
int tso;
u32 tx_flags = 0;
unsigned short f;
u16 count = TXD_USE_COUNT(skb_headlen(skb));
__be16 protocol = skb->protocol;
u8 hdr_len = 0; /*
* need: 1 descriptor per page * PAGE_SIZE/IXGBE_MAX_DATA_PER_TXD,
* + 1 desc for skb_headlen/IXGBE_MAX_DATA_PER_TXD,
* + 2 desc gap to keep tail from touching head,
* + 1 desc for context descriptor,
* otherwise try next time
*/
for (f = 0; f < skb_shinfo(skb)->nr_frags; f++)
count += TXD_USE_COUNT(skb_shinfo(skb)->frags[f].size); if (ixgbe_maybe_stop_tx(tx_ring, count + 3)) {
tx_ring->tx_stats.tx_busy++;
return NETDEV_TX_BUSY;
} /* record the location of the first descriptor for this packet */
first = &tx_ring->tx_buffer_info[tx_ring->next_to_use];
first->skb = skb;
first->bytecount = skb->len;
first->gso_segs = 1; -------------------------------------
ixgbe_tx_map(tx_ring, first, hdr_len); return NETDEV_TX_OK; out_drop:
dev_kfree_skb_any(first->skb);
first->skb = NULL; return NETDEV_TX_OK;
}
上面的操作是异步的,此时内核还不能释放SKB,而是网卡硬件发送完数据之后,会再次产生中断通知内核,然后内核才能释放内存 ; 此时可以看到 napi的poll 回调函数 会清除tx 队列
ixgbe 驱动 为xxx驱动做准备1的更多相关文章
- Windows网络驱动、NDIS驱动(微端口驱动、中间层驱动、协议驱动)、TDI驱动(网络传输层过滤)、WFP(Windows Filtering Platform)
catalog . 引言 . Windows 2000网络结构和OSI模型 . NDIS驱动 . NDIS微端口驱动编程实例 . NDIS中间层驱动编程实例 . NDIS协议层驱动编程实例 . TDI ...
- spi驱动框架全面分析,从master驱动到设备驱动
内核版本:linux2.6.32.2 硬件资源:s3c2440 参考: 韦东山SPI视频教程 内容概括: 1.I2C 驱动框架回顾 2.SPI 框架简单介绍 3.maste ...
- WDM驱动和NT驱动之我见
WDM驱动是NT驱动的进化版.我个人觉得它的主要好处有两个 1.能检测到设备的插入,系统能自动分配设备的硬件信息,如中断号.IO端口.设备物理地址等 2.支持设备的开机状态拔出 之前的NT驱动和硬件关 ...
- 字符设备驱动、平台设备驱动、设备驱动模型、sysfs的比较和关联
转载自:http://www.kancloud.cn/yueqian_scut/emlinux/106829 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动.设备驱动模型和sy ...
- 乾坤合一~Linux设备驱动之终端设备驱动
多想拥你在我的怀里 却无法超越那距离 美好回忆渐渐地远去 盼望今生出现奇迹 无尽的想念 荒了容颜 无助的爱恋 从未改变 这是今天的旋律,,,,今生今世,遥不可及~ 1 终端设备 终端是一种字符型设备, ...
- linux 驱动之LCD驱动(有framebuffer)
<简介> LCD驱动里有个很重要的概念叫帧缓冲(framebuffer),它是Linux系统为显示设备提供的一个接口,应用程序在图形模式允许对显示缓冲区进行读写操作.用户根本不用关心物理显 ...
- [kernel]字符设备驱动、平台设备驱动、设备驱动模型、sysfs几者之间的比较和关联
转自:http://www.2cto.com/kf/201510/444943.html Linux驱动开发经验总结,绝对干货! 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动 ...
- 二十、网卡框架分析、虚拟网卡驱动和DM9621驱动分析
一.网络设备驱动的结构 网卡设备不同于字符设备和块设备, 网络设备并不对应于/dev目录下的文件,它存放在/sys/class/net目录下. Linux系统对网络设备驱动定义了四个层次: 1. 网络 ...
- (55)Linux驱动开发之一驱动概述
驱动 ...
随机推荐
- Jersey实现跨服务器上传图片:UniformInterfaceException:403 Forbidden
jersey.api.client.UniformInterfaceException :returned a response status of 403 Forbidden 图片服务器:端口808 ...
- 租房数据分析,knn算法使用
import numpy as np import pandas as pd import matplotlib.pyplot as plt data = pd.read_excel('jiemo.x ...
- centos7安装redis6
wget http://download.redis.io/releases/redis-6.0.6.tar.gz?_ga=2.104030464.1049731693.1595900008-1330 ...
- beego增删改查
package main import ( "fmt" "github.com/astaxie/beego/orm" _ "github.com/go ...
- css变量复用 全局变量-局部变量
前言 简单使用场景:同一套后台系统有多套主题的情况下,主题色作为一个最常用到的可复用的颜色,非常有必要像js的全局变量一样存在全局变量中以作复用,之前我第一个想到的是sass的变量声明,未曾想到css ...
- 圆形进度条的模仿1-DrawArc,DrawCircle,DrawText讲解
1:画弧 canvas.drawArc(oval,startAngle,sweepAngle,useCenter,paint) 第一个参数:绘制的区域,oval可以是被定好了的一个区域,也可以将ova ...
- Linux命令之date +%F
date命令显示当前日期 date +%F显示当前日期 [10:02:52 root@C8[ 2020-06-16DIR]#touch `hostname`_`date +%F`.log [10:03 ...
- Linux命令之命令别名
对于经常执行的较长的命令,可以将其定义成较短的别名,以方便执行 显示当前shell进程所有可用的命令别名 [04:33:43 root@C8[ ~]#alias alias cp='cp -i' al ...
- npm install各种命令模式
npm install 几种命令模式: npm install moduleName 安装模块到项目目录下 npm install -g moduleName npm install -g 将模块安装 ...
- deployer服务器端的配置
虽然说是无侵入的部署工具,但是还是需要我们来做一些微小的配置的,因为出于安全考虑,我们一般不会开发 root 用户的 SSH 登录,而是使用其它用户,比如 Ubuntu 默认的 ubuntu 用户.我 ...