【Linux5.4】【TUN】代码学习记录(4)–tun_notifier_block
register_netdevice_notifier(&tun_notifier_block)将tun_notifier_block添加在netdev_chain链表内。
tun_notifier_block包含什么?
static struct notifier_block tun_notifier_block __read_mostly = {
.notifier_call = tun_device_event,
};
notifier_block 结构体定义在<include/linux/notifier.h>
typedef int (*notifier_fn_t)(struct notifier_block *nb,
unsigned long action, void *data);
struct notifier_block {
notifier_fn_t notifier_call;
struct notifier_block __rcu *next;
int priority;
};
tun_notifier_block中只挂载了一个函数tun_device_event
tun_device_event
static int tun_device_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct tun_struct *tun = netdev_priv(dev);
int i;
if (dev->rtnl_link_ops != &tun_link_ops)
return NOTIFY_DONE;
switch (event) {
case NETDEV_CHANGE_TX_QUEUE_LEN:
if (tun_queue_resize(tun))
return NOTIFY_BAD;
break;
case NETDEV_UP://call_netdevice_notifier(nb, NETDEV_UP, dev)时匹配
for (i = 0; i < tun->numqueues; i++) {
struct tun_file *tfile;
tfile = rtnl_dereference(tun->tfiles[i]);
/* sk_write_space调用的函数实际是tun_sock_write_space
* 这在tun_chr_open中进行定义
*/
tfile->socket.sk->sk_write_space(tfile->socket.sk);
}
break;
default:
break;
}
return NOTIFY_DONE;//#define NOTIFY_DONE 0x0000 /* Don't care */
}
tun_queue_resize,对tun中每个tfile->tx_ring重置空间大小
static int tun_queue_resize(struct tun_struct *tun)
{
struct net_device *dev = tun->dev;
struct tun_file *tfile;
struct ptr_ring **rings;
int n = tun->numqueues + tun->numdisabled;
int ret, i;
/* rings指针,申请一组内存空间,包含n个rings类型指针 */
rings = kmalloc_array(n, sizeof(*rings), GFP_KERNEL);
if (!rings)
return -ENOMEM;
/* 将rings前tun->numqueues个一一对应绑定tun中每一个tfile的tx_ring */
for (i = 0; i < tun->numqueues; i++) {
tfile = rtnl_dereference(tun->tfiles[i]);
rings[i] = &tfile->tx_ring;
}
/* 将rings后tun->numdisabled个一一对应绑定tun->disabled中每一个tfile的tx_ring */
list_for_each_entry(tfile, &tun->disabled, next)
rings[i++] = &tfile->tx_ring;
/* 重置rings大小 */
ret = ptr_ring_resize_multiple(rings, n,
dev->tx_queue_len, GFP_KERNEL,
tun_ptr_free);
kfree(rings);
return ret;
}
ptr_ring_resize_multiple 重置rings空间大小。其中最后一个参数实际destroy是tun_ptr_free
/*
* Note: producer lock is nested within consumer lock, so if you
* resize you must make sure all uses nest correctly.
* In particular if you consume ring in interrupt or BH context, you must
* disable interrupts/BH when doing so.
*/
static inline int ptr_ring_resize_multiple(struct ptr_ring **rings,
unsigned int nrings,
int size,
gfp_t gfp, void (*destroy)(void *))
{
unsigned long flags;
void ***queues;//???这个是什么定义
int i;
queues = kmalloc_array(nrings, sizeof(*queues), gfp);
if (!queues)
goto noqueues;
for (i = 0; i < nrings; ++i) {
queues[i] = __ptr_ring_init_queue_alloc(size, gfp);
if (!queues[i])
goto nomem;
}
for (i = 0; i < nrings; ++i) {
spin_lock_irqsave(&(rings[i])->consumer_lock, flags);
spin_lock(&(rings[i])->producer_lock);
//交换rings和queues队列内指针?
queues[i] = __ptr_ring_swap_queue(rings[i], queues[i],
size, gfp, destroy);
spin_unlock(&(rings[i])->producer_lock);
spin_unlock_irqrestore(&(rings[i])->consumer_lock, flags);
}
//销毁queues
for (i = 0; i < nrings; ++i)
kvfree(queues[i]);
kfree(queues);
return 0;
nomem:
while (--i >= 0)
kvfree(queues[i]);
kfree(queues);
noqueues:
return -ENOMEM;
}
tun_ptr_free 释放ptr所指内存
void tun_ptr_free(void *ptr)
{
if (!ptr)
return;
if (tun_is_xdp_frame(ptr)) {
struct xdp_frame *xdpf = tun_ptr_to_xdp(ptr);
xdp_return_frame(xdpf);
} else {
__skb_array_destroy_skb(ptr);
}
}
EXPORT_SYMBOL_GPL(tun_ptr_free);