QEMU源码全解析 —— virtio(21)

88 篇文章 19 订阅
本文详细回顾和总结了virtio驱动在QEMU中的初始化过程,包括设备重置、状态位设置、特性协商等关键步骤,涉及Linux内核源码的相关函数和流程。
摘要由CSDN通过智能技术生成

接前一篇文章: QEMU源码全解析 —— virtio(20)

从开始 QEMU源码全解析 —— virtio(17) QEMU源码全解析 —— virtio(20) ,用4篇文章讲解了 virtio 驱动的加载过程,本回来回顾和复习一下。

一般来讲,virtio驱动初始化一个设备的过程如下:

(1)重置设备

这是在上一回所讲的register_virtio_device函数中通过dev->config->reset调用完成的。这里要注意,老版本中调用的是dev->config->reset(),而新版本中直接使用virtio_reset_device函数。该函数在 Linux 内核源码/drivers/virtio/virtio.c中,代码如下:

  1. /**
  2. * virtio_reset_device - quiesce device for removal
  3. * @dev: the device to reset
  4. *
  5. * Prevents device from sending interrupts and accessing memory.
  6. *
  7. * Generally used for cleanup during driver / device removal.
  8. *
  9. * Once this has been invoked, caller must ensure that
  10. * virtqueue_notify / virtqueue_kick are not in progress.
  11. *
  12. * Note: this guarantees that vq callbacks are not in progress, however caller
  13. * is responsible for preventing access from other contexts, such as a system
  14. * call/workqueue/bh. Invoking virtio_break_device then flushing any such
  15. * contexts is one way to handle that.
  16. * */
  17. void virtio_reset_device(struct virtio_device *dev)
  18. {
  19. #ifdef CONFIG_VIRTIO_HARDEN_NOTIFICATION
  20. /*
  21. * The below virtio_synchronize_cbs() guarantees that any
  22. * interrupt for this line arriving after
  23. * virtio_synchronize_vqs() has completed is guaranteed to see
  24. * vq->broken as true.
  25. */
  26. virtio_break_device(dev);
  27. virtio_synchronize_cbs(dev);
  28. #endif
  29. dev->config->reset(dev);
  30. }
  31. EXPORT_SYMBOL_GPL(virtio_reset_device);

可以看到,实际上virtio_reset_device函数可以说是了dev->config->reset()的简单封装,只是多了一些额外的配置选项及该选项下的相关处理。

(2)设置ACKNOWLEDGE状态位

设置ACKNOWLEDGE状态位,表示virtio驱动已经知道了该设备。这同样是在register_virtio_device函数中由virtio_add_status()函数完成的,代码片段如下:

  1. /* We always start by resetting the device, in case a previous
  2. * driver messed it up. This also tests that code path a little. */
  3. virtio_reset_device(dev);
  4. /* Acknowledge that we've seen the device. */
  5. virtio_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE);

virtio_add_status函数也在Linux内核源码/drivers/virtio/virtio.c中,代码如下:

  1. void virtio_add_status(struct virtio_device *dev, unsigned int status)
  2. {
  3. might_sleep();
  4. dev->config->set_status(dev, dev->config->get_status(dev) | status);
  5. }
  6. EXPORT_SYMBOL_GPL(virtio_add_status);

(3)设置DRIVER状态位

