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

88 篇文章 19 订阅
本文继续分析QEMU virtio设备的初始化过程,重点探讨pci_enable_device函数使能PCI设备,并解释force_legacy参数如何决定调用virtio_pci_legacy_probe或virtio_pci_modern_probe进行设备初始化。下一部分将深入研究这两个关键函数,特别是virtio_pci_modern_probe的细节。
摘要由CSDN通过智能技术生成

接前一篇文章:

前文书解析了virtio_pci_driver的 probe 回调函数virtio_pci_probe()的前三段代码,本回继续讲解余下代码。为了便于理解和回顾,再次贴出virtio_pci_probe函数源码,在 Linux 内核源码/drivers/ virtio /virtio_pci_common.c中,如下所示:

  1. static int virtio_pci_probe(struct pci_dev *pci_dev,
  2. const struct pci_device_id *id)
  3. {
  4. struct virtio_pci_device *vp_dev, *reg_dev = NULL;
  5. int rc;
  6. /* allocate our structure and fill it out */
  7. vp_dev = kzalloc(sizeof(struct virtio_pci_device), GFP_KERNEL);
  8. if (!vp_dev)
  9. return -ENOMEM;
  10. pci_set_drvdata(pci_dev, vp_dev);
  11. vp_dev->vdev.dev.parent = &pci_dev->dev;
  12. vp_dev->vdev.dev.release = virtio_pci_release_dev;
  13. vp_dev->pci_dev = pci_dev;
  14. INIT_LIST_HEAD(&vp_dev->virtqueues);
  15. spin_lock_init(&vp_dev->lock);
  16. /* enable the device */
  17. rc = pci_enable_device(pci_dev);
  18. if (rc)
  19. goto err_enable_device;
  20. if (force_legacy) {
  21. rc = virtio_pci_legacy_probe(vp_dev);
  22. /* Also try modern mode if we can't map BAR0 (no IO space). */
  23. if (rc == -ENODEV || rc == -ENOMEM)
  24. rc = virtio_pci_modern_probe(vp_dev);
  25. if (rc)
  26. goto err_probe;
  27. } else {
  28. rc = virtio_pci_modern_probe(vp_dev);
  29. if (rc == -ENODEV)
  30. rc = virtio_pci_legacy_probe(vp_dev);
  31. if (rc)
  32. goto err_probe;
  33. }
  34. pci_set_master(pci_dev);
  35. vp_dev->is_legacy = vp_dev->ldev.ioaddr ? true : false;
  36. rc = register_virtio_device(&vp_dev->vdev);
  37. reg_dev = vp_dev;
  38. if (rc)
  39. goto err_register;
  40. return 0;
  41. err_register:
  42. if (vp_dev->is_legacy)
  43. virtio_pci_legacy_remove(vp_dev);
  44. else
  45. virtio_pci_modern_remove(vp_dev);
  46. err_probe:
  47. pci_disable_device(pci_dev);
  48. err_enable_device:
  49. if (reg_dev)
  50. put_device(&vp_dev->vdev.dev);
  51. else
  52. kfree(vp_dev);
  53. return rc;
  54. }

(4)调用pci_enable_device函数使能该 PCI 设备。代码片段如下:

  1. /* enable the device */
  2. rc = pci_enable_device(pci_dev);
  3. if (rc)
  4. goto err_enable_device;

pci_dev是virtio_pci_probe函数的第1个参数,类型为struct pci_dev。

pci_enable_device函数在Linux内核源码/drivers/pci/pci.c中,代码如下:

  1. /**
  2. * pci_enable_device - Initialize device before it's used by a driver.
  3. * @dev: PCI device to be initialized
  4. *
  5. * Initialize device before it's used by a driver. Ask low-level code
  6. * to enable I/O and memory. Wake up the device if it was suspended.
  7. * Beware, this function can fail.
  8. *
  9. * Note we don't actually enable the device many times if we call
  10. * this function repeatedly (we just increment the count).
  11. */
  12. int pci_enable_device(struct pci_dev *dev)
  13. {
  14. return pci_enable_device_flags(dev, IORESOURCE_MEM | IORESOURCE_IO);
  15. }
  16. EXPORT_SYMBOL(pci_enable_device);

