virtio-bus

virtio_bus

virtio相关在这个位置。virtio在操作系统和硬件中起的作用在virtio背景中有说明,它与virtio涉及到的全部硬件设备都相关

./unikraft/plat/drivers/include

源码解读

virtio_bus.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct virtio_dev;
typedef int (*virtio_driver_init_func_t)(struct uk_alloc *); // 和ukbus里的init函数对应
typedef int (*virtio_driver_add_func_t)(struct virtio_dev *);

// virtio设备状态管理
enum virtio_dev_status {
/** Device reset */
VIRTIO_DEV_RESET,
/** Device Acknowledge and ready to be configured */
VIRTIO_DEV_INITIALIZED,
/** Device feature negotiated and ready to the started */
VIRTIO_DEV_CONFIGURED,
/** Device Running */
VIRTIO_DEV_RUNNING,
/** Device Stopped */
VIRTIO_DEV_STOPPED,
};

接下来定义了所有virtio设备的公有操作: 配置选项,feature bit,状态,前后端通信队列virtqueue操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* The structure define a list of configuration operation on a virtio device.
*/
struct virtio_config_ops {
/** Resetting the device */
void (*device_reset)(struct virtio_dev *vdev);
/** Set configuration option */
int (*config_set)(struct virtio_dev *vdev, __u16 offset,
const void *buf, __u32 len);
/** Get configuration option */
int (*config_get)(struct virtio_dev *vdev, __u16 offset, void *buf,
__u32 len, __u8 type_len);
/** Get the feature */
__u64 (*features_get)(struct virtio_dev *vdev);
/** Set the feature */
void (*features_set)(struct virtio_dev *vdev, __u64 features);
/** Get and Set Status */
__u8 (*status_get)(struct virtio_dev *vdev);
void (*status_set)(struct virtio_dev *vdev, __u8 status);
/** Find the virtqueue */
int (*vqs_find)(struct virtio_dev *vdev, __u16 num_vq, __u16 *vq_size);
/** Setup the virtqueue */
struct virtqueue *(*vq_setup)(struct virtio_dev *vdev, __u16 num_desc,
__u16 queue_id,
virtqueue_callback_t callback,
struct uk_alloc *a);
void (*vq_release)(struct virtio_dev *vdev, struct virtqueue *vq,
struct uk_alloc *a);
};

再对virtio_driver和virtio_dev进行定义:关于driver和device的关系:https://zhuanlan.zhihu.com/p/361918197. 具体内容也放在了virtio背景相关的文档里

virtio分为driver和device,driver部分运行于guest操作系统中,device部分运行于hypervisor中(监视虚拟机和硬件交互),driver和device是生产者和消费者模式动作,driver生产内存,device消费内存。不同virtio版本之间是互相兼容的,driver和device版本不同也可以互相运转。

driver和dev是相互绑定的关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* The structure define the virtio driver.
* 主要是关于函数绑定
*/
struct virtio_driver {
/** Next entry of the driver list */
UK_TAILQ_ENTRY(struct virtio_driver) next;
/** The id map for the virtio device */
const struct virtio_dev_id *dev_ids;
/** The init function for the driver */
virtio_driver_init_func_t init;
/** Adding the virtio device */
virtio_driver_add_func_t add_dev;
};

/**
* The structure defines the virtio device.
* 定义device相关操作以及virtqueue
*/
struct virtio_dev {
/* Feature bit describing the virtio device */
__u64 features;
/* List of the virtqueue for the device */
UK_TAILQ_HEAD(virtqueue_head, struct virtqueue) vqs;
/* Private data of the driver */
void *priv;
/* Virtio device identifier */
struct virtio_dev_id id;
/* List of the config operations */
struct virtio_config_ops *cops;
/* Reference to the virtio driver for the device */
struct virtio_driver *vdrv;
/* Status of the device */
enum virtio_dev_status status;
};

后面涉及的都是driver和dev相关的setter和getter函数,不展开了

virtio_bus.c

在这里需要先补充virtio_config对driver定义的相关宏

1
2
3
4
5
6
#define VIRTIO_CONFIG_STATUS_RESET         0x0  /* Reset the device */
#define VIRTIO_CONFIG_STATUS_ACK 0x1 /* recognize device as virtio */
#define VIRTIO_CONFIG_STATUS_DRIVER 0x2 /* driver for the device found*/
#define VIRTIO_CONFIG_STATUS_DRIVER_OK 0x4 /* initialization is complete */
#define VIRTIO_CONFIG_STATUS_NEEDS_RESET 0x40 /* device needs reset */
#define VIRTIO_CONFIG_STATUS_FAIL 0x80 /* device something's wrong*/
  • driver与dev的配对,遍历driver队列

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    /**
    * Find a match driver
    * @param vdev
    * Reference to the virtio device.
    */
    static struct virtio_driver *find_match_drv(struct virtio_dev *vdev)
    {
    int i = 0;
    struct virtio_driver *drv = NULL;

    UK_TAILQ_FOREACH(drv, &virtio_drvs, next) {
    i = 0;
    while (drv->dev_ids[i].virtio_device_id != VIRTIO_ID_INVALID) {
    if (virtio_device_id_match(&drv->dev_ids[i],
    &vdev->id)) { // id一致 就是dev要的driver
    return drv;
    }
    i++;
    }
    }
    return NULL;
    }
  • 注册device

