virtio设备中断分析

最近碰到cenos7.4虚拟机多队列收包时中断都跑到到一个cpu上的问题。
ls -l /sys/class/net 查看对应virtio号, 然后cat /proc/interrupts,发现只有一个virtio3-virtqueues。
所以学习virtio设备中断分析下:

1.两种中断任务:

1).当设备的配置信息发生改变(config changed),会产生一个中断(称为change中断),中断处理程序需要调用相应的处理函数(需要驱动定义)。

2).当设备向队列中写入信息时,会产生一个中断(称为vq中断),中断处理函数需要调用相应的队列的回调函数(需要驱动定义)。

2.三种中断处理方式:

1).不用msix中断,则change中断和所有vq中断共用一个中断irq。
中断处理函数:vp_interrupt。
( vp_interrupt函数中包含了对change中断和vq中断的处理)

2).使用msix中断,但只有2个vector,一个用来对应change中断,一个对应所有队列的vq中断。
change中断处理函数:vp_config_changed
vq中断处理函数:vp_vring_interrupt

在虚拟机内部查看如下:
在这里插入图片描述

3).使用msix中断,有n+1个vector,一个用来对应change中断,n个分别对应n个队列的vq中断。每个vq一个vector。
change中断处理函数:vp_config_changed
vq中断处理函数:vring_interrupt

在虚拟机内部查看如下:
在这里插入图片描述
ps:第一列为中断号,如果要绑定中断所在的cpu,使用如下命令(0为cpu,29为中断号):

echo 0 > /proc/irq/29/smp_affinity_list

3.内核初始化逻辑

源码下载参考:https://blog.csdn.net/qq_15437629/article/details/116225626?spm=1001.2014.3001.5501

/* the config->find_vqs() implementation */
int vp_find_vqs(struct virtio_device *vdev, unsigned nvqs,
		struct virtqueue *vqs[],
		vq_callback_t *callbacks[],
		const char * const names[])
{
	int err;

	/* Try MSI-X with one vector per queue. */
	err = vp_try_to_find_vqs(vdev, nvqs, vqs, callbacks, names, true, true);
	if (!err)
		return 0;
	/* Fallback: MSI-X with one vector for config, one shared for queues. */
	err = vp_try_to_find_vqs(vdev, nvqs, vqs, callbacks, names,
				 true, false);
	if (!err)
		return 0;
	/* Finally fall back to regular interrupts. */
	return vp_try_to_find_vqs(vdev, nvqs, vqs, callbacks, names,
				  false, false);
}

优先使用方式3,然后方式2,最后方式1. (最后两个参数分别为 use_msix 和 per_vq_vectors)

static int vp_try_to_find_vqs(struct virtio_device *vdev, unsigned nvqs,
			      struct virtqueue *vqs[],
			      vq_callback_t *callbacks[],
			      const char * const names[],
			      bool use_msix,
			      bool per_vq_vectors)
{
	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
	u16 msix_vec;
	int i, err, nvectors, allocated_vectors;

	vp_dev->vqs = kmalloc(nvqs * sizeof *vp_dev->vqs, GFP_KERNEL);
	if (!vp_dev->vqs)
		return -ENOMEM;

	if (!use_msix) { /* 不用msix,所有vq共用一个irq */
		/* Old style: one normal interrupt for change and all vqs. */
		err = vp_request_intx(vdev);
		if (err)
			goto error_find;
	} else {
		if (per_vq_vectors) { /* 每个vq一个vector */
			/* Best option: one for change interrupt, one per vq. */
			nvectors = 1;
			for (i = 0; i < nvqs; ++i)
				if (callbacks[i])
					++nvectors;
		} else {
			/* Second best: one for change, shared for all vqs. */
			nvectors = 2;
		}
  
		/* vp_request_msix_vectors函数完成任务:
		1.分配nvectors个msix中断用vector,并使用1个vector来指定vp_config_changed为change中断处理函数。
		2.如果per_vq_vectors为false,则nvectors就是2,再用掉另一个vector来指定n个队列共用的vq中断处理函数vp_vring_interrupt,
		3.如果per_vq_vectors为true,则在下面代码中为每个队列指定一个vector,vq中断处理函数为vring_interrupt 
		*/
		err = vp_request_msix_vectors(vdev, nvectors, per_vq_vectors);
		if (err)
			goto error_find;
	}

 	/* per_vq_vectors,标识是不是每一个队列分配一个vector */
	vp_dev->per_vq_vectors = per_vq_vectors;
	allocated_vectors = vp_dev->msix_used_vectors;
	for (i = 0; i < nvqs; ++i) {
		if (!names[i]) {
			vqs[i] = NULL;
			continue;
		} else if (!callbacks[i] || !vp_dev->msix_enabled)
			msix_vec = VIRTIO_MSI_NO_VECTOR;
		else if (vp_dev->per_vq_vectors)
			msix_vec = allocated_vectors++;
		else
			msix_vec = VP_MSIX_VQ_VECTOR;
		vqs[i] = vp_setup_vq(vdev, i, callbacks[i], names[i], msix_vec);
		if (IS_ERR(vqs[i])) {
			err = PTR_ERR(vqs[i]);
			goto error_find;
		}
 		 /* 如果vp_dev->per_vq_vectors不为true,或者VIRTIO_MSI_NO_VECTOR,则不执行下文 */
		if (!vp_dev->per_vq_vectors || msix_vec == VIRTIO_MSI_NO_VECTOR)
			continue;

		/* allocate per-vq irq if available and necessary */
		snprintf(vp_dev->msix_names[msix_vec],
			 sizeof *vp_dev->msix_names,
			 "%s-%s",
			 dev_name(&vp_dev->vdev.dev), names[i]);
  /*  如果per_vq_vectors为true,则为每个队列指定一个vector,vq中断处理函数为vring_interrupt */
		err = request_irq(vp_dev->msix_entries[msix_vec].vector,
				  vring_interrupt, 0,
				  vp_dev->msix_names[msix_vec],
				  vqs[i]);
		if (err) {
			vp_del_vq(vqs[i]);
			goto error_find;
		}
	}
	return 0;

error_find:
	vp_del_vqs(vdev);
	return err;
}

