Τη Παρασκευή, 13 Μαρτίου 2020 - 4:23:05 μ.μ. UTC+2, ο χρήστης Waldek
Kozaczuk έγραψε:
>
> Hey Fotis,
>
> Thank you for the patches. I have applied the 1st one from the series. I
> have some questions and suggestions regarding this one though. Please see
> them below.
>
>
> On Sunday, March 8, 2020 at 10:44:52 AM UTC-4, Fotis Xenakis wrote:
>>
>> The latest virtio spec adds shared memory regions:
>> "Shared memory regions are an additional facility available to devices
>> that need a region of memory that’s continuously shared between the
>> device and the driver, rather than passed between them in the way
>> virtqueue elements are."
>>
>> In virtio over PCI, these are enumerated as a sequence of
>> VIRTIO_PCI_CAP_SHARED_MEMORY_CFG capabilities, one per region.
>>
>> This patch extends the virtio over PCI implementation to discover all
>> such regions provided by a device.
>>
>> Signed-off-by: Fotis Xenakis <[email protected]>
>> ---
>> drivers/virtio-pci-device.cc | 78 +++++++++++++++++++++++++++++-------
>> drivers/virtio-pci-device.hh | 44 +++++++++++++-------
>> 2 files changed, 93 insertions(+), 29 deletions(-)
>>
>> diff --git a/drivers/virtio-pci-device.cc b/drivers/virtio-pci-device.cc
>> index c107ddd7..8e02c1c1 100644
>> --- a/drivers/virtio-pci-device.cc
>> +++ b/drivers/virtio-pci-device.cc
>> @@ -269,37 +269,87 @@ bool virtio_modern_pci_device::parse_pci_config()
>> return false;
>> }
>>
>> - parse_virtio_capability(_common_cfg, VIRTIO_PCI_CAP_COMMON_CFG);
>> - parse_virtio_capability(_isr_cfg, VIRTIO_PCI_CAP_ISR_CFG);
>> - parse_virtio_capability(_notify_cfg, VIRTIO_PCI_CAP_NOTIFY_CFG);
>> - parse_virtio_capability(_device_cfg, VIRTIO_PCI_CAP_DEVICE_CFG);
>> + parse_virtio_capability(_common_cfg, VIRTIO_PCI_CAP_COMMON_CFG,
>> nullptr);
>> + parse_virtio_capability(_isr_cfg, VIRTIO_PCI_CAP_ISR_CFG, nullptr);
>> + parse_virtio_capability(_notify_cfg, VIRTIO_PCI_CAP_NOTIFY_CFG,
>> nullptr);
>> + parse_virtio_capability(_device_cfg, VIRTIO_PCI_CAP_DEVICE_CFG,
>> nullptr);
>> + parse_virtio_capabilities(_shm_cfgs,
>> VIRTIO_PCI_CAP_SHARED_MEMORY_CFG);
>>
>> if (_notify_cfg) {
>> - _notify_offset_multiplier
>> =_dev->pci_readl(_notify_cfg->get_cfg_offset() +
>> + _notify_offset_multiplier =
>> _dev->pci_readl(_notify_cfg->get_cfg_offset() +
>> offsetof(virtio_pci_notify_cap,
>> notify_offset_multiplier));
>> }
>>
>> return _common_cfg && _isr_cfg && _notify_cfg && _device_cfg;
>> }
>>
>> -void
>> virtio_modern_pci_device::parse_virtio_capability(std::unique_ptr<virtio_capability>
>>
>> &ptr, u8 type)
>> -{
>> - u8 cfg_offset = _dev->find_capability(pci::function::PCI_CAP_VENDOR,
>> [type] (pci::function *fun, u8 offset) {
>> - u8 cfg_type = fun->pci_readb(offset + offsetof(struct
>> virtio_pci_cap, cfg_type));
>> - return type == cfg_type;
>> +// Parse all virtio PCI capabilities whose types match @type and append
>> them to
>> +// @caps.
>> +// From the spec: "The device MAY offer more than one structure of any
>> type -
>> +// this makes it possible for the device to expose multiple interfaces
>> to
>> +// drivers. The order of the capabilities in the capability list
>> specifies the
>> +// order of preference suggested by the device. A device may specify
>> that this
>> +// ordering mechanism be overridden by the use of the id field."
>> +void virtio_modern_pci_device::parse_virtio_capabilities(
>> + std::vector<std::unique_ptr<virtio_capability>>& caps, u8 type)
>> +{
>> + std::vector<u8> past_ids;
>> + auto pred = [&past_ids] (u8 id) {
>> + return std::none_of(past_ids.cbegin(), past_ids.cend(), [id] (u8
>> past_id) {
>> + return past_id == id;
>> + });
>> + };
>> +
>> + while (true) {
>> + std::unique_ptr<virtio_capability> ptr;
>> + // Search for a matching capability with an id not already in
>> past_ids
>> + parse_virtio_capability(ptr, type, pred);
>> + if (!ptr) {
>> + return;
>> + }
>> + u8 id = _dev->pci_readb(ptr->get_cfg_offset() +
>> offsetof(virtio_pci_cap, id));
>> + past_ids.push_back(id);
>> + caps.emplace_back(ptr.release());
>> + }
>> +}
>>
> So the idea with the implementation of parse_virtio_capabilities() above
> is really to collect all capabilities of the given type (in our case so far
> VIRTIO_PCI_CAP_SHARED_MEMORY_CFG)? I wonder if the code can be simplified
> if we add a method function::find_capabilities(u8 cap_id) in
> pci-function.cc that will simply return a collection of matching offsets?
>
> The general idea - we do not have to do it in this patch. I noticed we
> re-read same data in u8 function::find_capability() over and over (and each
> read is an exit I think) and that is what cause the pci initialization
> slower especially for modern devices (I see 10ms difference in boot time
> between running the same program with '--virtio modern' and '--virtio
> legacy' (default). Maybe some easy caching - again let us not worry now.
>
This is exactly the purpose of parse_virtio_capabilities(): to parse all
the virtio PCI capabilities of a specific type (virtio_pci_cap.cfg_type). I
tried to make the patch non-pervasive as possible (not changing any public
interface), so I haven't really thought how such changes could help
simplify. I am looking into the option you suggested though, as it would
sure help reduce the number of pci_read*() calls, if not also simplify the
code!
It hadn't dawned on me that the pci_read*() calls probably cause VM exits
(which makes sense) and was pretty liberal with them. I am revisiting the
overall configuration parsing with this in mind and will come up with a v2
of the patch. My target is to avoid most of the duplicate calls to
pci_read*(), while not making it too complex. Do you think there is any
concern of the PCI configuration space changing during the parsing,
prohibiting caching (seems unlikely, just a thought)?
Also, is it better if I include the changes that actually expose the shared
memory regions to the drivers in v2 of the patch or create a new patch for
that when this is merged?
> +
>> +// Parse a single virtio PCI capability, whose type must match @type and
>> id must
>> +// satisfy @id_pred.
>> +void
>> virtio_modern_pci_device::parse_virtio_capability(std::unique_ptr<virtio_capability>&
>>
>> ptr, u8 type,
>> + std::function<bool (u8)> id_pred)
>> +{
>> + u8 cfg_offset = _dev->find_capability(pci::function::PCI_CAP_VENDOR,
>> + [type, id_pred] (pci::function *fun, u8 offset) {
>> + u8 cfg_type = fun->pci_readb(offset +
>> offsetof(virtio_pci_cap, cfg_type));
>> + if (cfg_type != type) {
>> + return false;
>> + }
>> + u8 id = fun->pci_readb(offset + offsetof(virtio_pci_cap,
>> id));
>> + if (id_pred && !id_pred(id)) {
>> + return false;
>> + }
>> + return true;
>>
> Easy optimization suggestion (given that I think each pci_readb is an exit
> to hypervisor) - maybe first check if id_pred not null and only then do
> pci_readb() and call id_pred. Most parse_virtio_capability() calls pass
> id_pred as null, right?
>
A very good suggestion indeed, I will include it in v2, thank you!
> });
>>
>> if (cfg_offset != 0xFF) {
>> - u8 bar_index = _dev->pci_readb(cfg_offset + offsetof(struct
>> virtio_pci_cap, bar));
>> - u32 offset = _dev->pci_readl(cfg_offset + offsetof(struct
>> virtio_pci_cap, offset));
>> - u32 length = _dev->pci_readl(cfg_offset + offsetof(struct
>> virtio_pci_cap, length));
>> -
>> + u8 bar_index = _dev->pci_readb(cfg_offset +
>> offsetof(virtio_pci_cap, bar));
>> auto bar_no = bar_index + 1;
>> auto bar = _dev->get_bar(bar_no);
>> if (bar && bar->is_mmio() && !bar->is_mapped()) {
>> bar->map();
>> }
>>
>> + u64 offset = _dev->pci_readl(cfg_offset +
>> offsetof(virtio_pci_cap, offset));
>> + u64 length = _dev->pci_readl(cfg_offset +
>> offsetof(virtio_pci_cap, length));
>> + if (type == VIRTIO_PCI_CAP_SHARED_MEMORY_CFG) {
>> + // The shared memory region capability is defined by a
>> struct
>> + // virtio_pci_cap64
>> + u32 offset_hi = _dev->pci_readl(cfg_offset +
>> offsetof(virtio_pci_cap64, offset_hi));
>> + u32 length_hi = _dev->pci_readl(cfg_offset +
>> offsetof(virtio_pci_cap64, length_hi));
>> + offset |= ((u64)offset_hi << 32);
>> + length |= ((u64)length_hi << 32);
>> + }
>> +
>> ptr.reset(new
>> virtio_modern_pci_device::virtio_capability(cfg_offset, bar, bar_no,
>> offset, length));
>> }
>> }
>> diff --git a/drivers/virtio-pci-device.hh b/drivers/virtio-pci-device.hh
>> index 5a891d93..9a32aa18 100644
>> --- a/drivers/virtio-pci-device.hh
>> +++ b/drivers/virtio-pci-device.hh
>> @@ -98,7 +98,7 @@ public:
>> ~virtio_legacy_pci_device() {}
>>
>> virtual const char *get_version() { return "legacy"; }
>> - virtual u16 get_type_id() { return _dev->get_subsystem_id(); };
>> + virtual u16 get_type_id() { return _dev->get_subsystem_id(); }
>>
>> virtual void select_queue(int queue);
>> virtual u16 get_queue_size();
>> @@ -115,7 +115,7 @@ public:
>> virtual u8 read_config(u32 offset);
>> virtual u8 read_and_ack_isr();
>>
>> - virtual bool is_modern() { return false; };
>> + virtual bool is_modern() { return false; }
>> protected:
>> virtual bool parse_pci_config();
>>
>> @@ -145,6 +145,8 @@ enum VIRTIO_MODERN_PCI_CONFIG {
>> VIRTIO_PCI_CAP_DEVICE_CFG = 4,
>> /* PCI configuration access */
>> VIRTIO_PCI_CAP_PCI_CFG = 5,
>> + /* Shared memory region */
>> + VIRTIO_PCI_CAP_SHARED_MEMORY_CFG = 8,
>> };
>>
>> /* This is the PCI capability header: */
>> @@ -154,11 +156,20 @@ struct virtio_pci_cap {
>> u8 cap_len; /* Generic PCI field: capability length */
>> u8 cfg_type; /* Identifies the structure. */
>> u8 bar; /* Where to find it. */
>> - u8 padding[3]; /* Pad to full dword. */
>> + u8 id; /* Multiple capabilities of the same type */
>> + u8 padding[2]; /* Pad to full dword. */
>> u32 offset; /* Offset within bar. */
>> u32 length; /* Length of the structure, in bytes. */
>> };
>>
>> +/* A variant of virtio_pci_cap, for capabilities that require offsets or
>> lengths
>> + * larger than 4GiB */
>> +struct virtio_pci_cap64 {
>> + struct virtio_pci_cap cap;
>> + u32 offset_hi;
>> + u32 length_hi;
>> +};
>> +
>> /* The notification location is found using the
>> VIRTIO_PCI_CAP_NOTIFY_CFG capability.
>> * This capability is immediately followed by an additional field, like
>> so:*/
>> struct virtio_pci_notify_cap {
>> @@ -198,7 +209,7 @@ struct virtio_pci_common_cfg {
>> class virtio_modern_pci_device : public virtio_pci_device {
>> public:
>> struct virtio_capability {
>> - virtio_capability(u32 cfg_offset, pci::bar* bar, u32 bar_no, u32
>> bar_offset, u32 length) :
>> + virtio_capability(u32 cfg_offset, pci::bar* bar, u32 bar_no, u64
>> bar_offset, u64 length) :
>> _cfg_offset(cfg_offset),
>> _bar(bar),
>> _bar_no(bar_no),
>> @@ -207,27 +218,27 @@ public:
>> assert(_length > 0 && _bar_offset >= 0 && _bar_offset +
>> _length <= _bar->get_size());
>> }
>>
>> - u8 virtio_conf_readb(u32 offset) {
>> + u8 virtio_conf_readb(u64 offset) {
>> verify_offset(offset, sizeof(u8));
>> return _bar->readb(_bar_offset + offset);
>> };
>> - u16 virtio_conf_readw(u32 offset) {
>> + u16 virtio_conf_readw(u64 offset) {
>> verify_offset(offset, sizeof(u16));
>> return _bar->readw(_bar_offset + offset);
>> };
>> - u32 virtio_conf_readl(u32 offset) {
>> + u32 virtio_conf_readl(u64 offset) {
>> verify_offset(offset, sizeof(u32));
>> return _bar->readl(_bar_offset + offset);
>> };
>> - void virtio_conf_writeb(u32 offset, u8 val) {
>> + void virtio_conf_writeb(u64 offset, u8 val) {
>> verify_offset(offset, sizeof(u8));
>> _bar->writeb(_bar_offset + offset, val);
>> };
>> - void virtio_conf_writew(u32 offset, u16 val) {
>> + void virtio_conf_writew(u64 offset, u16 val) {
>> verify_offset(offset, sizeof(u16));
>> _bar->writew(_bar_offset + offset, val);
>> };
>> - void virtio_conf_writel(u32 offset, u32 val) {
>> + void virtio_conf_writel(u64 offset, u32 val) {
>> verify_offset(offset, sizeof(u32));
>> _bar->writel(_bar_offset + offset, val);
>> };
>> @@ -237,15 +248,15 @@ public:
>> virtio_d("%s bar=%d, offset=%x, size=%x", prefix, _bar_no,
>> _bar_offset, _length);
>> }
>> private:
>> - inline void verify_offset(u32 offset, u32 size) {
>> + inline void verify_offset(u64 offset, u32 size) {
>> assert(offset >= 0 && offset + size <= _length);
>> }
>>
>> u32 _cfg_offset;
>> pci::bar* _bar;
>> u32 _bar_no;
>> - u32 _bar_offset;
>> - u32 _length;
>> + u64 _bar_offset;
>> + u64 _length;
>> };
>>
>> explicit virtio_modern_pci_device(pci::device *dev);
>> @@ -276,12 +287,15 @@ public:
>> protected:
>> virtual bool parse_pci_config();
>> private:
>> - void parse_virtio_capability(std::unique_ptr<virtio_capability>
>> &ptr, u8 type);
>> + void
>> parse_virtio_capabilities(std::vector<std::unique_ptr<virtio_capability>>&
>> caps, u8 type);
>> + void parse_virtio_capability(std::unique_ptr<virtio_capability>
>> &ptr, u8 type,
>> + std::function<bool (u8)> id_pred);
>>
>> std::unique_ptr<virtio_capability> _common_cfg;
>> std::unique_ptr<virtio_capability> _isr_cfg;
>> std::unique_ptr<virtio_capability> _notify_cfg;
>> std::unique_ptr<virtio_capability> _device_cfg;
>> + std::vector<std::unique_ptr<virtio_capability>> _shm_cfgs;
>>
>> u32 _notify_offset_multiplier;
>> u32 _queues_notify_offsets[64];
>> @@ -293,4 +307,4 @@ virtio_device* create_virtio_pci_device(pci::device
>> *dev);
>>
>> }
>>
>> -#endif //VIRTIO_PCI_DEVICE_HH
>> \ No newline at end of file
>> +#endif //VIRTIO_PCI_DEVICE_HH
>> --
>> 2.25.1
>>
>>
--
You received this message because you are subscribed to the Google Groups "OSv
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/osv-dev/74665250-8c81-4859-b3f2-e714802df7c4%40googlegroups.com.