流程包括:找到dev对应的driver;初始化device,具体执行流程在virtio_device_reinit中,它首先识别设备(ACK),然后找到driver(STATUS_DRIVER),最后标记设备初始状态为VIRTIO_DEV_INITIALIZED;再初始化和通信队列virtioqueue,最后加入driver列表中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
int virtio_bus_register_device(struct virtio_dev *vdev)
{
struct virtio_driver *drv = NULL;
int rc = 0;

UK_ASSERT(vdev);
/* Check for the dev with the driver list */
drv = find_match_drv(vdev);
if (!drv) {
uk_pr_err("Failed to find the driver for the virtio device %p (id:%"__PRIu16")\n",
vdev, vdev->id.virtio_device_id);
return -EFAULT;
}
vdev->vdrv = drv;

/* Initialize the device */
rc = virtio_device_reinit(vdev);
if (rc != 0) {
uk_pr_err("Failed to initialize the virtio device %p (id:%"__PRIu16": %d\n",
vdev, vdev->id.virtio_device_id, rc);
return rc;
}

/* Initialize the virtqueue list */
UK_TAILQ_INIT(&vdev->vqs);

/* Calling the driver add device */
rc = drv->add_dev(vdev);
if (rc != 0) {
uk_pr_err("Failed to add the virtio device %p: %d\n", vdev, rc);
goto virtio_dev_fail_set;
}
exit:
return rc;

virtio_dev_fail_set:
/**
* We set the status to fail. We can ignore the exit status from the
* status update.
*/
virtio_dev_status_update(vdev, VIRTIO_CONFIG_STATUS_FAIL);
goto exit;
}


/**
* Reinitialize the virtio device
* @param vdev
* Reference to the virtio device.
*/
static int virtio_device_reinit(struct virtio_dev *vdev)
{
int rc = 0;

/**
* Resetting the virtio device
* This may not be necessary while initializing the device for the first
* time.
*/
if (vdev->cops->device_reset) {
vdev->cops->device_reset(vdev);
/* Set the device status */
vdev->status = VIRTIO_DEV_RESET;
}
/* Acknowledge the virtio device */
rc = virtio_dev_status_update(vdev, VIRTIO_CONFIG_STATUS_ACK);
if (rc != 0) {
uk_pr_err("Failed to acknowledge the virtio device %p: %d\n",
vdev, rc);
return rc;
}

/* Acknowledge the virtio driver */
rc = virtio_dev_status_update(vdev, VIRTIO_CONFIG_STATUS_DRIVER);
if (rc != 0) {
uk_pr_err("Failed to acknowledge the virtio driver %p: %d\n",
vdev, rc);
return rc;
}
vdev->status = VIRTIO_DEV_INITIALIZED;
uk_pr_info("Virtio device %p initialized\n", vdev);
return rc;
}
  • driver注册
    上面的函数讲了如何注册一个dev,这里讲注册driver

其中probe:probe函数在设备驱动注册最后收尾工作,当设备的device 和其对应的driver 在总线上完成配对之后,系统就调用platform设备的probe函数完成驱动注册最后工作。资源、 中断调用函数以及其他相关工作。

这里没有列驱动注册完成后的收尾工作,默认return 0. 但是init和probe还是要注册上的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

/**
* Probe for the virtio device.
*/
static int virtio_bus_probe(void)
{
return 0;
}

/**
* Initialize the virtio bus driver(s).
* @param mem_alloc
* Reference to the mem_allocator.
* @return
* (int) On successful initialization return the count of device
* initialized.
* On error return -1.
*/

/**
* Initialize the virtio bus driver(s).
* @param mem_alloc
* Reference to the mem_allocator.
* @return
* (int) On successful initialization return the count of device
* initialized.
* On error return -1.
*/
static int virtio_bus_init(struct uk_alloc *mem_alloc)
{
struct virtio_driver *drv = NULL, *ndrv = NULL;
int ret = 0, dev_count = 0;

a = mem_alloc;
UK_TAILQ_FOREACH_SAFE(drv, &virtio_drvs, next, ndrv) {
if (drv->init) {
ret = drv->init(a);
if (unlikely(ret)) {
uk_pr_err("Failed to initialize virtio driver %p: %d\n",
drv, ret);
UK_TAILQ_REMOVE(&virtio_drvs, drv, next);
} else
dev_count++;
}
}
return (dev_count > 0) ? dev_count : 0;
}

/**
* Register the virtio driver(s).
* @param vdrv
* Reference to the virtio_driver
*/
void _virtio_bus_register_driver(struct virtio_driver *vdrv)
{
UK_TAILQ_INSERT_TAIL(&virtio_drvs, vdrv, next);
}

static struct uk_bus virtio_bus = {
.init = virtio_bus_init,
.probe = virtio_bus_probe,
};
UK_BUS_REGISTER(&virtio_bus);