vp_request_msix_vectors函数完成任务:
1.分配nvectors个msix中断用vector,并使用1个vector来指定vp_config_changed为change中断处理函数。
2.如果per_vq_vectors为false,则nvectors就是2,再用掉另一个vector来指定n个队列共用的vq中断处理函数vp_vring_interrupt,
3.如果per_vq_vectors为true,则在下面代码中为每个队列指定一个vector,vq中断处理函数为vring_interrupt

static int vp_request_msix_vectors(struct virtio_device *vdev, int nvectors,
				   bool per_vq_vectors)
{
	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
	const char *name = dev_name(&vp_dev->vdev.dev);
	unsigned i, v;
	int err = -ENOMEM;

	vp_dev->msix_vectors = nvectors;

	vp_dev->msix_entries = kmalloc(nvectors * sizeof *vp_dev->msix_entries,
				       GFP_KERNEL);
	if (!vp_dev->msix_entries)
		goto error;
	vp_dev->msix_names = kmalloc(nvectors * sizeof *vp_dev->msix_names,
				     GFP_KERNEL);
	if (!vp_dev->msix_names)
		goto error;
	vp_dev->msix_affinity_masks
		= kzalloc(nvectors * sizeof *vp_dev->msix_affinity_masks,
			  GFP_KERNEL);
	if (!vp_dev->msix_affinity_masks)
		goto error;
	for (i = 0; i < nvectors; ++i)
		if (!alloc_cpumask_var(&vp_dev->msix_affinity_masks[i],
					GFP_KERNEL))
			goto error;

	for (i = 0; i < nvectors; ++i)
		vp_dev->msix_entries[i].entry = i;

	err = pci_enable_msix_exact(vp_dev->pci_dev,
				    vp_dev->msix_entries, nvectors);
	if (err)
		goto error;
	vp_dev->msix_enabled = 1;

 	/* 使用一个vector,从vp_dev->msix_used_vectors读取下一个可用的vector,然后将这个vector同vp_config_changed绑定 */
	/* Set the vector used for configuration */
	v = vp_dev->msix_used_vectors;
	snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names,
		 "%s-config", name);
	err = request_irq(vp_dev->msix_entries[v].vector,
			  vp_config_changed, 0, vp_dev->msix_names[v],
			  vp_dev);
	if (err)
		goto error;
	++vp_dev->msix_used_vectors;

	v = vp_dev->config_vector(vp_dev, v);
	/* Verify we had enough resources to assign the vector */
	if (v == VIRTIO_MSI_NO_VECTOR) {
		err = -EBUSY;
		goto error;
	}

 	/* 如果per_vq_vectors为0,则为所有队列分配一个vector,指定所有队列的vq中断的处理函数为vp_vring_interrupt */
	if (!per_vq_vectors) {
		/* Shared vector for all VQs */
		v = vp_dev->msix_used_vectors;
		snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names,
			 "%s-virtqueues", name);
		err = request_irq(vp_dev->msix_entries[v].vector,
				  vp_vring_interrupt, 0, vp_dev->msix_names[v],
				  vp_dev);
		if (err)
			goto error;
		++vp_dev->msix_used_vectors;
	}
	return 0;
error:
	vp_free_vectors(vdev);
	return err;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

造夢先森

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值