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

Linux lookback驱动分析

2015-10-12 19:19 549 查看
Linux的网络驱动中,lookback 驱动算是最为简单的。本次分析的程序来自 Linux-2.6.32.68 源码,其中 lookback.c 驱动程序位于 /drives/net/ 目录下。

普通的网卡驱动都是以模块化注册到系统的,但是 lookback 驱动是和 kernel 一体的,Linux在启动的时候会调用 lookback 驱动。

1、设备注册

lookback网络驱动接口在一个全局的网络设备列表里插入一个数据结构.每个接口由一个结构 net_device 项来描述, 它在 <linux/netdevice.h> 里定义.

net_device 结构, 如同许多其他内核结构, 包含一个 kobject,以及因此它可被引用计数并通过 sysfs 输出. 如同别的这样的结构, 它必须动态分配. 进行这种分配的内核函数是 alloc_netdev, 它有下列原型:

struct net_device *alloc_netdev(
int sizeof_priv,
const char *name,
void (*setup)(struct net_device*));


sizeof_priv 是驱动的的"私有数据"区的大小;name 是这个接口的名字;setup是一个初始化函数的指针, 被调用来设置 net_device结构的剩余部分。

net_device 结构完成初始化之后,接着传递这个结构给 register_netdev 完成设备的注册。

/* Setup and register the loopback device. */
static __net_init int loopback_net_init(struct net *net)
{
struct net_device *dev;
int err;

err = -ENOMEM;
//初始化net_device 结构
dev = alloc_netdev(0, "lo", loopback_setup);
if (!dev)
goto out;
//设置网络命名空间
dev_net_set(dev, net);
//注册网络设备
err = register_netdev(dev);
if (err)
goto out_free_netdev;

net->loopback_dev = dev;
return 0;

out_free_netdev:
free_netdev(dev);
out:
if (net == &init_net)
panic("loopback: Failed to register netdevice: %d\n", err);
return err;
}
Lookback驱动中初始化net_device 结构语句:
dev = alloc_netdev(0, "lo", loopback_setup)
初始化函数为loopback_setup

static void loopback_setup(struct net_device *dev)
{
//最大传输单元
dev->mtu		= (16 * 1024) + 20 + 20 + 12;
//硬件头部长度,被发送报文在 IP 头之前的字节数。对于以太网接口值为14
dev->hard_header_len	= ETH_HLEN;	/* 14	*/
//硬件 (MAC) 地址长度。以太网地址长度是 6 个字节
dev->addr_len		= ETH_ALEN;	/* 6	*/
//设备发送队列中可以排队的最大帧数
dev->tx_queue_len	= 0;
//接口的硬件类型,类型定义于 <linux/if_arp.h>
dev->type		= ARPHRD_LOOPBACK;	/* 0x0001*/
//接口标志。IFF_LOOPBACK这个标志只在环回接口中设置. 内核检查 IFF_LOOPBACK , 以代替硬连线 lo
dev->flags		= IFF_LOOPBACK;
dev->priv_flags	       &= ~IFF_XMIT_DST_RELEASE;
dev->features 		= NETIF_F_SG | NETIF_F_FRAGLIST
| NETIF_F_TSO
| NETIF_F_NO_CSUM
| NETIF_F_HIGHDMA
| NETIF_F_LLTX
| NETIF_F_NETNS_LOCAL;
// 声明对 ethtool 支持
dev->ethtool_ops	= &loopback_ethtool_ops;
dev->header_ops		= ð_header_ops;
dev->netdev_ops		= &loopback_ops;
dev->destructor		= loopback_dev_free;
}


ethtool 是一个实用工具, 提供大量控制网络接口的操作。对 ethtool 支持的相关声明可在 <linux/ethtool.h> 中找到. 它的核心是一个 ethtool_ops类型的结构, 里面包含一个全部 24 个不同方法来支持 ethtool.

static const struct ethtool_ops loopback_ethtool_ops = {
.get_link		= always_on,
.set_tso		= ethtool_op_set_tso,
.get_tx_csum		= always_on,
.get_sg			= always_on,
.get_rx_csum		= always_on,
};


执行destructor 的时候, loopback_dev_free释放了 net_device 结构,一旦已调用了free_netdev, 将再不能对个设备或者私有数据做任何引用。

static void loopback_dev_free(struct net_device *dev)
{
struct pcpu_lstats *lstats = dev->ml_priv;

free_percpu(lstats);
free_netdev(dev);


网络驱动中,还声明了一些能够操作它的函数:

static const struct net_device_ops loopback_ops = {
.ndo_init      = loopback_dev_init,
.ndo_start_xmit= loopback_xmit,
.ndo_get_stats = loopback_get_stats,
};


其中loopback_dev_init 用于设备初始化

static int loopback_dev_init(struct net_device *dev)
{
struct pcpu_lstats *lstats;

lstats = alloc_percpu(struct pcpu_lstats);
if (!lstats)
return -ENOMEM;

dev->ml_priv = lstats;
return 0;
}


loopback_xmit用于发送报文

static netdev_tx_t loopback_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct pcpu_lstats *pcpu_lstats, *lb_stats;
int len;

skb_orphan(skb);
//帮助函数( eth_type_trans ), 用于发现一个合适值来赋给 protocol
skb->protocol = eth_type_trans(skb, dev);

/* it's OK to use per_cpu_ptr() because BHs are off */
pcpu_lstats = dev->ml_priv;
lb_stats = per_cpu_ptr(pcpu_lstats, smp_processor_id());

len = skb->len;
if (likely(netif_rx(skb) == NET_RX_SUCCESS)) {
lb_stats->bytes += len;
lb_stats->packets++;
} else
lb_stats->drops++;

return NETDEV_TX_OK;
}


loopback_get_stats用于获取接口的统计信息

static struct net_device_stats *loopback_get_stats(struct net_device *dev)
{
const struct pcpu_lstats *pcpu_lstats;
struct net_device_stats *stats = &dev->stats;
unsigned long bytes = 0;
unsigned long packets = 0;
unsigned long drops = 0;
int i;

pcpu_lstats = dev->ml_priv;
for_each_possible_cpu(i) {
const struct pcpu_lstats *lb_stats;

lb_stats = per_cpu_ptr(pcpu_lstats, i);
bytes   += lb_stats->bytes;
packets += lb_stats->packets;
drops   += lb_stats->drops;
}
stats->rx_packets = packets;
stats->tx_packets = packets;
stats->rx_dropped = drops;
stats->rx_errors  = drops;
stats->rx_bytes   = bytes;
stats->tx_bytes   = bytes;
return stats;
}


2、设备注销

调用了unregister_netdev函数,注销一个网络设备。

static __net_exit void loopback_net_exit(struct net *net)
{
struct net_device *dev = net->loopback_dev;

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