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