net_device

include/linux/etherdevice.h

#define alloc_etherdev(sizeof_priv) alloc_etherdev_mq(sizeof_priv, 1)
#define alloc_etherdev_mq(sizeof_priv, count) alloc_etherdev_mqs(sizeof_priv, count, count)
net/ethernet/eth.c

struct net_device *alloc_etherdev_mqs(int sizeof_priv, unsigned int txqs,
				      unsigned int rxqs)
{
	return alloc_netdev_mqs(sizeof_priv, "eth%d", ether_setup, txqs, rxqs);
}
EXPORT_SYMBOL(alloc_etherdev_mqs);
net/core/dev.c

/**
 *	alloc_netdev_mqs - allocate network device
 *	@sizeof_priv:	size of private data to allocate space for
 *	@name:		device name format string
 *	@setup:		callback to initialize device
 *	@txqs:		the number of TX subqueues to allocate
 *	@rxqs:		the number of RX subqueues to allocate
 *
 *	Allocates a struct net_device with private data area for driver use
 *	and performs basic initialization.  Also allocates subqueue structs
 *	for each queue on the device.
 */
struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
		void (*setup)(struct net_device *),
		unsigned int txqs, unsigned int rxqs)
{
	struct net_device *dev;	// 网络设备
	size_t alloc_size;	// 实际分配的大小
	struct net_device *p;	// 指向分配的内存

	BUG_ON(strlen(name) >= sizeof(dev->name));

	/* 传输队列大小不能小于1
	 */
	if (txqs < 1) {
		pr_err("alloc_netdev: Unable to allocate device with zero queues\n");
		return NULL;
	}

/* 是否启用sysfs虚拟文件系统。sysfs以更整齐更直观的方式向用户展示了内核的各种参数
 * /proc将会向sysfs迁移
 */
#ifdef CONFIG_SYSFS
	/* 接收队列大小不能小于1
	 */
	if (rxqs < 1) {
		pr_err("alloc_netdev: Unable to allocate device with zero RX queues\n");
		return NULL;
	}
#endif

	/* 内存大小为net_device结构大小加上传递进来的值的大小
	 * 要32字节对齐,对齐的公式为  ( xx + 31  ) & ( ~31 )
	 */
	alloc_size = sizeof(struct net_device);
	if (sizeof_priv) {
		/* ensure 32-byte alignment of private area */
		alloc_size = ALIGN(alloc_size, NETDEV_ALIGN);
		alloc_size += sizeof_priv;
	}
	/* ensure 32-byte alignment of whole construct */
	alloc_size += NETDEV_ALIGN - 1;

	// 分配内存
	p = kzalloc(alloc_size, GFP_KERNEL | __GFP_NOWARN | __GFP_REPEAT);
	if (!p)
		p = vzalloc(alloc_size);
	if (!p)
		return NULL;

	dev = PTR_ALIGN(p, NETDEV_ALIGN);	// 网络设备的起始地址要32字节对齐
	dev->padded = (char *)dev - (char *)p;	// 保存网络设备的起始地址与所分配内存的起始地址之间的间距

	dev->pcpu_refcnt = alloc_percpu(int);	// 引用计数,是PRE_CPU变量
	if (!dev->pcpu_refcnt)
		goto free_dev;

	if (dev_addr_init(dev))	// 插入一个空的地址到dev->dev_addrs中,并将此空地址赋给dev->dev_addr 
		goto free_pcpu;

	dev_mc_init(dev);	// 初始化多播地址列表为空
	dev_uc_init(dev);	// 初始化单播地址列表为空

	dev_net_set(dev, &init_net);	// 当支持用户处定义网络空间时使用,略过

	dev->gso_max_size = GSO_MAX_SIZE;	// 65536
	dev->gso_max_segs = GSO_MAX_SEGS;	// 65535

	INIT_LIST_HEAD(&dev->napi_list);
	INIT_LIST_HEAD(&dev->unreg_list);
	INIT_LIST_HEAD(&dev->close_list);
	INIT_LIST_HEAD(&dev->link_watch_list);
	INIT_LIST_HEAD(&dev->adj_list.upper);
	INIT_LIST_HEAD(&dev->adj_list.lower);
	INIT_LIST_HEAD(&dev->all_adj_list.upper);
	INIT_LIST_HEAD(&dev->all_adj_list.lower);
	dev->priv_flags = IFF_XMIT_DST_RELEASE;	// 设置标记位:在进行dev_hard_start_xmit时可以释放skb->dst
	/* setup为参数,以太网调用ether_setup
	 * dev->priv_flags设置支持在传输过程中支持共享skb
	 */
	setup(dev);	

	dev->num_tx_queues = txqs;	// txqs = 1
	dev->real_num_tx_queues = txqs;	// txqs = 1
	if (netif_alloc_netdev_queues(dev))	// 分配1个netdev_queue对象,并把它赋值给dev->_tx
		goto free_all;

/* 是否启用sysfs虚拟文件系统。sysfs以更整齐更直观的方式向用户展示了内核的各种参数
 * /proc将会向sysfs迁移
 */
#ifdef CONFIG_SYSFS
	dev->num_rx_queues = rxqs;	// txqs = 1
	dev->real_num_rx_queues = rxqs;	// txqs = 1
	if (netif_alloc_rx_queues(dev))	// 分配1个netdev_rx_queue 对象,并把它赋值给dev->_rx
		goto free_all;
#endif

	strcpy(dev->name, name);	// 设置dev->name为传进来的参数name
	dev->group = INIT_NETDEV_GROUP;	// 设置dev->group为0
	if (!dev->ethtool_ops)
		dev->ethtool_ops = &default_ethtool_ops;
	return dev;

free_all:
	free_netdev(dev);
	return NULL;

free_pcpu:
	free_percpu(dev->pcpu_refcnt);
	netif_free_tx_queues(dev);
#ifdef CONFIG_SYSFS
	kfree(dev->_rx);
#endif

free_dev:
	netdev_freemem(dev);
	return NULL;
}
EXPORT_SYMBOL(alloc_netdev_mqs);

