qemu中得到guest前端的通知处理后再发中断给前端
来源:互联网 发布:冬季太极服女淘宝网 编辑:程序博客网 时间:2024/05/22 06:55
在qemu/hw/virtio/virtio-pci.c 中定义定义了对配置空间的操作
static const MemoryRegionOps virtio_pci_config_ops = {
.read = virtio_pci_config_read,
.write = virtio_pci_config_write,
.impl = {
.min_access_size = 1,
.max_access_size = 4,
},
.endianness = DEVICE_LITTLE_ENDIAN,
};
在guest 前端和qemu后端就是通过写配置空间来通信的
static void virtio_pci_config_write(void *opaque, hwaddr addr,
uint64_t val, unsigned size)
{
VirtIOPCIProxy *proxy = opaque;
uint32_t config = VIRTIO_PCI_CONFIG_SIZE(&proxy->pci_dev);
VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
if (addr < config) {
virtio_ioport_write(proxy, addr, val);
return;
}
}
在virtio_pci_config_write 中如果写的地址小于配置空间的话,就调用virtio_ioport_write
static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
{
VirtIOPCIProxy *proxy = opaque;
VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
hwaddr pa;
switch (addr) {
case VIRTIO_PCI_QUEUE_PFN:
pa = (hwaddr)val << VIRTIO_PCI_QUEUE_ADDR_SHIFT;
if (pa == 0) {
virtio_pci_reset(DEVICE(proxy));
}
else
virtio_queue_set_addr(vdev, vdev->queue_sel, pa);
break;
case VIRTIO_PCI_QUEUE_SEL:
if (val < VIRTIO_QUEUE_MAX)
vdev->queue_sel = val;
break;
case VIRTIO_PCI_QUEUE_NOTIFY:
if (val < VIRTIO_QUEUE_MAX) {
virtio_queue_notify(vdev, val);
}
break;
}
在virtio_ioport_write 中我们只关注VIRTIO_PCI_QUEUE_NOTIFY,代表qemu 收到guest前端写vq的通知
void virtio_queue_notify(VirtIODevice *vdev, int n)
{
virtio_queue_notify_vq(&vdev->vq[n]);
}
static void virtio_queue_notify_vq(VirtQueue *vq)
{
if (vq->vring.desc && vq->handle_output) {
VirtIODevice *vdev = vq->vdev;
if (unlikely(vdev->broken)) {
return;
}
trace_virtio_queue_notify(vdev, vq - vdev->vq, vq);
vq->handle_output(vdev, vq);
}
}
针对virtio_blk 其注册的handle_out 函数为virtio_blk_handle_output
static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
{
VirtIOBlock *s = (VirtIOBlock *)vdev;
if (s->dataplane) {
/* Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start
* dataplane here instead of waiting for .set_status().
*/
virtio_device_start_ioeventfd(vdev);
if (!s->dataplane_disabled) {
return;
}
}
virtio_blk_handle_vq(s, vq);
}
在virtio_blk_handle_output 如果没有优化的话,s->dataplane 会等与NULL,因此直接调用virtio_blk_handle_vq
void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq)
{
VirtIOBlockReq *req;
MultiReqBuffer mrb = {};
blk_io_plug(s->blk);
while ((req = virtio_blk_get_request(s, vq))) {
if (virtio_blk_handle_request(req, &mrb)) {
virtqueue_detach_element(req->vq, &req->elem, 0);
virtio_blk_free_request(req);
break;
}
}
if (mrb.num_reqs) {
virtio_blk_submit_multireq(s->blk, &mrb);
}
blk_io_unplug(s->blk);
}
在virtio_blk_handle_vq 中通过virtio_blk_get_request 得到VirtIOBlockReq,然后在virtio_blk_handle_request 中处理
static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s, VirtQueue *vq)
{
VirtIOBlockReq *req = virtqueue_pop(vq, sizeof(VirtIOBlockReq));
if (req) {
virtio_blk_init_request(s, vq, req);
}
return req;
}
virtio_blk_get_request的实现很简单就调用virtqueue_pop 从VirtQueue 中弹出一个VirtIOBlockReq
static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
{
uint32_t type;
struct iovec *in_iov = req->elem.in_sg;
struct iovec *iov = req->elem.out_sg;
unsigned in_num = req->elem.in_num;
unsigned out_num = req->elem.out_num;
VirtIOBlock *s = req->dev;
VirtIODevice *vdev = VIRTIO_DEVICE(s);
switch (type & ~(VIRTIO_BLK_T_OUT | VIRTIO_BLK_T_BARRIER)) {
case VIRTIO_BLK_T_IN:
{
bool is_write = type & VIRTIO_BLK_T_OUT;
req->sector_num = virtio_ldq_p(VIRTIO_DEVICE(req->dev),
&req->out.sector);
if (is_write) {
qemu_iovec_init_external(&req->qiov, iov, out_num);
trace_virtio_blk_handle_write(req, req->sector_num,
req->qiov.size / BDRV_SECTOR_SIZE);
} else {
qemu_iovec_init_external(&req->qiov, in_iov, in_num);
trace_virtio_blk_handle_read(req, req->sector_num,
req->qiov.size / BDRV_SECTOR_SIZE);
}
if (!virtio_blk_sect_range_ok(req->dev, req->sector_num,
req->qiov.size)) {
virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
block_acct_invalid(blk_get_stats(req->dev->blk),
is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ);
virtio_blk_free_request(req);
return 0;
}
}
在virtio_blk_handle_request 中出来玩vq中的消息如果要通过guest前端的话则调用virtio_blk_req_complete
static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status)
{
VirtIOBlock *s = req->dev;
VirtIODevice *vdev = VIRTIO_DEVICE(s);
trace_virtio_blk_req_complete(req, status);
stb_p(&req->in->status, status);
virtqueue_push(req->vq, &req->elem, req->in_len);
if (s->dataplane_started && !s->dataplane_disabled) {
virtio_blk_data_plane_notify(s->dataplane, req->vq);
} else {
virtio_notify(vdev, req->vq);
}
}
继续调用virtio_notify
void virtio_notify(VirtIODevice *vdev, VirtQueue *vq)
{
if (!virtio_should_notify(vdev, vq)) {
return;
}
trace_virtio_notify(vdev, vq);
virtio_set_isr(vq->vdev, 0x1);
virtio_notify_vector(vdev, vq->vector);
}
static void virtio_notify_vector(VirtIODevice *vdev, uint16_t vector)
{
BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
if (unlikely(vdev->broken)) {
return;
}
if (k->notify) {
k->notify(qbus->parent, vector);
}
}
最终的notify赋值如下:
在qemu/hw/virtio/virtio-pci.c中的virtio_pci_bus_class_init函数中有赋值。
static void virtio_pci_bus_class_init(ObjectClass *klass, void *data)
{
BusClass *bus_class = BUS_CLASS(klass);
VirtioBusClass *k = VIRTIO_BUS_CLASS(klass);
bus_class->max_dev = 1;
k->notify = virtio_pci_notify;
}
static void virtio_pci_notify(DeviceState *d, uint16_t vector)
{
VirtIOPCIProxy *proxy = to_virtio_pci_proxy_fast(d);
if (msix_enabled(&proxy->pci_dev))
msix_notify(&proxy->pci_dev, vector);
else {
VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
pci_set_irq(&proxy->pci_dev, atomic_read(&vdev->isr) & 1);
}
}
可见是通过msix中断来通过guest 前端的
static const MemoryRegionOps virtio_pci_config_ops = {
.read = virtio_pci_config_read,
.write = virtio_pci_config_write,
.impl = {
.min_access_size = 1,
.max_access_size = 4,
},
.endianness = DEVICE_LITTLE_ENDIAN,
};
在guest 前端和qemu后端就是通过写配置空间来通信的
static void virtio_pci_config_write(void *opaque, hwaddr addr,
uint64_t val, unsigned size)
{
VirtIOPCIProxy *proxy = opaque;
uint32_t config = VIRTIO_PCI_CONFIG_SIZE(&proxy->pci_dev);
VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
if (addr < config) {
virtio_ioport_write(proxy, addr, val);
return;
}
}
在virtio_pci_config_write 中如果写的地址小于配置空间的话,就调用virtio_ioport_write
static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
{
VirtIOPCIProxy *proxy = opaque;
VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
hwaddr pa;
switch (addr) {
case VIRTIO_PCI_QUEUE_PFN:
pa = (hwaddr)val << VIRTIO_PCI_QUEUE_ADDR_SHIFT;
if (pa == 0) {
virtio_pci_reset(DEVICE(proxy));
}
else
virtio_queue_set_addr(vdev, vdev->queue_sel, pa);
break;
case VIRTIO_PCI_QUEUE_SEL:
if (val < VIRTIO_QUEUE_MAX)
vdev->queue_sel = val;
break;
case VIRTIO_PCI_QUEUE_NOTIFY:
if (val < VIRTIO_QUEUE_MAX) {
virtio_queue_notify(vdev, val);
}
break;
}
在virtio_ioport_write 中我们只关注VIRTIO_PCI_QUEUE_NOTIFY,代表qemu 收到guest前端写vq的通知
void virtio_queue_notify(VirtIODevice *vdev, int n)
{
virtio_queue_notify_vq(&vdev->vq[n]);
}
static void virtio_queue_notify_vq(VirtQueue *vq)
{
if (vq->vring.desc && vq->handle_output) {
VirtIODevice *vdev = vq->vdev;
if (unlikely(vdev->broken)) {
return;
}
trace_virtio_queue_notify(vdev, vq - vdev->vq, vq);
vq->handle_output(vdev, vq);
}
}
针对virtio_blk 其注册的handle_out 函数为virtio_blk_handle_output
static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
{
VirtIOBlock *s = (VirtIOBlock *)vdev;
if (s->dataplane) {
/* Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start
* dataplane here instead of waiting for .set_status().
*/
virtio_device_start_ioeventfd(vdev);
if (!s->dataplane_disabled) {
return;
}
}
virtio_blk_handle_vq(s, vq);
}
在virtio_blk_handle_output 如果没有优化的话,s->dataplane 会等与NULL,因此直接调用virtio_blk_handle_vq
void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq)
{
VirtIOBlockReq *req;
MultiReqBuffer mrb = {};
blk_io_plug(s->blk);
while ((req = virtio_blk_get_request(s, vq))) {
if (virtio_blk_handle_request(req, &mrb)) {
virtqueue_detach_element(req->vq, &req->elem, 0);
virtio_blk_free_request(req);
break;
}
}
if (mrb.num_reqs) {
virtio_blk_submit_multireq(s->blk, &mrb);
}
blk_io_unplug(s->blk);
}
在virtio_blk_handle_vq 中通过virtio_blk_get_request 得到VirtIOBlockReq,然后在virtio_blk_handle_request 中处理
static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s, VirtQueue *vq)
{
VirtIOBlockReq *req = virtqueue_pop(vq, sizeof(VirtIOBlockReq));
if (req) {
virtio_blk_init_request(s, vq, req);
}
return req;
}
virtio_blk_get_request的实现很简单就调用virtqueue_pop 从VirtQueue 中弹出一个VirtIOBlockReq
static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
{
uint32_t type;
struct iovec *in_iov = req->elem.in_sg;
struct iovec *iov = req->elem.out_sg;
unsigned in_num = req->elem.in_num;
unsigned out_num = req->elem.out_num;
VirtIOBlock *s = req->dev;
VirtIODevice *vdev = VIRTIO_DEVICE(s);
switch (type & ~(VIRTIO_BLK_T_OUT | VIRTIO_BLK_T_BARRIER)) {
case VIRTIO_BLK_T_IN:
{
bool is_write = type & VIRTIO_BLK_T_OUT;
req->sector_num = virtio_ldq_p(VIRTIO_DEVICE(req->dev),
&req->out.sector);
if (is_write) {
qemu_iovec_init_external(&req->qiov, iov, out_num);
trace_virtio_blk_handle_write(req, req->sector_num,
req->qiov.size / BDRV_SECTOR_SIZE);
} else {
qemu_iovec_init_external(&req->qiov, in_iov, in_num);
trace_virtio_blk_handle_read(req, req->sector_num,
req->qiov.size / BDRV_SECTOR_SIZE);
}
if (!virtio_blk_sect_range_ok(req->dev, req->sector_num,
req->qiov.size)) {
virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
block_acct_invalid(blk_get_stats(req->dev->blk),
is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ);
virtio_blk_free_request(req);
return 0;
}
}
在virtio_blk_handle_request 中出来玩vq中的消息如果要通过guest前端的话则调用virtio_blk_req_complete
static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status)
{
VirtIOBlock *s = req->dev;
VirtIODevice *vdev = VIRTIO_DEVICE(s);
trace_virtio_blk_req_complete(req, status);
stb_p(&req->in->status, status);
virtqueue_push(req->vq, &req->elem, req->in_len);
if (s->dataplane_started && !s->dataplane_disabled) {
virtio_blk_data_plane_notify(s->dataplane, req->vq);
} else {
virtio_notify(vdev, req->vq);
}
}
继续调用virtio_notify
void virtio_notify(VirtIODevice *vdev, VirtQueue *vq)
{
if (!virtio_should_notify(vdev, vq)) {
return;
}
trace_virtio_notify(vdev, vq);
virtio_set_isr(vq->vdev, 0x1);
virtio_notify_vector(vdev, vq->vector);
}
static void virtio_notify_vector(VirtIODevice *vdev, uint16_t vector)
{
BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
if (unlikely(vdev->broken)) {
return;
}
if (k->notify) {
k->notify(qbus->parent, vector);
}
}
最终的notify赋值如下:
在qemu/hw/virtio/virtio-pci.c中的virtio_pci_bus_class_init函数中有赋值。
static void virtio_pci_bus_class_init(ObjectClass *klass, void *data)
{
BusClass *bus_class = BUS_CLASS(klass);
VirtioBusClass *k = VIRTIO_BUS_CLASS(klass);
bus_class->max_dev = 1;
k->notify = virtio_pci_notify;
}
static void virtio_pci_notify(DeviceState *d, uint16_t vector)
{
VirtIOPCIProxy *proxy = to_virtio_pci_proxy_fast(d);
if (msix_enabled(&proxy->pci_dev))
msix_notify(&proxy->pci_dev, vector);
else {
VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
pci_set_irq(&proxy->pci_dev, atomic_read(&vdev->isr) & 1);
}
}
可见是通过msix中断来通过guest 前端的
0 0
- qemu中得到guest前端的通知处理后再发中断给前端
- 给不了解前端的同学讲前端
- CGI中对前端数据的处理
- java 直接返回Date类型数据给前端,前端的处理方法
- json字符串返回给前端处理
- 给前端开发人员的建议
- 前端面试中常见的算法问题读后整理
- 前端面试中常见的算法问题读后整理
- 前端得到Select信息
- qemu-guest-agent的一些用途
- 得到.crash 后的处理
- 语音识别的前端处理
- 前端获取图片压缩后上传给后台
- 讲给Android程序员看的前端教程(35)——事件处理机制
- 给网页设计前端开发人员的建议
- 天气预报的前端不给力啊
- Yii给前端模板的赋值方式
- 前端传数组给后台的问题
- 《第一行代码》FragmentBestPractice项目(简单新闻应用)
- codeforces 149D Coloring Brackets (区间DP OR 记忆化搜索 总结!!!)
- BZOJ 1294: [SCOI2009]围豆豆Bean 状压DP,SPFA,计算集合射线法
- java方法返回json数据
- 正则表达式:注释过滤
- qemu中得到guest前端的通知处理后再发中断给前端
- Activity A跳转B,横竖屏切换
- AsynTask源码分析
- 编译安卓版flatbuf1.6.0
- 解决Windows 下 anaconda python2 与 python3 共存问题
- HDU1166 敌兵布阵(树状数组)
- lua元表以及元方法
- 【Java SE】如何安装JDK以及配置Java运行环境
- 关于我使用的分词工具的总结