设置DRIVER状态位,表示virtio驱动知道怎样驱动该设备。这是在virtio总线的probe函数virtio_dev_probe中,通过virtio_add_status函数完成的。virtio_dev_probe函数在Linux内核源码/drivers/virtio/virtio.c中,代码如下:

  1. static struct bus_type virtio_bus = {
  2. .name = "virtio",
  3. .match = virtio_dev_match,
  4. .dev_groups = virtio_dev_groups,
  5. .uevent = virtio_uevent,
  6. .probe = virtio_dev_probe,
  7. .remove = virtio_dev_remove,
  8. };
  9. int register_virtio_driver(struct virtio_driver *driver)
  10. {
  11. /* Catch this early. */
  12. BUG_ON(driver->feature_table_size && !driver->feature_table);
  13. driver->driver.bus = &virtio_bus;
  14. return driver_register(&driver->driver);
  15. }
  16. EXPORT_SYMBOL_GPL(register_virtio_driver);
  1. static int virtio_dev_probe(struct device *_d)
  2. {
  3. int err, i;
  4. struct virtio_device *dev = dev_to_virtio(_d);
  5. struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
  6. u64 device_features;
  7. u64 driver_features;
  8. u64 driver_features_legacy;
  9. /* We have a driver! */
  10. virtio_add_status(dev, VIRTIO_CONFIG_S_DRIVER);
  11. /* Figure out what features the device supports. */
  12. device_features = dev->config->get_features(dev);
  13. /* Figure out what features the driver supports. */
  14. driver_features = 0;
  15. for (i = 0; i < drv->feature_table_size; i++) {
  16. unsigned int f = drv->feature_table[i];
  17. BUG_ON(f >= 64);
  18. driver_features |= (1ULL << f);
  19. }
  20. /* Some drivers have a separate feature table for virtio v1.0 */
  21. if (drv->feature_table_legacy) {
  22. driver_features_legacy = 0;
  23. for (i = 0; i < drv->feature_table_size_legacy; i++) {
  24. unsigned int f = drv->feature_table_legacy[i];
  25. BUG_ON(f >= 64);
  26. driver_features_legacy |= (1ULL << f);
  27. }
  28. } else {
  29. driver_features_legacy = driver_features;
  30. }
  31. if (device_features & (1ULL << VIRTIO_F_VERSION_1))
  32. dev->features = driver_features & device_features;
  33. else
  34. dev->features = driver_features_legacy & device_features;
  35. /* Transport features always preserved to pass to finalize_features. */
  36. for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++)
  37. if (device_features & (1ULL << i))
  38. __virtio_set_bit(dev, i);
  39. err = dev->config->finalize_features(dev);
  40. if (err)
  41. goto err;
  42. if (drv->validate) {
  43. u64 features = dev->features;
  44. err = drv->validate(dev);
  45. if (err)
  46. goto err;
  47. /* Did validation change any features? Then write them again. */
  48. if (features != dev->features) {
  49. err = dev->config->finalize_features(dev);
  50. if (err)
  51. goto err;
  52. }
  53. }
  54. err = virtio_features_ok(dev);
  55. if (err)
  56. goto err;
  57. err = drv->probe(dev);
  58. if (err)
  59. goto err;
  60. /* If probe didn't do it, mark device DRIVER_OK ourselves. */
  61. if (!(dev->config->get_status(dev) & VIRTIO_CONFIG_S_DRIVER_OK))
  62. virtio_device_ready(dev);
  63. if (drv->scan)
  64. drv->scan(dev);
  65. virtio_config_enable(dev);
  66. return 0;
  67. err:
  68. virtio_add_status(dev, VIRTIO_CONFIG_S_FAILED);
  69. return err;
  70. }

关键代码片段为:

  1. /* We have a driver! */
  2. virtio_add_status(dev, VIRTIO_CONFIG_S_DRIVER);

(4)读取virtio设备的feature位,求出驱动设置的feature

读取virtio设备的feature位,求出驱动设置的feature。这也是在上边的virtio_dev_probe函数中完成的,代码片段如下:

  1. /* Figure out what features the device supports. */
  2. device_features = dev->config->get_features(dev);
  3. /* Figure out what features the driver supports. */
  4. driver_features = 0;
  5. for (i = 0; i < drv->feature_table_size; i++) {
  6. unsigned int f = drv->feature_table[i];
  7. BUG_ON(f >= 64);
  8. driver_features |= (1ULL << f);
  9. }
  10. /* Some drivers have a separate feature table for virtio v1.0 */
  11. if (drv->feature_table_legacy) {
  12. driver_features_legacy = 0;
  13. for (i = 0; i < drv->feature_table_size_legacy; i++) {
  14. unsigned int f = drv->feature_table_legacy[i];
  15. BUG_ON(f >= 64);
  16. driver_features_legacy |= (1ULL << f);
  17. }
  18. } else {
  19. driver_features_legacy = driver_features;
  20. }

(5)将两者(virtio设备的feature和驱动设置的feature)计算子集