net/core/dev_addr_lists.c

/**
 *	dev_addr_init - Init device address list
 *	@dev: device
 *
 *	Init device address list and create the first element,
 *	used by ->dev_addr.
 *
 *	The caller must hold the rtnl_mutex.
 */
int dev_addr_init(struct net_device *dev)
{
	unsigned char addr[MAX_ADDR_LEN];	// 32字节数组
	struct netdev_hw_addr *ha;
	int err;

	/* rtnl_mutex must be held here */

	__hw_addr_init(&dev->dev_addrs);	// 列表初始化
	memset(addr, 0, sizeof(addr));		// 对32字节数组清0
	// 加入一个新的MAC地址,不是全局使用,不是同步使用,同步数为0,而且地址为空
	err = __hw_addr_add(&dev->dev_addrs, addr, sizeof(addr),
			    NETDEV_HW_ADDR_T_LAN);
	if (!err) {
		/*
		 * Get the first (previously created) address from the list
		 * and set dev_addr pointer to this location.
		 */
		ha = list_first_entry(&dev->dev_addrs.list,
				      struct netdev_hw_addr, list);
		dev->dev_addr = ha->addr;	// 此时这个地址为空
	}
	return err;
}
EXPORT_SYMBOL(dev_addr_init);

net/core/dev_addr_lists.c

// 加入一个MAC地址,不是全局使用,不是同步使用,同步数为0
static int __hw_addr_add(struct netdev_hw_addr_list *list,
			 const unsigned char *addr, int addr_len,
			 unsigned char addr_type)
{
	return __hw_addr_add_ex(list, addr, addr_len, addr_type, false, false,
				0);
}

net/core/dev_addr_lists.c

/* 将addr加入到list当中去
 * 当addr在list中己存在,只是增加引用计数,否则分配一个新的地址,并加入到list中去
 */
static int __hw_addr_add_ex(struct netdev_hw_addr_list *list,
			    const unsigned char *addr, int addr_len,
			    unsigned char addr_type, bool global, bool sync,
			    int sync_count)
{
	struct netdev_hw_addr *ha;

	/* 地址长度不能大于32,这是内核支持的最大MAC地址
	 * 以太网的地址长度为6字节
	 */
	if (addr_len > MAX_ADDR_LEN)
		return -EINVAL;
	
	// 对list中的所有地址进行比较,如果己存在,引用计数加1,返回,否则分配一个新的MAC地址
	list_for_each_entry(ha, &list->list, list) {	
		if (!memcmp(ha->addr, addr, addr_len) &&
		    ha->type == addr_type) {	// 如果地址类型相同并且地址与参数相同(地址己存在)
			if (global) {	
				/* check if addr is already used as global 
				 * 全局使用只能是一个,所以直接返加,不增加引用计数
				 */
				if (ha->global_use)
					return 0;
				else
					ha->global_use = true;
			}
			if (sync) {	
				if (ha->synced && sync_count)
					return -EEXIST;
				else
					ha->synced++;
			}
			ha->refcount++;	// 引用计数加1
			return 0;
		}
	}

	// 分配一个MAC地址,根据参数初始化。将其插入到list中
	return __hw_addr_create_ex(list, addr, addr_len, addr_type, global,
				   sync);
}

net/core/dev_addr_lists.c

