只有偏执狂才能生存
分类: LINUX
2013-07-31 09:19:36
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
static int vp_try_to_find_vqs(struct virtio_device *vdev, unsigned nvqs,
struct virtqueue *vqs[],
vq_callback_t *callbacks[],
const char *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;
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_request;
} 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为0,则nvectors就是2,再用掉另一个vector来指定n个队列共用的vq中断处理函数vp_vring_interrupt,
如果per_vq_vectors为1,则在下面代码中为每个队列指定一个vector,vq中断处理函数为vring_interrupt */
err = vp_request_msix_vectors(vdev, nvectors, per_vq_vectors);
if (err)
goto error_request;
}
/* 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] = 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不为1,或者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为1,则为每个队列指定一个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);
error_request:
return err;
}
vp_request_msix_vectors函数完成任务:
1.分配nvectors个msix中断用vector,并使用1个vector来指定vp_config_changed为change中断处理函数。
2.如果per_vq_vectors为0,则nvectors就是2,再用掉另一个vector来指定n个队列共用的vq中断处理函数vp_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_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;
/* pci_enable_msix returns positive if we can't get this many. */
err = pci_enable_msix(vp_dev->pci_dev, vp_dev->msix_entries, nvectors);
if (err > 0)
err = -ENOSPC;
if (err)
goto error;
vp_dev->msix_vectors = nvectors;
vp_dev->msix_enabled = 1;
/* Set the vector used for configuration */
/* 使用一个vector,从vp_dev->msix_used_vectors读取下一个可用的vector,然后将这个vector同vp_config_changed绑定 */
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; /* 上面用了一个vector,自增1 */
iowrite16(v, vp_dev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR);
/* Verify we had enough resources to assign the vector */
v = ioread16(vp_dev->ioaddr + VIRTIO_MSI_CONFIG_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;
}