将两者(virtio设备的feature和驱动设置的feature)计算子集。这也是在上边的virtio_dev_probe函数中完成的,代码片段如下:

  1. if (device_features & (1ULL << VIRTIO_F_VERSION_1))
  2. dev->features = driver_features & device_features;
  3. else
  4. dev->features = driver_features_legacy & device_features;

(6)向设备写入此子集特性

计算virtio设备的feature和驱动设置的feature的子集后,向设备写入这个子集特性。这也是在上边的virtio_dev_probe函数中完成的,代码片段如下:

  1. /* Transport features always preserved to pass to finalize_features. */
  2. for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++)
  3. if (device_features & (1ULL << i))
  4. __virtio_set_bit(dev, i);
  5. err = dev->config->finalize_features(dev);
  6. if (err)
  7. goto err;

计算driver_features和device_features,然后调用virtio_finalize_features。

(7)设置FEATURES_OK特性位

设置FEATURES_OK特性位,在此之后,virtio驱动就不会再接收新的特性了。这也是在上边的virtio_dev_probe函数中完成的,代码片段如下:

  1. err = virtio_features_ok(dev);
  2. if (err)
  3. goto err;

这一步是在virtio_features_ok函数中通过调用virtio_add_status函数完成的。virtio_features_ok函数也在Linux内核源码/drivers/virtio/virtio.c中,代码如下:

  1. /* Do some validation, then set FEATURES_OK */
  2. static int virtio_features_ok(struct virtio_device *dev)
  3. {
  4. unsigned int status;
  5. might_sleep();
  6. if (virtio_check_mem_acc_cb(dev)) {
  7. if (!virtio_has_feature(dev, VIRTIO_F_VERSION_1)) {
  8. dev_warn(&dev->dev,
  9. "device must provide VIRTIO_F_VERSION_1\n");
  10. return -ENODEV;
  11. }
  12. if (!virtio_has_feature(dev, VIRTIO_F_ACCESS_PLATFORM)) {
  13. dev_warn(&dev->dev,
  14. "device must provide VIRTIO_F_ACCESS_PLATFORM\n");
  15. return -ENODEV;
  16. }
  17. }
  18. if (!virtio_has_feature(dev, VIRTIO_F_VERSION_1))
  19. return 0;
  20. virtio_add_status(dev, VIRTIO_CONFIG_S_FEATURES_OK);
  21. status = dev->config->get_status(dev);
  22. if (!(status & VIRTIO_CONFIG_S_FEATURES_OK)) {
  23. dev_err(&dev->dev, "virtio: device refuses features: %x\n",
  24. status);
  25. return -ENODEV;
  26. }
  27. return 0;
  28. }

上边提到的关键代码片段为:

    virtio_add_status(dev, VIRTIO_CONFIG_S_FEATURES_OK);

(8)重新读取设备的feature位

重新读取设备的feature位,确保设置了VIRTIO_CONFIG_S_FEATURES_OK。代码片段如下:

  1. status = dev->config->get_status(dev);
  2. if (!(status & VIRTIO_CONFIG_S_FEATURES_OK)) {
  3. dev_err(&dev->dev, "virtio: device refuses features: %x\n",
  4. status);
  5. return -ENODEV;
  6. }

如果并没有设置成功,则设备不支持virtio驱动设置的一些状态,表示设备不可用。这同样是在virtio_features_ok函数中完成的。

(9)执行设备相关的初始化操作

执行设备相关的 初始化操作 ,包括发现设备的virtqueue、读写virtio设备的配置空间等。这些都是在virtio_dev_probe函数中通过调用驱动的probe函数(即drv->probe(dev))完成的。代码片段如下:

  1. err = drv->probe(dev);
  2. if (err)
  3. goto err;