pci_enable_device_flags函数在同文件中,代码如下:

  1. static int pci_enable_device_flags(struct pci_dev *dev, unsigned long flags)
  2. {
  3. struct pci_dev *bridge;
  4. int err;
  5. int i, bars = 0;
  6. /*
  7. * Power state could be unknown at this point, either due to a fresh
  8. * boot or a device removal call. So get the current power state
  9. * so that things like MSI message writing will behave as expected
  10. * (e.g. if the device really is in D0 at enable time).
  11. */
  12. pci_update_current_state(dev, dev->current_state);
  13. if (atomic_inc_return(&dev->enable_cnt) > 1)
  14. return 0; /* already enabled */
  15. bridge = pci_upstream_bridge(dev);
  16. if (bridge)
  17. pci_enable_bridge(bridge);
  18. /* only skip sriov related */
  19. for (i = 0; i <= PCI_ROM_RESOURCE; i++)
  20. if (dev->resource[i].flags & flags)
  21. bars |= (1 << i);
  22. for (i = PCI_BRIDGE_RESOURCES; i < DEVICE_COUNT_RESOURCE; i++)
  23. if (dev->resource[i].flags & flags)
  24. bars |= (1 << i);
  25. err = do_pci_enable_device(dev, bars);
  26. if (err < 0)
  27. atomic_dec(&dev->enable_cnt);
  28. return err;
  29. }

(5)接下来,调用virtio_pci_legacy_probe或者virtio_pci_modern_probe函数来初始化该PCI设备对应的virtio设备。代码片段如下:

  1. if (force_legacy) {
  2. rc = virtio_pci_legacy_probe(vp_dev);
  3. /* Also try modern mode if we can't map BAR0 (no IO space). */
  4. if (rc == -ENODEV || rc == -ENOMEM)
  5. rc = virtio_pci_modern_probe(vp_dev);
  6. if (rc)
  7. goto err_probe;
  8. } else {
  9. rc = virtio_pci_modern_probe(vp_dev);
  10. if (rc == -ENODEV)
  11. rc = virtio_pci_legacy_probe(vp_dev);
  12. if (rc)
  13. goto err_probe;
  14. }

如果force_legacy标志为true,则先调用virtio_pci_legacy_probe函数,出现错误(-ENODEV或-ENOMEM)时再调用virtio_pci_modern_probe函数;如果force_legacy标志为false,则正好反过来,先调用virtio_pci_modern_probe函数,出现错误(-ENODEV或-ENOMEM)时再调用virtio_pci_legacy_probe函数。

force_legacy是模块参数,也在Linux内核源码/drivers/virtio/virtio_pci_common.c中,相关代码如下:

  1. static bool force_legacy = false;
  2. #if IS_ENABLED(CONFIG_VIRTIO_PCI_LEGACY)
  3. module_param(force_legacy, bool, 0444);
  4. MODULE_PARM_DESC(force_legacy,
  5. "Force legacy mode for transitional virtio 1 devices");
  6. #endif
  • virtio_pci_legacy_probe

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

  1. /* the PCI probing function */
  2. int virtio_pci_legacy_probe(struct virtio_pci_device *vp_dev)
  3. {
  4. struct virtio_pci_legacy_device *ldev = &vp_dev->ldev;
  5. struct pci_dev *pci_dev = vp_dev->pci_dev;
  6. int rc;
  7. ldev->pci_dev = pci_dev;
  8. rc = vp_legacy_probe(ldev);
  9. if (rc)
  10. return rc;
  11. vp_dev->isr = ldev->isr;
  12. vp_dev->vdev.id = ldev->id;
  13. vp_dev->vdev.config = &virtio_pci_config_ops;
  14. vp_dev->config_vector = vp_config_vector;
  15. vp_dev->setup_vq = setup_vq;
  16. vp_dev->del_vq = del_vq;
  17. return 0;
  18. }
  • virtio_pci_modern_probe

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

  1. /* the PCI probing function */
  2. int virtio_pci_modern_probe(struct virtio_pci_device *vp_dev)
  3. {
  4. struct virtio_pci_modern_device *mdev = &vp_dev->mdev;
  5. struct pci_dev *pci_dev = vp_dev->pci_dev;
  6. int err;
  7. mdev->pci_dev = pci_dev;
  8. err = vp_modern_probe(mdev);
  9. if (err)
  10. return err;
  11. if (mdev->device)
  12. vp_dev->vdev.config = &virtio_pci_config_ops;
  13. else
  14. vp_dev->vdev.config = &virtio_pci_config_nodev_ops;
  15. vp_dev->config_vector = vp_config_vector;
  16. vp_dev->setup_vq = setup_vq;
  17. vp_dev->del_vq = del_vq;
  18. vp_dev->isr = mdev->isr;
  19. vp_dev->vdev.id = mdev->id;
  20. return 0;
  21. }

下一回将对这两个函数尤其是virtio_pci_modern_probe()进行详细解析。

举报

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