// 分配一个MAC地址,根据参数初始化。将其插入到list中
static int __hw_addr_create_ex(struct netdev_hw_addr_list *list,
			       const unsigned char *addr, int addr_len,
			       unsigned char addr_type, bool global,
			       bool sync)
{
	struct netdev_hw_addr *ha;
	int alloc_size;

	/* 如果netdev_hw_addr的大小小于缓冲线的大小,内存大小设为缓冲线的大小,提高效率 
	 */
	alloc_size = sizeof(*ha);
	if (alloc_size < L1_CACHE_BYTES)
		alloc_size = L1_CACHE_BYTES;
	ha = kmalloc(alloc_size, GFP_ATOMIC);	// 分配内存
	if (!ha)
		return -ENOMEM;
	memcpy(ha->addr, addr, addr_len);	// 将参数地址拷贝到分配的内存中
	ha->type = addr_type;	// 设置地址类型
	ha->refcount = 1;	// 引用计数设为1
	ha->global_use = global;	// 是否全局使用
	ha->synced = sync ? 1 : 0;	// 是否同步
	ha->sync_cnt = 0;
	list_add_tail_rcu(&ha->list, &list->list);	// 将分配出的地址插入列表末尾(列表由参数传递进来)
	list->count++;	// 列表中的元素数量加1(列表由参数传递进来)

	return 0;
}

net/ethernet/eth.c

/**
 * ether_setup - setup Ethernet network device
 * @dev: network device
 *
 * Fill in the fields of the device structure with Ethernet-generic values.
 */
void ether_setup(struct net_device *dev)
{
	dev->header_ops		= ð_header_ops;
	dev->type		= ARPHRD_ETHER;	// 类型为10M以太网
	dev->hard_header_len 	= ETH_HLEN;	// 以太网头部长14字节
	dev->mtu		= ETH_DATA_LEN;	// 以太网MTU为1500字节
	dev->addr_len		= ETH_ALEN;	// 以太网地址长度为6字节
	dev->tx_queue_len	= 1000;		/* Ethernet wants good queues 发送队列长度1000*/
	dev->flags		= IFF_BROADCAST|IFF_MULTICAST;	// 支持广播和多播
	dev->priv_flags		|= IFF_TX_SKB_SHARING;	// 在传输过程中支持共享skb

	memset(dev->broadcast, 0xFF, ETH_ALEN);	// 用oxFF填充广播地址

}
EXPORT_SYMBOL(ether_setup);

net/core/dev.c

static int netif_alloc_netdev_queues(struct net_device *dev)
{
	/* 内存大小为发送队列个数*netdev_queue大小
	 */
	unsigned int count = dev->num_tx_queues;
	struct netdev_queue *tx;
	size_t sz = count * sizeof(*tx);

	BUG_ON(count < 1 || count > 0xffff);

	tx = kzalloc(sz, GFP_KERNEL | __GFP_NOWARN | __GFP_REPEAT);
	if (!tx) {
		tx = vzalloc(sz);
		if (!tx)
			return -ENOMEM;
	}
	dev->_tx = tx;	// 传输队列

	netdev_for_each_tx_queue(dev, netdev_init_one_queue, NULL);	// 对 dev->_tx 列表中的每一个对象调用netdev_init_one_queue进行初始化
	spin_lock_init(&dev->tx_global_lock);

	return 0;
}

net/core/dev.c

static void netdev_init_one_queue(struct net_device *dev,
				  struct netdev_queue *queue, void *_unused)
{
	/* Initialize queue lock */
	spin_lock_init(&queue->_xmit_lock);
	netdev_set_xmit_lockdep_class(&queue->_xmit_lock, dev->type);
	queue->xmit_lock_owner = -1;
	netdev_queue_numa_node_write(queue, NUMA_NO_NODE);
	queue->dev = dev;	// 设置队列的网络设备
#ifdef CONFIG_BQL
	dql_init(&queue->dql, HZ);
#endif
}

net/core/dev.c

/* 是否启用sysfs虚拟文件系统。sysfs以更整齐更直观的方式向用户展示了内核的各种参数
 * /proc将会向sysfs迁移
 */
#ifdef CONFIG_SYSFS
static int netif_alloc_rx_queues(struct net_device *dev)
{
	unsigned int i, count = dev->num_rx_queues;
	struct netdev_rx_queue *rx;

	BUG_ON(count < 1);

	/* 内存大小为发送队列个数*netdev_rx_queue大小
	 */
	rx = kcalloc(count, sizeof(struct netdev_rx_queue), GFP_KERNEL);
	if (!rx)
		return -ENOMEM;

	dev->_rx = rx;	// 接收队列
	
	// 设置队列的网络设备
	for (i = 0; i < count; i++)
		rx[i].dev = dev;
	return 0;
}
#endif


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值