对于virtio balloon来说是virtballoon_probe函数。该函数在Linux内核源码/drivers/virtio/virtio_balloon.c中,代码如下:

  1. static int virtballoon_probe(struct virtio_device *vdev)
  2. {
  3. struct virtio_balloon *vb;
  4. int err;
  5. if (!vdev->config->get) {
  6. dev_err(&vdev->dev, "%s failure: config access disabled\n",
  7. __func__);
  8. return -EINVAL;
  9. }
  10. vdev->priv = vb = kzalloc(sizeof(*vb), GFP_KERNEL);
  11. if (!vb) {
  12. err = -ENOMEM;
  13. goto out;
  14. }
  15. INIT_WORK(&vb->update_balloon_stats_work, update_balloon_stats_func);
  16. INIT_WORK(&vb->update_balloon_size_work, update_balloon_size_func);
  17. spin_lock_init(&vb->stop_update_lock);
  18. mutex_init(&vb->balloon_lock);
  19. init_waitqueue_head(&vb->acked);
  20. vb->vdev = vdev;
  21. balloon_devinfo_init(&vb->vb_dev_info);
  22. err = init_vqs(vb);
  23. if (err)
  24. goto out_free_vb;
  25. #ifdef CONFIG_BALLOON_COMPACTION
  26. vb->vb_dev_info.migratepage = virtballoon_migratepage;
  27. #endif
  28. if (virtio_has_feature(vdev, VIRTIO_BALLOON_F_FREE_PAGE_HINT)) {
  29. /*
  30. * There is always one entry reserved for cmd id, so the ring
  31. * size needs to be at least two to report free page hints.
  32. */
  33. if (virtqueue_get_vring_size(vb->free_page_vq) < 2) {
  34. err = -ENOSPC;
  35. goto out_del_vqs;
  36. }
  37. vb->balloon_wq = alloc_workqueue("balloon-wq",
  38. WQ_FREEZABLE | WQ_CPU_INTENSIVE, 0);
  39. if (!vb->balloon_wq) {
  40. err = -ENOMEM;
  41. goto out_del_vqs;
  42. }
  43. INIT_WORK(&vb->report_free_page_work, report_free_page_func);
  44. vb->cmd_id_received_cache = VIRTIO_BALLOON_CMD_ID_STOP;
  45. vb->cmd_id_active = cpu_to_virtio32(vb->vdev,
  46. VIRTIO_BALLOON_CMD_ID_STOP);
  47. vb->cmd_id_stop = cpu_to_virtio32(vb->vdev,
  48. VIRTIO_BALLOON_CMD_ID_STOP);
  49. spin_lock_init(&vb->free_page_list_lock);
  50. INIT_LIST_HEAD(&vb->free_page_list);
  51. /*
  52. * We're allowed to reuse any free pages, even if they are
  53. * still to be processed by the host.
  54. */
  55. err = virtio_balloon_register_shrinker(vb);
  56. if (err)
  57. goto out_del_balloon_wq;
  58. }
  59. if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_DEFLATE_ON_OOM)) {
  60. vb->oom_nb.notifier_call = virtio_balloon_oom_notify;
  61. vb->oom_nb.priority = VIRTIO_BALLOON_OOM_NOTIFY_PRIORITY;
  62. err = register_oom_notifier(&vb->oom_nb);
  63. if (err < 0)
  64. goto out_unregister_shrinker;
  65. }
  66. if (virtio_has_feature(vdev, VIRTIO_BALLOON_F_PAGE_POISON)) {
  67. /* Start with poison val of 0 representing general init */
  68. __u32 poison_val = 0;
  69. /*
  70. * Let the hypervisor know that we are expecting a
  71. * specific value to be written back in balloon pages.
  72. *
  73. * If the PAGE_POISON value was larger than a byte we would
  74. * need to byte swap poison_val here to guarantee it is
  75. * little-endian. However for now it is a single byte so we
  76. * can pass it as-is.
  77. */
  78. if (!want_init_on_free())
  79. memset(&poison_val, PAGE_POISON, sizeof(poison_val));
  80. virtio_cwrite_le(vb->vdev, struct virtio_balloon_config,
  81. poison_val, &poison_val);
  82. }
  83. vb->pr_dev_info.report = virtballoon_free_page_report;
  84. if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_REPORTING)) {
  85. unsigned int capacity;
  86. capacity = virtqueue_get_vring_size(vb->reporting_vq);
  87. if (capacity < PAGE_REPORTING_CAPACITY) {
  88. err = -ENOSPC;
  89. goto out_unregister_oom;
  90. }
  91. /*
  92. * The default page reporting order is @pageblock_order, which
  93. * corresponds to 512MB in size on ARM64 when 64KB base page
  94. * size is used. The page reporting won't be triggered if the
  95. * freeing page can't come up with a free area like that huge.
  96. * So we specify the page reporting order to 5, corresponding
  97. * to 2MB. It helps to avoid THP splitting if 4KB base page
  98. * size is used by host.
  99. *
  100. * Ideally, the page reporting order is selected based on the
  101. * host's base page size. However, it needs more work to report
  102. * that value. The hard-coded order would be fine currently.
  103. */
  104. #if defined(CONFIG_ARM64) && defined(CONFIG_ARM64_64K_PAGES)
  105. vb->pr_dev_info.order = 5;
  106. #endif
  107. err = page_reporting_register(&vb->pr_dev_info);
  108. if (err)
  109. goto out_unregister_oom;
  110. }
  111. virtio_device_ready(vdev);
  112. if (towards_target(vb))
  113. virtballoon_changed(vdev);
  114. return 0;
  115. out_unregister_oom:
  116. if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_DEFLATE_ON_OOM))
  117. unregister_oom_notifier(&vb->oom_nb);
  118. out_unregister_shrinker:
  119. if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_FREE_PAGE_HINT))
  120. virtio_balloon_unregister_shrinker(vb);
  121. out_del_balloon_wq:
  122. if (virtio_has_feature(vdev, VIRTIO_BALLOON_F_FREE_PAGE_HINT))
  123. destroy_workqueue(vb->balloon_wq);
  124. out_del_vqs:
  125. vdev->config->del_vqs(vdev);
  126. out_free_vb:
  127. kfree(vb);
  128. out:
  129. return err;
  130. }

(10)设置DRIVER_OK状态位

设置DRIVER_OK状态位,这通常是在具体设备驱动的probe函数中,通过调用virtio_device_ready函数完成的。对于virtio ballon设备来说,就是上边的virtballoon_probe函数。代码片段如下:

    virtio_device_ready(vdev);

virtio_device_ready函数在Linux内核源码/include/linux/virtio_config.h中,代码如下:

  1. /**
  2. * virtio_device_ready - enable vq use in probe function
  3. * @dev: the virtio device
  4. *
  5. * Driver must call this to use vqs in the probe function.
  6. *
  7. * Note: vqs are enabled automatically after probe returns.
  8. */
  9. static inline
  10. void virtio_device_ready(struct virtio_device *dev)
  11. {
  12. unsigned status = dev->config->get_status(dev);
  13. WARN_ON(status & VIRTIO_CONFIG_S_DRIVER_OK);
  14. #ifdef CONFIG_VIRTIO_HARDEN_NOTIFICATION
  15. /*
  16. * The virtio_synchronize_cbs() makes sure vring_interrupt()
  17. * will see the driver specific setup if it sees vq->broken
  18. * as false (even if the notifications come before DRIVER_OK).
  19. */
  20. virtio_synchronize_cbs(dev);
  21. __virtio_unbreak_device(dev);
  22. #endif
  23. /*
  24. * The transport should ensure the visibility of vq->broken
  25. * before setting DRIVER_OK. See the comments for the transport
  26. * specific set_status() method.
  27. *
  28. * A well behaved device will only notify a virtqueue after
  29. * DRIVER_OK, this means the device should "see" the coherenct
  30. * memory write that set vq->broken as false which is done by
  31. * the driver when it sees DRIVER_OK, then the following
  32. * driver's vring_interrupt() will see vq->broken as false so
  33. * we won't lose any notification.
  34. */
  35. dev->config->set_status(dev, status | VIRTIO_CONFIG_S_DRIVER_OK);
  36. }

如果设备驱动没有设置DRIVER_OK状态位,则会由总线的probe函数virtio_dev_probe函数来设置。这也是在上边的virtio_dev_probe函数中完成的,代码片段如下:

  1. /* If probe didn't do it, mark device DRIVER_OK ourselves. */
  2. if (!(dev->config->get_status(dev) & VIRTIO_CONFIG_S_DRIVER_OK))
  3. virtio_device_ready(dev);

至此,virtio驱动的加载就基本讲解完了。下一回开始讲解virtio驱动的初始化。

举报

选择你想要举报的内容(必选)
  • 内容涉黄
  • 政治相关
  • 内容抄袭
  • 涉嫌广告
  • 内容侵权
  • 侮辱谩骂
  • 样式问题
  • 其他