* Re: [PATCH v5 5/7] iommu: Add virtio-iommu driver
From: Michael S. Tsirkin @ 2018-11-27 18:10 UTC (permalink / raw)
To: Jean-Philippe Brucker
Cc: Mark Rutland, virtio-dev@lists.oasis-open.org, Lorenzo Pieralisi,
tnowicki@caviumnetworks.com, devicetree@vger.kernel.org,
Marc Zyngier, linux-pci@vger.kernel.org, joro@8bytes.org,
Will Deacon, virtualization@lists.linux-foundation.org,
eric.auger@redhat.com, iommu@lists.linux-foundation.org,
robh+dt@kernel.org, bhelgaas@google.com, Robin Murphy,
kvmarm@lists.cs.columbia.edu
In-Reply-To: <0fed79f3-ca2e-b539-74f5-cd2a8091f65d@arm.com>
On Tue, Nov 27, 2018 at 05:58:18PM +0000, Jean-Philippe Brucker wrote:
> On 23/11/2018 21:48, Michael S. Tsirkin wrote:
> >> +struct virtio_iommu_config {
> >> + /* Supported page sizes */
> >> + __u64 page_size_mask;
> >> + /* Supported IOVA range */
> >> + struct virtio_iommu_range input_range;
> >> + /* Max domain ID size */
> >> + __u8 domain_bits;
> >> + __u8 padding[3];
> >
> > Not enough padding here it seems. Structure is 8 byte
> > aligned on 64 bit systems.
>
> The next field (probe_size) is 4 bytes, so the alignment ends up fine.
> That field is introduced in patch 6, maybe I should move it here?
>
> Thanks,
> Jean
Sounds like a good idea.
--
MST
^ permalink raw reply
* Re: [PATCH v5 5/7] iommu: Add virtio-iommu driver
From: Michael S. Tsirkin @ 2018-11-27 18:10 UTC (permalink / raw)
To: Jean-Philippe Brucker
Cc: mark.rutland, virtio-dev, lorenzo.pieralisi, tnowicki, devicetree,
marc.zyngier, linux-pci, joro, will.deacon, virtualization,
eric.auger, iommu, robh+dt, bhelgaas, robin.murphy, kvmarm
In-Reply-To: <e76a51ba-7a0f-3fca-a1ae-640131f9dfe1@arm.com>
On Tue, Nov 27, 2018 at 05:55:20PM +0000, Jean-Philippe Brucker wrote:
> On 23/11/2018 21:56, Michael S. Tsirkin wrote:
> >> +config VIRTIO_IOMMU
> >> + bool "Virtio IOMMU driver"
> >> + depends on VIRTIO=y
> >> + select IOMMU_API
> >> + select INTERVAL_TREE
> >> + select ARM_DMA_USE_IOMMU if ARM
> >> + help
> >> + Para-virtualised IOMMU driver with virtio.
> >> +
> >> + Say Y here if you intend to run this kernel as a guest.
> >> +
> >
> > Given it is arm specific right now, shouldn't this depend on ARM?
> > E.g. there's a hack for x86 right now.
>
> Sure, I'll make it depend on ARM64 for now
>
> [..]
> >> +static int viommu_probe(struct virtio_device *vdev)
> >> +{
> >> + struct device *parent_dev = vdev->dev.parent;
> >> + struct viommu_dev *viommu = NULL;
> >> + struct device *dev = &vdev->dev;
> >> + u64 input_start = 0;
> >> + u64 input_end = -1UL;
> >> + int ret;
> >> +
> >> + if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1) ||
> >> + !virtio_has_feature(vdev, VIRTIO_IOMMU_F_MAP_UNMAP))
> >
> > Why bother with a feature bit for this then btw?
>
> We'll need a new feature bit for sharing page tables with the hardware,
> because they require different requests (attach_table/invalidate instead
> of map/unmap.) A future device supporting page table sharing won't
> necessarily need to support map/unmap.
>
> Thanks,
> Jean
I don't see virtio iommu being extended to support ARM specific
requests. This just won't scale, too many different
descriptor formats out there.
If you want to go that way down the road, you should avoid
virtio iommu, instead emulate and share code with the ARM SMMU (probably
with a different vendor id so you can implement the
report on map for devices without PRI).
Others on the TC might feel differently.
If someone's looking into adding virtio iommu support in hardware,
that's a different matter. Which is it?
--
MST
^ permalink raw reply
* Re: [PATCH v5 5/7] iommu: Add virtio-iommu driver
From: Michael S. Tsirkin @ 2018-11-27 18:04 UTC (permalink / raw)
To: Jean-Philippe Brucker
Cc: mark.rutland, virtio-dev, lorenzo.pieralisi, tnowicki, devicetree,
marc.zyngier, linux-pci, joro, will.deacon, virtualization,
eric.auger, iommu, robh+dt, bhelgaas, robin.murphy, kvmarm
In-Reply-To: <fd221210-7e12-1d15-0c80-52a761f6893d@arm.com>
On Tue, Nov 27, 2018 at 05:50:50PM +0000, Jean-Philippe Brucker wrote:
> On 23/11/2018 22:02, Michael S. Tsirkin wrote:
> >> +/*
> >> + * __viommu_sync_req - Complete all in-flight requests
> >> + *
> >> + * Wait for all added requests to complete. When this function returns, all
> >> + * requests that were in-flight at the time of the call have completed.
> >> + */
> >> +static int __viommu_sync_req(struct viommu_dev *viommu)
> >> +{
> >> + int ret = 0;
> >> + unsigned int len;
> >> + size_t write_len;
> >> + struct viommu_request *req;
> >> + struct virtqueue *vq = viommu->vqs[VIOMMU_REQUEST_VQ];
> >> +
> >> + assert_spin_locked(&viommu->request_lock);
> >> +
> >> + virtqueue_kick(vq);
> >> +
> >> + while (!list_empty(&viommu->requests)) {
> >> + len = 0;
> >> + req = virtqueue_get_buf(vq, &len);
> >> + if (!req)
> >> + continue;
> >> +
> >> + if (!len)
> >> + viommu_set_req_status(req->buf, req->len,
> >> + VIRTIO_IOMMU_S_IOERR);
> >> +
> >> + write_len = req->len - req->write_offset;
> >> + if (req->writeback && len == write_len)
> >> + memcpy(req->writeback, req->buf + req->write_offset,
> >> + write_len);
> >> +
> >> + list_del(&req->list);
> >> + kfree(req);
> >> + }
> >
> > I didn't notice this in the past but it seems this will spin
> > with interrupts disabled until host handles the request.
> > Please do not do this - host execution can be another
> > task that needs the same host CPU. This will then disable
> > interrupts for a very very long time.
>
> In the guest yes, but that doesn't prevent the host from running another
> task right?
Doesn't prevent it but it will delay it significantly
until scheduler decides to kick the VCPU task out.
> My tests run fine when QEMU is bound to a single CPU, even
> though vcpu and viommu run in different threads
>
> > What to do then? Queue in software and wake up task.
>
> Unfortunately I can't do anything here, because IOMMU drivers can't
> sleep in the iommu_map() or iommu_unmap() path.
>
> The problem is the same
> for all IOMMU drivers. That's because the DMA API allows drivers to call
> some functions with interrupts disabled. For example
> Documentation/DMA-API-HOWTO.txt allows dma_alloc_coherent() and
> dma_unmap_single() to be called in interrupt context.
In fact I don't really understand how it's supposed to
work at all: you only sync when ring is full.
So host may not have seen your map request if ring
is not full.
Why is it safe to use the address with a device then?
> > As kick is vm exit, kick under interrupts disabled is discouraged too:
> > better to prepare for kick enable interrupts then kick.
>
> That was on my list of things to look at, because it could relax
> things for device drivers that don't call us with interrupts disabled. I
> just tried it and I can see some performance improvement (7% and 4% on
> tcp_stream and tcp_maerts respectively, +/-2.5%).
>
> Since it's an optimization I'll leave it for later (ACPI and module
> support is higher on my list). The resulting change is complicated
> because we now need to deal with threads adding new requests while
> sync() is running. With my current prototype one thread could end up
> staying in sync() while other threads add new async requests, so I need
> to find a way to bound it.
>
> Thanks,
> Jean
^ permalink raw reply
* Re: [PATCH v5 0/7] Add virtio-iommu driver
From: Auger Eric @ 2018-11-27 18:02 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: mark.rutland, virtio-dev, lorenzo.pieralisi, tnowicki, devicetree,
Jean-Philippe Brucker, linux-pci, joro, will.deacon, robin.murphy,
virtualization, marc.zyngier, iommu, robh+dt, kvmarm, bhelgaas
In-Reply-To: <20181127121036-mutt-send-email-mst@kernel.org>
Hi Michael,
On 11/27/18 6:16 PM, Michael S. Tsirkin wrote:
> On Tue, Nov 27, 2018 at 06:09:25PM +0100, Auger Eric wrote:
>> Hi Michael,
>>
>> On 11/27/18 5:53 PM, Michael S. Tsirkin wrote:
>>> On Thu, Nov 22, 2018 at 07:37:54PM +0000, Jean-Philippe Brucker wrote:
>>>> Implement the virtio-iommu driver, following specification v0.9 [1].
>>>>
>>>> Since v4 [2] I fixed the issues reported by Eric, and added Reviewed-by
>>>> from Eric and Rob. Thanks!
>>>>
>>>> I changed the specification to fix one inconsistency discussed in v4.
>>>> That the device fills the probe buffer with zeroes is now a "SHOULD"
>>>> instead of a "MAY", since it's the only way for the driver to know if
>>>> the device wrote the status. Existing devices already do this. In
>>>> addition the device now needs to fill the three padding bytes at the
>>>> tail with zeroes.
>>>>
>>>> You can find Linux driver and kvmtool device on branches
>>>> virtio-iommu/v0.9 [3]. I also lightly tested with Eric's latest QEMU
>>>> device [4].
>>>
>>> I tried to get this to work on my x86 box but without
>>> success. Any hints? Does this have to do with the IORT table?
>>> I think we really should just reserve our own table ID
>>> and avoid the pain of trying to add things to the IORT spec.
>>> I'm reluctant to merge lots of code that I can't easily test.
>>> Again, if we found a way to push more configuration into
>>> virtio config space the problem space would be smaller.
>>
>> You can at least test it with QEMU ARM virt in TCG mode.
>
> It's slow enough that I generally just focus on KVM.
fair enough
>
>> Then I have
>> worked on the IORT integration in PC/Q35 but this is not yet functional.
>> I need to debug what's wrong on guest ACPI probing. I plan to work on
>> this this week.
>>
>> Thanks
>>
>> Eric
>
> Sounds good. Did you need to make changes to IORT? I don't remember. If
> yes it would be very easy to just have a virtio specific ACPI table -
> I am assuming ARM guys will be just as hostile to virt changes
> to IORT as they were to minor changes to ARM vIOMMU.
Well the only difference is that on ARM we have 3 nodes in the IORT: the
root complex node -> the VIRTIO-IOMMU node -> the ITS node. The ITS is
the ARM MSI controller which does MSI translation. So the ARM IORT
describes the chained ID mappings from the end point RID through the
StreamID SMMU input to the device ID MSI controller input. On Intel we
don't have the last node.
But again my integration is not yet functional and I don't know yet if I
have captured the whole picture.
Thanks
Eric
>
>
>>>
>>>> [1] Virtio-iommu specification v0.9, sources, pdf and diff from v0.8
>>>> git://linux-arm.org/virtio-iommu.git virtio-iommu/v0.9
>>>> http://jpbrucker.net/virtio-iommu/spec/v0.9/virtio-iommu-v0.9.pdf
>>>> http://jpbrucker.net/virtio-iommu/spec/diffs/virtio-iommu-pdf-diff-v0.8-v0.9.pdf
>>>>
>>>> [2] [PATCH v4 0/7] Add virtio-iommu driver
>>>> https://lists.linuxfoundation.org/pipermail/iommu/2018-November/031074.html
>>>>
>>>> [3] git://linux-arm.org/linux-jpb.git virtio-iommu/v0.9
>>>> git://linux-arm.org/kvmtool-jpb.git virtio-iommu/v0.9
>>>>
>>>> [4] [RFC v9 00/17] VIRTIO-IOMMU device
>>>> https://www.mail-archive.com/qemu-devel@nongnu.org/msg575578.html
>>>>
>>>> Jean-Philippe Brucker (7):
>>>> dt-bindings: virtio-mmio: Add IOMMU description
>>>> dt-bindings: virtio: Add virtio-pci-iommu node
>>>> of: Allow the iommu-map property to omit untranslated devices
>>>> PCI: OF: Initialize dev->fwnode appropriately
>>>> iommu: Add virtio-iommu driver
>>>> iommu/virtio: Add probe request
>>>> iommu/virtio: Add event queue
>>>>
>>>> .../devicetree/bindings/virtio/iommu.txt | 66 +
>>>> .../devicetree/bindings/virtio/mmio.txt | 30 +
>>>> MAINTAINERS | 7 +
>>>> drivers/iommu/Kconfig | 11 +
>>>> drivers/iommu/Makefile | 1 +
>>>> drivers/iommu/virtio-iommu.c | 1157 +++++++++++++++++
>>>> drivers/of/base.c | 10 +-
>>>> drivers/pci/of.c | 7 +
>>>> include/uapi/linux/virtio_ids.h | 1 +
>>>> include/uapi/linux/virtio_iommu.h | 161 +++
>>>> 10 files changed, 1448 insertions(+), 3 deletions(-)
>>>> create mode 100644 Documentation/devicetree/bindings/virtio/iommu.txt
>>>> create mode 100644 drivers/iommu/virtio-iommu.c
>>>> create mode 100644 include/uapi/linux/virtio_iommu.h
>>>>
>>>> --
>>>> 2.19.1
^ permalink raw reply
* Re: [PATCH v5 5/7] iommu: Add virtio-iommu driver
From: Jean-Philippe Brucker @ 2018-11-27 17:58 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: Mark Rutland, virtio-dev@lists.oasis-open.org, Lorenzo Pieralisi,
tnowicki@caviumnetworks.com, devicetree@vger.kernel.org,
Marc Zyngier, linux-pci@vger.kernel.org, joro@8bytes.org,
Will Deacon, virtualization@lists.linux-foundation.org,
eric.auger@redhat.com, iommu@lists.linux-foundation.org,
robh+dt@kernel.org, bhelgaas@google.com, Robin Murphy,
kvmarm@lists.cs.columbia.edu
In-Reply-To: <20181123164716-mutt-send-email-mst@kernel.org>
On 23/11/2018 21:48, Michael S. Tsirkin wrote:
>> +struct virtio_iommu_config {
>> + /* Supported page sizes */
>> + __u64 page_size_mask;
>> + /* Supported IOVA range */
>> + struct virtio_iommu_range input_range;
>> + /* Max domain ID size */
>> + __u8 domain_bits;
>> + __u8 padding[3];
>
> Not enough padding here it seems. Structure is 8 byte
> aligned on 64 bit systems.
The next field (probe_size) is 4 bytes, so the alignment ends up fine.
That field is introduced in patch 6, maybe I should move it here?
Thanks,
Jean
^ permalink raw reply
* Re: [PATCH v5 5/7] iommu: Add virtio-iommu driver
From: Jean-Philippe Brucker @ 2018-11-27 17:55 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: mark.rutland, virtio-dev, lorenzo.pieralisi, tnowicki, devicetree,
marc.zyngier, linux-pci, joro, will.deacon, virtualization,
eric.auger, iommu, robh+dt, bhelgaas, robin.murphy, kvmarm
In-Reply-To: <20181123165206-mutt-send-email-mst@kernel.org>
On 23/11/2018 21:56, Michael S. Tsirkin wrote:
>> +config VIRTIO_IOMMU
>> + bool "Virtio IOMMU driver"
>> + depends on VIRTIO=y
>> + select IOMMU_API
>> + select INTERVAL_TREE
>> + select ARM_DMA_USE_IOMMU if ARM
>> + help
>> + Para-virtualised IOMMU driver with virtio.
>> +
>> + Say Y here if you intend to run this kernel as a guest.
>> +
>
> Given it is arm specific right now, shouldn't this depend on ARM?
> E.g. there's a hack for x86 right now.
Sure, I'll make it depend on ARM64 for now
[..]
>> +static int viommu_probe(struct virtio_device *vdev)
>> +{
>> + struct device *parent_dev = vdev->dev.parent;
>> + struct viommu_dev *viommu = NULL;
>> + struct device *dev = &vdev->dev;
>> + u64 input_start = 0;
>> + u64 input_end = -1UL;
>> + int ret;
>> +
>> + if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1) ||
>> + !virtio_has_feature(vdev, VIRTIO_IOMMU_F_MAP_UNMAP))
>
> Why bother with a feature bit for this then btw?
We'll need a new feature bit for sharing page tables with the hardware,
because they require different requests (attach_table/invalidate instead
of map/unmap.) A future device supporting page table sharing won't
necessarily need to support map/unmap.
Thanks,
Jean
^ permalink raw reply
* Re: [PATCH v5 5/7] iommu: Add virtio-iommu driver
From: Jean-Philippe Brucker @ 2018-11-27 17:50 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: mark.rutland, virtio-dev, lorenzo.pieralisi, tnowicki, devicetree,
marc.zyngier, linux-pci, joro, will.deacon, virtualization,
eric.auger, iommu, robh+dt, bhelgaas, robin.murphy, kvmarm
In-Reply-To: <20181123165742-mutt-send-email-mst@kernel.org>
On 23/11/2018 22:02, Michael S. Tsirkin wrote:
>> +/*
>> + * __viommu_sync_req - Complete all in-flight requests
>> + *
>> + * Wait for all added requests to complete. When this function returns, all
>> + * requests that were in-flight at the time of the call have completed.
>> + */
>> +static int __viommu_sync_req(struct viommu_dev *viommu)
>> +{
>> + int ret = 0;
>> + unsigned int len;
>> + size_t write_len;
>> + struct viommu_request *req;
>> + struct virtqueue *vq = viommu->vqs[VIOMMU_REQUEST_VQ];
>> +
>> + assert_spin_locked(&viommu->request_lock);
>> +
>> + virtqueue_kick(vq);
>> +
>> + while (!list_empty(&viommu->requests)) {
>> + len = 0;
>> + req = virtqueue_get_buf(vq, &len);
>> + if (!req)
>> + continue;
>> +
>> + if (!len)
>> + viommu_set_req_status(req->buf, req->len,
>> + VIRTIO_IOMMU_S_IOERR);
>> +
>> + write_len = req->len - req->write_offset;
>> + if (req->writeback && len == write_len)
>> + memcpy(req->writeback, req->buf + req->write_offset,
>> + write_len);
>> +
>> + list_del(&req->list);
>> + kfree(req);
>> + }
>
> I didn't notice this in the past but it seems this will spin
> with interrupts disabled until host handles the request.
> Please do not do this - host execution can be another
> task that needs the same host CPU. This will then disable
> interrupts for a very very long time.
In the guest yes, but that doesn't prevent the host from running another
task right? My tests run fine when QEMU is bound to a single CPU, even
though vcpu and viommu run in different threads
> What to do then? Queue in software and wake up task.
Unfortunately I can't do anything here, because IOMMU drivers can't
sleep in the iommu_map() or iommu_unmap() path. The problem is the same
for all IOMMU drivers. That's because the DMA API allows drivers to call
some functions with interrupts disabled. For example
Documentation/DMA-API-HOWTO.txt allows dma_alloc_coherent() and
dma_unmap_single() to be called in interrupt context.
> As kick is vm exit, kick under interrupts disabled is discouraged too:
> better to prepare for kick enable interrupts then kick.
That was on my list of things to look at, because it could relax
things for device drivers that don't call us with interrupts disabled. I
just tried it and I can see some performance improvement (7% and 4% on
tcp_stream and tcp_maerts respectively, +/-2.5%).
Since it's an optimization I'll leave it for later (ACPI and module
support is higher on my list). The resulting change is complicated
because we now need to deal with threads adding new requests while
sync() is running. With my current prototype one thread could end up
staying in sync() while other threads add new async requests, so I need
to find a way to bound it.
Thanks,
Jean
^ permalink raw reply
* Re: [PATCH v5 0/7] Add virtio-iommu driver
From: Michael S. Tsirkin @ 2018-11-27 17:16 UTC (permalink / raw)
To: Auger Eric
Cc: mark.rutland, virtio-dev, lorenzo.pieralisi, tnowicki, devicetree,
Jean-Philippe Brucker, linux-pci, joro, will.deacon, robin.murphy,
virtualization, marc.zyngier, iommu, robh+dt, kvmarm, bhelgaas
In-Reply-To: <6c061729-c404-ac25-f86f-7fe222bf5bc7@redhat.com>
On Tue, Nov 27, 2018 at 06:09:25PM +0100, Auger Eric wrote:
> Hi Michael,
>
> On 11/27/18 5:53 PM, Michael S. Tsirkin wrote:
> > On Thu, Nov 22, 2018 at 07:37:54PM +0000, Jean-Philippe Brucker wrote:
> >> Implement the virtio-iommu driver, following specification v0.9 [1].
> >>
> >> Since v4 [2] I fixed the issues reported by Eric, and added Reviewed-by
> >> from Eric and Rob. Thanks!
> >>
> >> I changed the specification to fix one inconsistency discussed in v4.
> >> That the device fills the probe buffer with zeroes is now a "SHOULD"
> >> instead of a "MAY", since it's the only way for the driver to know if
> >> the device wrote the status. Existing devices already do this. In
> >> addition the device now needs to fill the three padding bytes at the
> >> tail with zeroes.
> >>
> >> You can find Linux driver and kvmtool device on branches
> >> virtio-iommu/v0.9 [3]. I also lightly tested with Eric's latest QEMU
> >> device [4].
> >
> > I tried to get this to work on my x86 box but without
> > success. Any hints? Does this have to do with the IORT table?
> > I think we really should just reserve our own table ID
> > and avoid the pain of trying to add things to the IORT spec.
> > I'm reluctant to merge lots of code that I can't easily test.
> > Again, if we found a way to push more configuration into
> > virtio config space the problem space would be smaller.
>
> You can at least test it with QEMU ARM virt in TCG mode.
It's slow enough that I generally just focus on KVM.
> Then I have
> worked on the IORT integration in PC/Q35 but this is not yet functional.
> I need to debug what's wrong on guest ACPI probing. I plan to work on
> this this week.
>
> Thanks
>
> Eric
Sounds good. Did you need to make changes to IORT? I don't remember. If
yes it would be very easy to just have a virtio specific ACPI table -
I am assuming ARM guys will be just as hostile to virt changes
to IORT as they were to minor changes to ARM vIOMMU.
> >
> >> [1] Virtio-iommu specification v0.9, sources, pdf and diff from v0.8
> >> git://linux-arm.org/virtio-iommu.git virtio-iommu/v0.9
> >> http://jpbrucker.net/virtio-iommu/spec/v0.9/virtio-iommu-v0.9.pdf
> >> http://jpbrucker.net/virtio-iommu/spec/diffs/virtio-iommu-pdf-diff-v0.8-v0.9.pdf
> >>
> >> [2] [PATCH v4 0/7] Add virtio-iommu driver
> >> https://lists.linuxfoundation.org/pipermail/iommu/2018-November/031074.html
> >>
> >> [3] git://linux-arm.org/linux-jpb.git virtio-iommu/v0.9
> >> git://linux-arm.org/kvmtool-jpb.git virtio-iommu/v0.9
> >>
> >> [4] [RFC v9 00/17] VIRTIO-IOMMU device
> >> https://www.mail-archive.com/qemu-devel@nongnu.org/msg575578.html
> >>
> >> Jean-Philippe Brucker (7):
> >> dt-bindings: virtio-mmio: Add IOMMU description
> >> dt-bindings: virtio: Add virtio-pci-iommu node
> >> of: Allow the iommu-map property to omit untranslated devices
> >> PCI: OF: Initialize dev->fwnode appropriately
> >> iommu: Add virtio-iommu driver
> >> iommu/virtio: Add probe request
> >> iommu/virtio: Add event queue
> >>
> >> .../devicetree/bindings/virtio/iommu.txt | 66 +
> >> .../devicetree/bindings/virtio/mmio.txt | 30 +
> >> MAINTAINERS | 7 +
> >> drivers/iommu/Kconfig | 11 +
> >> drivers/iommu/Makefile | 1 +
> >> drivers/iommu/virtio-iommu.c | 1157 +++++++++++++++++
> >> drivers/of/base.c | 10 +-
> >> drivers/pci/of.c | 7 +
> >> include/uapi/linux/virtio_ids.h | 1 +
> >> include/uapi/linux/virtio_iommu.h | 161 +++
> >> 10 files changed, 1448 insertions(+), 3 deletions(-)
> >> create mode 100644 Documentation/devicetree/bindings/virtio/iommu.txt
> >> create mode 100644 drivers/iommu/virtio-iommu.c
> >> create mode 100644 include/uapi/linux/virtio_iommu.h
> >>
> >> --
> >> 2.19.1
^ permalink raw reply
* Re: [PATCH v5 0/7] Add virtio-iommu driver
From: Auger Eric @ 2018-11-27 17:09 UTC (permalink / raw)
To: Michael S. Tsirkin, Jean-Philippe Brucker
Cc: mark.rutland, virtio-dev, lorenzo.pieralisi, tnowicki, devicetree,
marc.zyngier, linux-pci, joro, will.deacon, robin.murphy,
virtualization, iommu, robh+dt, kvmarm, bhelgaas
In-Reply-To: <20181127114402-mutt-send-email-mst@kernel.org>
Hi Michael,
On 11/27/18 5:53 PM, Michael S. Tsirkin wrote:
> On Thu, Nov 22, 2018 at 07:37:54PM +0000, Jean-Philippe Brucker wrote:
>> Implement the virtio-iommu driver, following specification v0.9 [1].
>>
>> Since v4 [2] I fixed the issues reported by Eric, and added Reviewed-by
>> from Eric and Rob. Thanks!
>>
>> I changed the specification to fix one inconsistency discussed in v4.
>> That the device fills the probe buffer with zeroes is now a "SHOULD"
>> instead of a "MAY", since it's the only way for the driver to know if
>> the device wrote the status. Existing devices already do this. In
>> addition the device now needs to fill the three padding bytes at the
>> tail with zeroes.
>>
>> You can find Linux driver and kvmtool device on branches
>> virtio-iommu/v0.9 [3]. I also lightly tested with Eric's latest QEMU
>> device [4].
>
> I tried to get this to work on my x86 box but without
> success. Any hints? Does this have to do with the IORT table?
> I think we really should just reserve our own table ID
> and avoid the pain of trying to add things to the IORT spec.
> I'm reluctant to merge lots of code that I can't easily test.
> Again, if we found a way to push more configuration into
> virtio config space the problem space would be smaller.
You can at least test it with QEMU ARM virt in TCG mode. Then I have
worked on the IORT integration in PC/Q35 but this is not yet functional.
I need to debug what's wrong on guest ACPI probing. I plan to work on
this this week.
Thanks
Eric
>
>> [1] Virtio-iommu specification v0.9, sources, pdf and diff from v0.8
>> git://linux-arm.org/virtio-iommu.git virtio-iommu/v0.9
>> http://jpbrucker.net/virtio-iommu/spec/v0.9/virtio-iommu-v0.9.pdf
>> http://jpbrucker.net/virtio-iommu/spec/diffs/virtio-iommu-pdf-diff-v0.8-v0.9.pdf
>>
>> [2] [PATCH v4 0/7] Add virtio-iommu driver
>> https://lists.linuxfoundation.org/pipermail/iommu/2018-November/031074.html
>>
>> [3] git://linux-arm.org/linux-jpb.git virtio-iommu/v0.9
>> git://linux-arm.org/kvmtool-jpb.git virtio-iommu/v0.9
>>
>> [4] [RFC v9 00/17] VIRTIO-IOMMU device
>> https://www.mail-archive.com/qemu-devel@nongnu.org/msg575578.html
>>
>> Jean-Philippe Brucker (7):
>> dt-bindings: virtio-mmio: Add IOMMU description
>> dt-bindings: virtio: Add virtio-pci-iommu node
>> of: Allow the iommu-map property to omit untranslated devices
>> PCI: OF: Initialize dev->fwnode appropriately
>> iommu: Add virtio-iommu driver
>> iommu/virtio: Add probe request
>> iommu/virtio: Add event queue
>>
>> .../devicetree/bindings/virtio/iommu.txt | 66 +
>> .../devicetree/bindings/virtio/mmio.txt | 30 +
>> MAINTAINERS | 7 +
>> drivers/iommu/Kconfig | 11 +
>> drivers/iommu/Makefile | 1 +
>> drivers/iommu/virtio-iommu.c | 1157 +++++++++++++++++
>> drivers/of/base.c | 10 +-
>> drivers/pci/of.c | 7 +
>> include/uapi/linux/virtio_ids.h | 1 +
>> include/uapi/linux/virtio_iommu.h | 161 +++
>> 10 files changed, 1448 insertions(+), 3 deletions(-)
>> create mode 100644 Documentation/devicetree/bindings/virtio/iommu.txt
>> create mode 100644 drivers/iommu/virtio-iommu.c
>> create mode 100644 include/uapi/linux/virtio_iommu.h
>>
>> --
>> 2.19.1
^ permalink raw reply
* Re: [PATCH v5 0/7] Add virtio-iommu driver
From: Michael S. Tsirkin @ 2018-11-27 16:53 UTC (permalink / raw)
To: Jean-Philippe Brucker
Cc: mark.rutland, virtio-dev, lorenzo.pieralisi, tnowicki, devicetree,
marc.zyngier, linux-pci, joro, will.deacon, robin.murphy,
virtualization, eric.auger, iommu, robh+dt, kvmarm, bhelgaas
In-Reply-To: <20181122193801.50510-1-jean-philippe.brucker@arm.com>
On Thu, Nov 22, 2018 at 07:37:54PM +0000, Jean-Philippe Brucker wrote:
> Implement the virtio-iommu driver, following specification v0.9 [1].
>
> Since v4 [2] I fixed the issues reported by Eric, and added Reviewed-by
> from Eric and Rob. Thanks!
>
> I changed the specification to fix one inconsistency discussed in v4.
> That the device fills the probe buffer with zeroes is now a "SHOULD"
> instead of a "MAY", since it's the only way for the driver to know if
> the device wrote the status. Existing devices already do this. In
> addition the device now needs to fill the three padding bytes at the
> tail with zeroes.
>
> You can find Linux driver and kvmtool device on branches
> virtio-iommu/v0.9 [3]. I also lightly tested with Eric's latest QEMU
> device [4].
I tried to get this to work on my x86 box but without
success. Any hints? Does this have to do with the IORT table?
I think we really should just reserve our own table ID
and avoid the pain of trying to add things to the IORT spec.
I'm reluctant to merge lots of code that I can't easily test.
Again, if we found a way to push more configuration into
virtio config space the problem space would be smaller.
> [1] Virtio-iommu specification v0.9, sources, pdf and diff from v0.8
> git://linux-arm.org/virtio-iommu.git virtio-iommu/v0.9
> http://jpbrucker.net/virtio-iommu/spec/v0.9/virtio-iommu-v0.9.pdf
> http://jpbrucker.net/virtio-iommu/spec/diffs/virtio-iommu-pdf-diff-v0.8-v0.9.pdf
>
> [2] [PATCH v4 0/7] Add virtio-iommu driver
> https://lists.linuxfoundation.org/pipermail/iommu/2018-November/031074.html
>
> [3] git://linux-arm.org/linux-jpb.git virtio-iommu/v0.9
> git://linux-arm.org/kvmtool-jpb.git virtio-iommu/v0.9
>
> [4] [RFC v9 00/17] VIRTIO-IOMMU device
> https://www.mail-archive.com/qemu-devel@nongnu.org/msg575578.html
>
> Jean-Philippe Brucker (7):
> dt-bindings: virtio-mmio: Add IOMMU description
> dt-bindings: virtio: Add virtio-pci-iommu node
> of: Allow the iommu-map property to omit untranslated devices
> PCI: OF: Initialize dev->fwnode appropriately
> iommu: Add virtio-iommu driver
> iommu/virtio: Add probe request
> iommu/virtio: Add event queue
>
> .../devicetree/bindings/virtio/iommu.txt | 66 +
> .../devicetree/bindings/virtio/mmio.txt | 30 +
> MAINTAINERS | 7 +
> drivers/iommu/Kconfig | 11 +
> drivers/iommu/Makefile | 1 +
> drivers/iommu/virtio-iommu.c | 1157 +++++++++++++++++
> drivers/of/base.c | 10 +-
> drivers/pci/of.c | 7 +
> include/uapi/linux/virtio_ids.h | 1 +
> include/uapi/linux/virtio_iommu.h | 161 +++
> 10 files changed, 1448 insertions(+), 3 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/virtio/iommu.txt
> create mode 100644 drivers/iommu/virtio-iommu.c
> create mode 100644 include/uapi/linux/virtio_iommu.h
>
> --
> 2.19.1
^ permalink raw reply
* RE: [PATCH v5 0/7] Add virtio-iommu driver
From: Bharat Bhushan @ 2018-11-27 7:09 UTC (permalink / raw)
To: Auger Eric, Jean-Philippe Brucker,
iommu@lists.linux-foundation.org, linux-pci@vger.kernel.org,
devicetree@vger.kernel.org,
virtualization@lists.linux-foundation.org,
virtio-dev@lists.oasis-open.org, joro@8bytes.org, mst@redhat.com
Cc: mark.rutland@arm.com, lorenzo.pieralisi@arm.com,
tnowicki@caviumnetworks.com, marc.zyngier@arm.com,
robin.murphy@arm.com, will.deacon@arm.com, robh+dt@kernel.org,
bhelgaas@google.com, kvmarm@lists.cs.columbia.edu
In-Reply-To: <250a38a9-e6b1-35ae-9261-eb8aab123f43@redhat.com>
Hi Jean,
> -----Original Message-----
> From: Auger Eric <eric.auger@redhat.com>
> Sent: Friday, November 23, 2018 1:59 PM
> To: Jean-Philippe Brucker <jean-philippe.brucker@arm.com>;
> iommu@lists.linux-foundation.org; linux-pci@vger.kernel.org;
> devicetree@vger.kernel.org; virtualization@lists.linux-foundation.org; virtio-
> dev@lists.oasis-open.org; joro@8bytes.org; mst@redhat.com
> Cc: jasowang@redhat.com; robh+dt@kernel.org; mark.rutland@arm.com;
> bhelgaas@google.com; frowand.list@gmail.com;
> kvmarm@lists.cs.columbia.edu; tnowicki@caviumnetworks.com;
> kevin.tian@intel.com; marc.zyngier@arm.com; robin.murphy@arm.com;
> will.deacon@arm.com; lorenzo.pieralisi@arm.com; Bharat Bhushan
> <bharat.bhushan@nxp.com>
> Subject: Re: [PATCH v5 0/7] Add virtio-iommu driver
>
> Hi Jean,
>
> On 11/22/18 8:37 PM, Jean-Philippe Brucker wrote:
> > Implement the virtio-iommu driver, following specification v0.9 [1].
> >
> > Since v4 [2] I fixed the issues reported by Eric, and added
> > Reviewed-by from Eric and Rob. Thanks!
> >
> > I changed the specification to fix one inconsistency discussed in v4.
> > That the device fills the probe buffer with zeroes is now a "SHOULD"
> > instead of a "MAY", since it's the only way for the driver to know if
> > the device wrote the status. Existing devices already do this. In
> > addition the device now needs to fill the three padding bytes at the
> > tail with zeroes.
> >
> > You can find Linux driver and kvmtool device on branches
> > virtio-iommu/v0.9 [3]. I also lightly tested with Eric's latest QEMU
> > device [4].
> >
> > [1] Virtio-iommu specification v0.9, sources, pdf and diff from v0.8
> > git://linux-arm.org/virtio-iommu.git virtio-iommu/v0.9
> >
> https://emea01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fjpbr
> ucker.net%2Fvirtio-iommu%2Fspec%2Fv0.9%2Fvirtio-iommu-
> v0.9.pdf&data=02%7C01%7Cbharat.bhushan%40nxp.com%7C6e7180e7
> df8e41943d4108d6511db8ed%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0%
> 7C1%7C636785585424990803&sdata=la0tSTLcOI5HkQ65a%2BCHKeI3H5iu
> qZ%2F8r6Q5YF8tfsU%3D&reserved=0
> >
> >
> https://emea01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fjpbr
> > ucker.net%2Fvirtio-iommu%2Fspec%2Fdiffs%2Fvirtio-iommu-pdf-diff-v0.8-
> v
> >
> 0.9.pdf&data=02%7C01%7Cbharat.bhushan%40nxp.com%7C6e7180e7d
> f8e4194
> >
> 3d4108d6511db8ed%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0%7C1%7C6
> 3678558
> >
> 5424990803&sdata=AEXEib4lihcpfE6O6wLf%2BMElPtA7ZLGYE2mj0288PZ
> k%3D&
> > amp;reserved=0
> >
> > [2] [PATCH v4 0/7] Add virtio-iommu driver
> >
> >
> https://emea01.safelinks.protection.outlook.com/?url=https%3A%2F%2Flis
> > ts.linuxfoundation.org%2Fpipermail%2Fiommu%2F2018-
> November%2F031074.ht
> >
> ml&data=02%7C01%7Cbharat.bhushan%40nxp.com%7C6e7180e7df8e4
> 1943d410
> >
> 8d6511db8ed%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0%7C1%7C636785
> 5854249
> >
> 90803&sdata=mUUSBQ%2FjEeRGaisGBK20G9WmfXPwlERKDaeeRqHW4
> 08%3D&r
> > eserved=0
> >
> > [3] git://linux-arm.org/linux-jpb.git virtio-iommu/v0.9
> > git://linux-arm.org/kvmtool-jpb.git virtio-iommu/v0.9
> >
> > [4] [RFC v9 00/17] VIRTIO-IOMMU device
> >
> >
> https://emea01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fw
> ww
> > .mail-archive.com%2Fqemu-
> devel%40nongnu.org%2Fmsg575578.html&data=
> >
> 02%7C01%7Cbharat.bhushan%40nxp.com%7C6e7180e7df8e41943d4108d651
> 1db8ed%
> >
> 7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0%7C1%7C636785585424990803&
> amp;sd
> >
> ata=fo9WKE33Nm%2FdW2C2XcSVmv9itWjEyRN1irgEZgOWtZI%3D&rese
> rved=0
> >
> > Jean-Philippe Brucker (7):
> > dt-bindings: virtio-mmio: Add IOMMU description
> > dt-bindings: virtio: Add virtio-pci-iommu node
> > of: Allow the iommu-map property to omit untranslated devices
> > PCI: OF: Initialize dev->fwnode appropriately
> > iommu: Add virtio-iommu driver
> > iommu/virtio: Add probe request
> > iommu/virtio: Add event queue
> >
> > .../devicetree/bindings/virtio/iommu.txt | 66 +
> > .../devicetree/bindings/virtio/mmio.txt | 30 +
> > MAINTAINERS | 7 +
> > drivers/iommu/Kconfig | 11 +
> > drivers/iommu/Makefile | 1 +
> > drivers/iommu/virtio-iommu.c | 1157 +++++++++++++++++
> > drivers/of/base.c | 10 +-
> > drivers/pci/of.c | 7 +
> > include/uapi/linux/virtio_ids.h | 1 +
> > include/uapi/linux/virtio_iommu.h | 161 +++
> > 10 files changed, 1448 insertions(+), 3 deletions(-) create mode
> > 100644 Documentation/devicetree/bindings/virtio/iommu.txt
> > create mode 100644 drivers/iommu/virtio-iommu.c create mode 100644
> > include/uapi/linux/virtio_iommu.h
> >
> for the whole series
> Tested-by: Eric Auger <eric.auger@redhat.com>
I have tested this series with virtio/vfio both
Tested-by: Bharat Bhushan <bharat.bhushan@nxp.com>
Thanks
-Bharat
>
> Thanks
>
> Eric
^ permalink raw reply
* Re: [PATCH net-next v3 00/13] virtio: support packed ring
From: David Miller @ 2018-11-27 6:18 UTC (permalink / raw)
To: mst; +Cc: virtio-dev, netdev, linux-kernel, virtualization, maxime.coquelin,
wexu
In-Reply-To: <20181127010157-mutt-send-email-mst@kernel.org>
From: "Michael S. Tsirkin" <mst@redhat.com>
Date: Tue, 27 Nov 2018 01:08:08 -0500
> On Wed, Nov 21, 2018 at 06:03:17PM +0800, Tiwei Bie wrote:
>> Hi,
>>
>> This patch set implements packed ring support in virtio driver.
>>
>> A performance test between pktgen (pktgen_sample03_burst_single_flow.sh)
>> and DPDK vhost (testpmd/rxonly/vhost-PMD) has been done, I saw
>> ~30% performance gain in packed ring in this case.
>>
>> To make this patch set work with below patch set for vhost,
>> some hacks are needed to set the _F_NEXT flag in indirect
>> descriptors (this should be fixed in vhost):
>>
>> https://lkml.org/lkml/2018/7/3/33
>
> I went over it and I think it's correct spec-wise.
>
> I have some ideas for enhancements but let's start
> with getting this stuff merged first.
>
> Acked-by: Michael S. Tsirkin <mst@redhat.com>
Series applied.
^ permalink raw reply
* Re: [PATCH net-next v3 00/13] virtio: support packed ring
From: Michael S. Tsirkin @ 2018-11-27 6:08 UTC (permalink / raw)
To: Tiwei Bie
Cc: virtio-dev, netdev, linux-kernel, virtualization, maxime.coquelin,
wexu
In-Reply-To: <20181121100330.24846-1-tiwei.bie@intel.com>
On Wed, Nov 21, 2018 at 06:03:17PM +0800, Tiwei Bie wrote:
> Hi,
>
> This patch set implements packed ring support in virtio driver.
>
> A performance test between pktgen (pktgen_sample03_burst_single_flow.sh)
> and DPDK vhost (testpmd/rxonly/vhost-PMD) has been done, I saw
> ~30% performance gain in packed ring in this case.
>
> To make this patch set work with below patch set for vhost,
> some hacks are needed to set the _F_NEXT flag in indirect
> descriptors (this should be fixed in vhost):
>
> https://lkml.org/lkml/2018/7/3/33
I went over it and I think it's correct spec-wise.
I have some ideas for enhancements but let's start
with getting this stuff merged first.
Acked-by: Michael S. Tsirkin <mst@redhat.com>
> v2 -> v3:
> - Use leXX instead of virtioXX (MST);
> - Refactor split ring first (MST);
> - Add debug helpers (MST);
> - Put split/packed ring specific fields in sub structures (MST);
> - Handle normal descriptors and indirect descriptors differently (MST);
> - Track the DMA addr/len related info in a separate structure (MST);
> - Calculate AVAIL/USED flags only when wrap counter wraps (MST);
> - Define a struct/union to read event structure (MST);
> - Define a macro for wrap counter bit in uapi (MST);
> - Define the AVAIL/USED bits as shifts instead of values (MST);
> - s/_F_/_FLAG_/ in VRING_PACKED_EVENT_* as they are values (MST);
> - Drop the notify workaround for QEMU's tx-timer in packed ring (MST);
>
> v1 -> v2:
> - Use READ_ONCE() to read event off_wrap and flags together (Jason);
> - Add comments related to ccw (Jason);
>
> RFC v6 -> v1:
> - Avoid extra virtio_wmb() in virtqueue_enable_cb_delayed_packed()
> when event idx is off (Jason);
> - Fix bufs calculation in virtqueue_enable_cb_delayed_packed() (Jason);
> - Test the state of the desc at used_idx instead of last_used_idx
> in virtqueue_enable_cb_delayed_packed() (Jason);
> - Save wrap counter (as part of queue state) in the return value
> of virtqueue_enable_cb_prepare_packed();
> - Refine the packed ring definitions in uapi;
> - Rebase on the net-next tree;
>
> RFC v5 -> RFC v6:
> - Avoid tracking addr/len/flags when DMA API isn't used (MST/Jason);
> - Define wrap counter as bool (Jason);
> - Use ALIGN() in vring_init_packed() (Jason);
> - Avoid using pointer to track `next` in detach_buf_packed() (Jason);
> - Add comments for barriers (Jason);
> - Don't enable RING_PACKED on ccw for now (noticed by Jason);
> - Refine the memory barrier in virtqueue_poll();
> - Add a missing memory barrier in virtqueue_enable_cb_delayed_packed();
> - Remove the hacks in virtqueue_enable_cb_prepare_packed();
>
> RFC v4 -> RFC v5:
> - Save DMA addr, etc in desc state (Jason);
> - Track used wrap counter;
>
> RFC v3 -> RFC v4:
> - Make ID allocation support out-of-order (Jason);
> - Various fixes for EVENT_IDX support;
>
> RFC v2 -> RFC v3:
> - Split into small patches (Jason);
> - Add helper virtqueue_use_indirect() (Jason);
> - Just set id for the last descriptor of a list (Jason);
> - Calculate the prev in virtqueue_add_packed() (Jason);
> - Fix/improve desc suppression code (Jason/MST);
> - Refine the code layout for XXX_split/packed and wrappers (MST);
> - Fix the comments and API in uapi (MST);
> - Remove the BUG_ON() for indirect (Jason);
> - Some other refinements and bug fixes;
>
> RFC v1 -> RFC v2:
> - Add indirect descriptor support - compile test only;
> - Add event suppression supprt - compile test only;
> - Move vring_packed_init() out of uapi (Jason, MST);
> - Merge two loops into one in virtqueue_add_packed() (Jason);
> - Split vring_unmap_one() for packed ring and split ring (Jason);
> - Avoid using '%' operator (Jason);
> - Rename free_head -> next_avail_idx (Jason);
> - Add comments for virtio_wmb() in virtqueue_add_packed() (Jason);
> - Some other refinements and bug fixes;
>
>
> Tiwei Bie (13):
> virtio: add packed ring types and macros
> virtio_ring: add _split suffix for split ring functions
> virtio_ring: put split ring functions together
> virtio_ring: put split ring fields in a sub struct
> virtio_ring: introduce debug helpers
> virtio_ring: introduce helper for indirect feature
> virtio_ring: allocate desc state for split ring separately
> virtio_ring: extract split ring handling from ring creation
> virtio_ring: cache whether we will use DMA API
> virtio_ring: introduce packed ring support
> virtio_ring: leverage event idx in packed ring
> virtio_ring: disable packed ring on unsupported transports
> virtio_ring: advertize packed ring layout
>
> drivers/misc/mic/vop/vop_main.c | 13 +
> drivers/remoteproc/remoteproc_virtio.c | 13 +
> drivers/s390/virtio/virtio_ccw.c | 14 +
> drivers/virtio/virtio_ring.c | 1811 +++++++++++++++++++++++++-------
> include/uapi/linux/virtio_config.h | 3 +
> include/uapi/linux/virtio_ring.h | 52 +
> 6 files changed, 1530 insertions(+), 376 deletions(-)
>
> --
> 2.14.5
^ permalink raw reply
* Re: [PATCH 0/9] drm: remove deprecated functions
From: Daniel Vetter @ 2018-11-26 14:12 UTC (permalink / raw)
To: Linus Walleij
Cc: Haneen Mohammed, Alexandre Belloni, Gilles Muller, Maxime Ripard,
open list:DRM PANEL DRIVERS, linux-kernel@vger.kernel.org,
Andrzej Hajda, cocci, Marek Vasut, Archit Taneja, Jonathan Corbet,
linux-doc, abrodkin, Dave Airlie, Ludovic Desroches, greenfoo,
linux+etnaviv, Nicolas Palix, Maarten Lankhorst, etnaviv,
Boris Brezillon, Christian Gmeiner
In-Reply-To: <CACRpkdaXUFwSiQnMqv2WBXr2ytn7kx3CeAuamkj9ZYU1C6bpnw@mail.gmail.com>
On Sat, Nov 24, 2018 at 10:17:13PM +0100, Linus Walleij wrote:
> On Wed, Nov 21, 2018 at 10:42 AM Daniel Vetter <daniel@ffwll.ch> wrote:
> > On Thu, Nov 15, 2018 at 11:38:35PM +0100, Linus Walleij wrote:
> > > On Thu, Nov 15, 2018 at 11:17 PM Fernando Ramos <greenfoo@gluegarage.com> wrote:
> > >
> > > > One of the things in the DRM TODO list ("Documentation/gpu/todo.rst") was to
> > > > "switch from reference/unreference to get/put". That's what this patch series is
> > > > about.
> > >
> > > The series:
> > > Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
> >
> > Since your reviewed it all, and there's a pile of acks for the driver
> > parts too: Want to go ahead and apply it too?
>
> OK I did... the git was quirky, patches changes around
> under my feet and dim started to complain about problems
> with commits that weren't even mine.
>
> It was especially scary.
>
> But I think I managed to apply the patches and push the
> branch now.
Except when you're racing with someone else you should only see conflicts
with stuff you've just pushed. Or if someone forgot to fix up their mess.
What was the conflict? Looking at the git-rerere log I'm not exactly sure
what happened ... Looks like only one of the patches didn't apply cleanly
anymore because it was somewhat outdated. Otherwise nothing from you.
-Daniel
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
^ permalink raw reply
* Re: [PATCH net 1/2] virtio-net: disable guest csum during XDP set
From: Jason Wang @ 2018-11-26 4:03 UTC (permalink / raw)
To: David Miller
Cc: mst, netdev, dsahern, linux-kernel, virtualization, pashinho1990,
brouer
In-Reply-To: <20181123.120119.2279234989819941118.davem@davemloft.net>
On 2018/11/24 上午4:01, David Miller wrote:
> From: Jason Wang <jasowang@redhat.com>
> Date: Thu, 22 Nov 2018 14:36:30 +0800
>
>> We don't disable VIRTIO_NET_F_GUEST_CSUM if XDP was set. This means we
>> can receive partial csumed packets with metadata kept in the
>> vnet_hdr. This may have several side effects:
>>
>> - It could be overridden by header adjustment, thus is might be not
>> correct after XDP processing.
>> - There's no way to pass such metadata information through
>> XDP_REDIRECT to another driver.
>> - XDP does not support checksum offload right now.
>>
>> So simply disable guest csum if possible in this the case of XDP.
>>
>> Fixes: 3f93522ffab2d ("virtio-net: switch off offloads on demand if possible on XDP set")
>> Reported-by: Jesper Dangaard Brouer <brouer@redhat.com>
>> Cc: Jesper Dangaard Brouer <brouer@redhat.com>
>> Cc: Pavel Popa <pashinho1990@gmail.com>
>> Cc: David Ahern <dsahern@gmail.com>
>> Signed-off-by: Jason Wang <jasowang@redhat.com>
> Applied and queued up for -stable.
>
> We really should have a way to use the checksum provided if the XDP
> program returns XDP_PASS and does not modify the packet contents
> or size.
Yes, I think this may require the assistance of BPF verifier to set a
flag or other. Then we can assume the metadata is safe to use.
Thanks
_______________________________________________
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization
^ permalink raw reply
* Re: [PATCH net-next 3/3] vhost: don't touch avail ring if in_order is negotiated
From: Jason Wang @ 2018-11-26 4:01 UTC (permalink / raw)
To: Michael S. Tsirkin; +Cc: netdev, linux-kernel, kvm, virtualization
In-Reply-To: <20181123103750-mutt-send-email-mst@kernel.org>
On 2018/11/23 下午11:41, Michael S. Tsirkin wrote:
> On Fri, Nov 23, 2018 at 11:00:16AM +0800, Jason Wang wrote:
>> Device use descriptors table in order, so there's no need to read
>> index from available ring. This eliminate the cache contention on
>> avail ring completely.
> Well this isn't what the in order feature says in the spec.
>
> It forces the used ring to be in the same order as
> the available ring. So I don't think you can skip
> checking the available ring.
Maybe I miss something. The spec
(https://github.com/oasis-tcs/virtio-spec master) said: "If
VIRTIO_F_IN_ORDER has been negotiated, driver uses descriptors in ring
order: starting from offset 0 in the table, and wrapping around at the
end of the table."
Even if I was wrong, maybe it's time to force this consider the obvious
improvement it brings? And maybe what you said is the reason that we
only allow the following optimization only for packed ring?
"notify the use of a batch of buffers to the driver by only writing out
a single used descriptor with the Buffer ID corresponding to the last
descriptor in the batch. "
This seems another good optimization for packed ring as well.
> And in fact depending on
> ring size and workload, using all of descriptor buffer might
> cause a slowdown.
This is not the sin of in order but the size of the queue I believe?
> Rather you should be able to get
> about the same speedup, but from skipping checking
> the used ring in virtio.
Yes, I've made such changes in virtio-net pmd. But since we're testing
it with vhost-kernel, the main contention was on available. So the
improvement was not obvious.
Thanks
>
>
>> Virito-user + vhost_kernel + XDP_DROP gives about ~10% improvement on
>> TX from 4.8Mpps to 5.3Mpps on Intel(R) Core(TM) i7-5600U CPU @
>> 2.60GHz.
>>
>> Signed-off-by: Jason Wang <jasowang@redhat.com>
>> ---
>> drivers/vhost/vhost.c | 19 ++++++++++++-------
>> 1 file changed, 12 insertions(+), 7 deletions(-)
>>
>> diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
>> index 3a5f81a66d34..c8be151bc897 100644
>> --- a/drivers/vhost/vhost.c
>> +++ b/drivers/vhost/vhost.c
>> @@ -2002,6 +2002,7 @@ int vhost_get_vq_desc(struct vhost_virtqueue *vq,
>> __virtio16 avail_idx;
>> __virtio16 ring_head;
>> int ret, access;
>> + bool in_order = vhost_has_feature(vq, VIRTIO_F_IN_ORDER);
>>
>> /* Check it isn't doing very strange things with descriptor numbers. */
>> last_avail_idx = vq->last_avail_idx;
>> @@ -2034,15 +2035,19 @@ int vhost_get_vq_desc(struct vhost_virtqueue *vq,
>>
>> /* Grab the next descriptor number they're advertising, and increment
>> * the index we've seen. */
>> - if (unlikely(vhost_get_avail(vq, ring_head,
>> - &vq->avail->ring[last_avail_idx & (vq->num - 1)]))) {
>> - vq_err(vq, "Failed to read head: idx %d address %p\n",
>> - last_avail_idx,
>> - &vq->avail->ring[last_avail_idx % vq->num]);
>> - return -EFAULT;
>> + if (!in_order) {
>> + if (unlikely(vhost_get_avail(vq, ring_head,
>> + &vq->avail->ring[last_avail_idx & (vq->num - 1)]))) {
>> + vq_err(vq, "Failed to read head: idx %d address %p\n",
>> + last_avail_idx,
>> + &vq->avail->ring[last_avail_idx % vq->num]);
>> + return -EFAULT;
>> + }
>> + head = vhost16_to_cpu(vq, ring_head);
>> + } else {
>> + head = last_avail_idx & (vq->num - 1);
>> }
>>
>> - head = vhost16_to_cpu(vq, ring_head);
>>
>> /* If their number is silly, that's an error. */
>> if (unlikely(head >= vq->num)) {
>> --
>> 2.17.1
_______________________________________________
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization
^ permalink raw reply
* Re: [PATCH net-next 2/3] vhost_net: support in order feature
From: Jason Wang @ 2018-11-26 3:52 UTC (permalink / raw)
To: Michael S. Tsirkin; +Cc: netdev, linux-kernel, kvm, virtualization
In-Reply-To: <20181123104333-mutt-send-email-mst@kernel.org>
On 2018/11/23 下午11:49, Michael S. Tsirkin wrote:
> On Fri, Nov 23, 2018 at 11:00:15AM +0800, Jason Wang wrote:
>> This makes vhost_net to support in order feature. This is as simple as
>> use datacopy path when it was negotiated. An alternative is not to
>> advertise in order when zerocopy is enabled which tends to be
>> suboptimal consider zerocopy may suffer from e.g HOL issues.
> Well IIRC vhost_zerocopy_signal_used is used to
> actually reorder used ring to match available ring.
> So with a big comment explaining why it is so,
> we could just enable IN_ORDER there too.
>
The problem is we allow switching between zerocopy and datacopy.
And what's more important, if we allow in order for zerocopy, a single
packet delay may hang all the rest.
Thanks
_______________________________________________
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization
^ permalink raw reply
* Call for Papers - WorldCIST'19 - La Toja Island, Spain | Deadline: November 30
From: Marle @ 2018-11-24 12:19 UTC (permalink / raw)
To: virtualization
[-- Attachment #1.1: Type: text/plain, Size: 9258 bytes --]
***** Proceedings published by Springer
------------------------------ -----------------------------
WorldCIST'19 - 7th World Conference on Information Systems and Technologies
16 - 19 April 2019 | La Toja Island, Spain
http://www.worldcist.org/ <http://www.worldcist.org/>
------------------------------ ------------------------------ ------------------------------ ------
Scope
The WorldCist'19 - 7th World Conference on Information Systems and Technologies, to be held at La Toja Island, Galicia, Spain, 16 - 19 April 2019, is a global forum for researchers and practitioners to present and discuss the most recent innovations, trends, results, experiences and concerns in the several perspectives of Information Systems and Technologies.
We are pleased to invite you to submit your papers to WorldCist'18. All submissions will be reviewed on the basis of relevance, originality, importance and clarity.
Themes
Submitted papers should be related with one or more of the main themes proposed for the Conference:
A) Information and Knowledge Management (IKM);
B) Organizational Models and Information Systems (OMIS);
C) Software and Systems Modeling (SSM);
D) Software Systems, Architectures, Applications and Tools (SSAAT);
E) Multimedia Systems and Applications (MSA);
F) Computer Networks, Mobility and Pervasive Systems (CNMPS);
G) Intelligent and Decision Support Systems (IDSS);
H) Big Data Analytics and Applications (BDAA);
I) Human-Computer Interaction (HCI);
J) Ethics, Computers and Security (ECS)
K) Health Informatics (HIS);
L) Information Technologies in Education (ITE);
M) Information Technologies in Radiocommunications (ITR);
N) Technologies for Biomedical Applications (TBA)
Types of Submissions and Decisions
Four types of papers can be submitted:
Full paper: Finished or consolidated R&D works, to be included in one of the Conference themes. These papers are assigned a 10-page limit.
Short paper: Ongoing works with relevant preliminary results, open to discussion. These papers are assigned a 7-page limit.
Poster paper: Initial work with relevant ideas, open to discussion. These papers are assigned to a 4-page limit.
Company paper: Companies' papers that show practical experience, R & D, tools, etc., focused on some topics of the conference. These papers are assigned to a 4-page limit.
Submitted papers must comply with the format of Advances in Intelligent Systems and Computing Series (see Instructions for Authors at Springer Website <https://www.springer.com/us/authors-editors/conference-proceedings/conference-proceedings-guidelines> or download a Word Template <ftp://ftp.springernature.com/cs-proceeding/llncs/word/splnproc1703.zip>or Latex Package <ftp://ftp.springernature.com/cs-proceeding/svproc/templates/ProcSci_TeX.zip>) be written in English, must not have been published before, not be under review for any other conference or publication and not include any information leading to the authors’ identification. Therefore, the authors’ names, affiliations and bibliographic references should not be included in the version for evaluation by the Program Committee. This information should only be included in the camera-ready version, saved in Word or Latex format and also in PDF format. These files must be accompanied by the Consent to Publish form <http://worldcist.org/copyright.pdf> filled out, in a ZIP file, and uploaded at the conference management system.
All papers will be subjected to a “double-blind review” by at least two members of the Program Committee.
Based on Program Committee evaluation, a paper can be rejected or accepted by the Conference Chairs. In the later case, it can be accepted as the type originally submitted or as another type. Thus, full papers can be accepted as short papers or poster papers only. Similarly, short papers can be accepted as poster papers only. In these cases, the authors will be allowed to maintain the original number of pages in the camera-ready version.
The authors of accepted poster papers must also build and print a poster to be exhibited during the Conference. This poster must follow an A1 or A2 vertical format. The Conference can includes Work Sessions where these posters are presented and orally discussed, with a 5 minute limit per poster.
The authors of accepted full papers will have 15 minutes to present their work in a Conference Work Session; approximately 5 minutes of discussion will follow each presentation. The authors of accepted short papers and company papers will have 11 minutes to present their work in a Conference Work Session; approximately 4 minutes of discussion will follow each presentation.
Publication and Indexing
To ensure that a full paper, short paper, poster paper or company paper is published, at least one of the authors must be fully registered by the 13th of January 2019, and the paper must comply with the suggested layout and page-limit. Additionally, all recommended changes must be addressed by the authors before they submit the camera-ready version.
No more than one paper per registration will be published. An extra fee must be paid for publication of additional papers, with a maximum of one additional paper per registration. One registration permits only the participation of one author in the conference.
Full and short papers will be published in Proceedings by Springer, in Advances in Intelligent Systems and Computing <http://www.springer.com/series/11156> series. Poster and company papers will not be published, just presented in the conference.
Published full and short papers will be submitted for indexation by ISI, EI-Compendex, SCOPUS, DBLP and Google Scholar, among others, and will be available in the SpringerLink Digital Library.
The authors of the best selected papers will be invited to extend them for publication in international journals indexed by ISI/SCI, SCOPUS and DBLP, among others, such as:
International Journal of Neural Systems <https://www.worldscientific.com/worldscinet/ijns> (IF: 4.58 / Q1)
Integrated Computer-Aided Engineering <http://www.iospress.nl/journal/integrated-computer-aided-engineering/> (IF: 3.667 / Q1)
Telecommunications Policy <https://www.journals.elsevier.com/telecommunications-policy> (IF: 2.087 / Q1)
Group Decision and Negotiation <https://www.springer.com/business+%26+management/operations+research/journal/10726> (IF: 1.869 / Q1)
Computers in Industry <https://www.journals.elsevier.com/computers-in-industry> (IF: 2.850 / Q2)
Journal of Medical Systems <http://www.springer.com/public+health/journal/10916> (IF: 2.098 / Q2)
Computer Languages, Systems & Structures <https://www.journals.elsevier.com/computer-languages-systems-and-structures/> (IF: 1.840 / Q2)
Cluster Computing <https://www.springer.com/computer/communication+networks/journal/10586> (IF: 1.601 / Q2)
Expert Systems - Journal of Knowledge Engineering <http://onlinelibrary.wiley.com/journal/10.1111/(ISSN)1468-0394> (IF: 1.43 / Q2)
Informatica - An International Journal <https://www.mii.lt/informatica/> (IF: 1.386 / Q2)
Journal of Intelligent & Fuzzy Systems <https://www.iospress.nl/journal/journal-of-intelligent-fuzzy-systems/> (IF: 1.426 / Q3)
Enterprise Information Systems <https://www.tandfonline.com/toc/teis20/current> (IF: 1.683 / Q3)
Data Technologies and Applications <http://www.emeraldgrouppublishing.com/products/journals/journals.htm?id=dta> (IF: 1.170 / Q3)
Innovations in Education and Teaching International <https://www.tandfonline.com/toc/riie20/current> (IF: 1.106 / Q3)
Intelligent Data Analysis <http://www.iospress.nl/journal/intelligent-data-analysis> (IF: 0.691 / Q4)
Computational and Mathematical Organization Theory <http://www.springer.com/business+%26+management/journal/10588> (IF: 0.641 / Q4)
AI Communications <https://www.iospress.nl/journal/ai-communications/> (IF: 0.461 / Q4)
Journal of Web Engineering <http://www.riverpublishers.com/journal.php?j=JWE/17/4> (IF: 0.311 / Q4)
Journal of Database Management <https://www.igi-global.com/journal/journal-database-management/1072> (IF: 0.231 / Q4)
Journal of Hospitality and Tourism Technology <http://www.emeraldgrouppublishing.com/products/journals/journals.htm?id=jhtt> (ISI - Emerging Sources Citation Index)
Computer Methods in Biomechanics and Biomedical Engineering - Imaging & Visualization <http://www.tandfonline.com/action/journalInformation?show=aimsScope&journalCode=tciv20#.V4KqBzUmMQs> (ISI - Emerging Sources Citation Index)
Journal of Information Systems Engineering & Management <http://www.lectitojournals.com/journal-for-information-systems-engineering-management>
Important Dates
Paper Submission: November 30, 2018
Notification of Acceptance: December 30, 2018
Payment of Registration, to ensure the inclusion of an accepted paper in the conference proceedings: January 13, 2019.
Camera-ready Submission: January 13, 2019
Website of WorldCIST'19: http://www.worldcist.org/ <http://www.worldcist.org/>
---
This email has been checked for viruses by AVG.
https://www.avg.com
[-- Attachment #1.2: Type: text/html, Size: 20470 bytes --]
[-- Attachment #2: Type: text/plain, Size: 183 bytes --]
_______________________________________________
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization
^ permalink raw reply
* Re: [PATCH v5 5/7] iommu: Add virtio-iommu driver
From: Michael S. Tsirkin @ 2018-11-23 22:02 UTC (permalink / raw)
To: Jean-Philippe Brucker
Cc: mark.rutland, virtio-dev, lorenzo.pieralisi, tnowicki, devicetree,
marc.zyngier, linux-pci, joro, will.deacon, virtualization,
eric.auger, iommu, robh+dt, bhelgaas, robin.murphy, kvmarm
In-Reply-To: <20181122193801.50510-6-jean-philippe.brucker@arm.com>
On Thu, Nov 22, 2018 at 07:37:59PM +0000, Jean-Philippe Brucker wrote:
> The virtio IOMMU is a para-virtualized device, allowing to send IOMMU
> requests such as map/unmap over virtio transport without emulating page
> tables. This implementation handles ATTACH, DETACH, MAP and UNMAP
> requests.
>
> The bulk of the code transforms calls coming from the IOMMU API into
> corresponding virtio requests. Mappings are kept in an interval tree
> instead of page tables.
>
> Signed-off-by: Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
> ---
> MAINTAINERS | 7 +
> drivers/iommu/Kconfig | 11 +
> drivers/iommu/Makefile | 1 +
> drivers/iommu/virtio-iommu.c | 916 ++++++++++++++++++++++++++++++
> include/uapi/linux/virtio_ids.h | 1 +
> include/uapi/linux/virtio_iommu.h | 104 ++++
> 6 files changed, 1040 insertions(+)
> create mode 100644 drivers/iommu/virtio-iommu.c
> create mode 100644 include/uapi/linux/virtio_iommu.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 1689dcfec800..3d8550c76f4a 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -15946,6 +15946,13 @@ S: Maintained
> F: drivers/virtio/virtio_input.c
> F: include/uapi/linux/virtio_input.h
>
> +VIRTIO IOMMU DRIVER
> +M: Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
> +L: virtualization@lists.linux-foundation.org
> +S: Maintained
> +F: drivers/iommu/virtio-iommu.c
> +F: include/uapi/linux/virtio_iommu.h
> +
> VIRTUAL BOX GUEST DEVICE DRIVER
> M: Hans de Goede <hdegoede@redhat.com>
> M: Arnd Bergmann <arnd@arndb.de>
> diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
> index bf2bbfa2a399..db5f2b8c23f5 100644
> --- a/drivers/iommu/Kconfig
> +++ b/drivers/iommu/Kconfig
> @@ -464,4 +464,15 @@ config QCOM_IOMMU
> help
> Support for IOMMU on certain Qualcomm SoCs.
>
> +config VIRTIO_IOMMU
> + bool "Virtio IOMMU driver"
> + depends on VIRTIO=y
> + select IOMMU_API
> + select INTERVAL_TREE
> + select ARM_DMA_USE_IOMMU if ARM
> + help
> + Para-virtualised IOMMU driver with virtio.
> +
> + Say Y here if you intend to run this kernel as a guest.
> +
> endif # IOMMU_SUPPORT
> diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
> index 5481e5fe1f95..bd7e55751d09 100644
> --- a/drivers/iommu/Makefile
> +++ b/drivers/iommu/Makefile
> @@ -36,3 +36,4 @@ obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o
> obj-$(CONFIG_FSL_PAMU) += fsl_pamu.o fsl_pamu_domain.o
> obj-$(CONFIG_S390_IOMMU) += s390-iommu.o
> obj-$(CONFIG_QCOM_IOMMU) += qcom_iommu.o
> +obj-$(CONFIG_VIRTIO_IOMMU) += virtio-iommu.o
> diff --git a/drivers/iommu/virtio-iommu.c b/drivers/iommu/virtio-iommu.c
> new file mode 100644
> index 000000000000..7540dab9c8dc
> --- /dev/null
> +++ b/drivers/iommu/virtio-iommu.c
> @@ -0,0 +1,916 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Virtio driver for the paravirtualized IOMMU
> + *
> + * Copyright (C) 2018 Arm Limited
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include <linux/amba/bus.h>
> +#include <linux/delay.h>
> +#include <linux/dma-iommu.h>
> +#include <linux/freezer.h>
> +#include <linux/interval_tree.h>
> +#include <linux/iommu.h>
> +#include <linux/module.h>
> +#include <linux/of_iommu.h>
> +#include <linux/of_platform.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/virtio.h>
> +#include <linux/virtio_config.h>
> +#include <linux/virtio_ids.h>
> +#include <linux/wait.h>
> +
> +#include <uapi/linux/virtio_iommu.h>
> +
> +#define MSI_IOVA_BASE 0x8000000
> +#define MSI_IOVA_LENGTH 0x100000
> +
> +#define VIOMMU_REQUEST_VQ 0
> +#define VIOMMU_NR_VQS 1
> +
> +struct viommu_dev {
> + struct iommu_device iommu;
> + struct device *dev;
> + struct virtio_device *vdev;
> +
> + struct ida domain_ids;
> +
> + struct virtqueue *vqs[VIOMMU_NR_VQS];
> + spinlock_t request_lock;
> + struct list_head requests;
> +
> + /* Device configuration */
> + struct iommu_domain_geometry geometry;
> + u64 pgsize_bitmap;
> + u8 domain_bits;
> +};
> +
> +struct viommu_mapping {
> + phys_addr_t paddr;
> + struct interval_tree_node iova;
> + u32 flags;
> +};
> +
> +struct viommu_domain {
> + struct iommu_domain domain;
> + struct viommu_dev *viommu;
> + struct mutex mutex; /* protects viommu pointer */
> + unsigned int id;
> +
> + spinlock_t mappings_lock;
> + struct rb_root_cached mappings;
> +
> + unsigned long nr_endpoints;
> +};
> +
> +struct viommu_endpoint {
> + struct viommu_dev *viommu;
> + struct viommu_domain *vdomain;
> +};
> +
> +struct viommu_request {
> + struct list_head list;
> + void *writeback;
> + unsigned int write_offset;
> + unsigned int len;
> + char buf[];
> +};
> +
> +#define to_viommu_domain(domain) \
> + container_of(domain, struct viommu_domain, domain)
> +
> +static int viommu_get_req_errno(void *buf, size_t len)
> +{
> + struct virtio_iommu_req_tail *tail = buf + len - sizeof(*tail);
> +
> + switch (tail->status) {
> + case VIRTIO_IOMMU_S_OK:
> + return 0;
> + case VIRTIO_IOMMU_S_UNSUPP:
> + return -ENOSYS;
> + case VIRTIO_IOMMU_S_INVAL:
> + return -EINVAL;
> + case VIRTIO_IOMMU_S_RANGE:
> + return -ERANGE;
> + case VIRTIO_IOMMU_S_NOENT:
> + return -ENOENT;
> + case VIRTIO_IOMMU_S_FAULT:
> + return -EFAULT;
> + case VIRTIO_IOMMU_S_IOERR:
> + case VIRTIO_IOMMU_S_DEVERR:
> + default:
> + return -EIO;
> + }
> +}
> +
> +static void viommu_set_req_status(void *buf, size_t len, int status)
> +{
> + struct virtio_iommu_req_tail *tail = buf + len - sizeof(*tail);
> +
> + tail->status = status;
> +}
> +
> +static off_t viommu_get_write_desc_offset(struct viommu_dev *viommu,
> + struct virtio_iommu_req_head *req,
> + size_t len)
> +{
> + size_t tail_size = sizeof(struct virtio_iommu_req_tail);
> +
> + return len - tail_size;
> +}
> +
> +/*
> + * __viommu_sync_req - Complete all in-flight requests
> + *
> + * Wait for all added requests to complete. When this function returns, all
> + * requests that were in-flight at the time of the call have completed.
> + */
> +static int __viommu_sync_req(struct viommu_dev *viommu)
> +{
> + int ret = 0;
> + unsigned int len;
> + size_t write_len;
> + struct viommu_request *req;
> + struct virtqueue *vq = viommu->vqs[VIOMMU_REQUEST_VQ];
> +
> + assert_spin_locked(&viommu->request_lock);
> +
> + virtqueue_kick(vq);
> +
> + while (!list_empty(&viommu->requests)) {
> + len = 0;
> + req = virtqueue_get_buf(vq, &len);
> + if (!req)
> + continue;
> +
> + if (!len)
> + viommu_set_req_status(req->buf, req->len,
> + VIRTIO_IOMMU_S_IOERR);
> +
> + write_len = req->len - req->write_offset;
> + if (req->writeback && len == write_len)
> + memcpy(req->writeback, req->buf + req->write_offset,
> + write_len);
> +
> + list_del(&req->list);
> + kfree(req);
> + }
I didn't notice this in the past but it seems this will spin
with interrupts disabled until host handles the request.
Please do not do this - host execution can be another
task that needs the same host CPU. This will then disable
interrupts for a very very long time.
What to do then? Queue in software and wake up task.
As kick is vm exit, kick under interrupts disabled is discouraged too:
better to prepare for kick enable interrupts then kick.
> +
> + return ret;
> +}
> +
> +static int viommu_sync_req(struct viommu_dev *viommu)
> +{
> + int ret;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&viommu->request_lock, flags);
> + ret = __viommu_sync_req(viommu);
> + if (ret)
> + dev_dbg(viommu->dev, "could not sync requests (%d)\n", ret);
> + spin_unlock_irqrestore(&viommu->request_lock, flags);
> +
> + return ret;
> +}
> +
> +/*
> + * __viommu_add_request - Add one request to the queue
> + * @buf: pointer to the request buffer
> + * @len: length of the request buffer
> + * @writeback: copy data back to the buffer when the request completes.
> + *
> + * Add a request to the queue. Only synchronize the queue if it's already full.
> + * Otherwise don't kick the queue nor wait for requests to complete.
> + *
> + * When @writeback is true, data written by the device, including the request
> + * status, is copied into @buf after the request completes. This is unsafe if
> + * the caller allocates @buf on stack and drops the lock between add_req() and
> + * sync_req().
> + *
> + * Return 0 if the request was successfully added to the queue.
> + */
> +static int __viommu_add_req(struct viommu_dev *viommu, void *buf, size_t len,
> + bool writeback)
> +{
> + int ret;
> + off_t write_offset;
> + struct viommu_request *req;
> + struct scatterlist top_sg, bottom_sg;
> + struct scatterlist *sg[2] = { &top_sg, &bottom_sg };
> + struct virtqueue *vq = viommu->vqs[VIOMMU_REQUEST_VQ];
> +
> + assert_spin_locked(&viommu->request_lock);
> +
> + write_offset = viommu_get_write_desc_offset(viommu, buf, len);
> + if (write_offset <= 0)
> + return -EINVAL;
> +
> + req = kzalloc(sizeof(*req) + len, GFP_ATOMIC);
> + if (!req)
> + return -ENOMEM;
> +
> + req->len = len;
> + if (writeback) {
> + req->writeback = buf + write_offset;
> + req->write_offset = write_offset;
> + }
> + memcpy(&req->buf, buf, write_offset);
> +
> + sg_init_one(&top_sg, req->buf, write_offset);
> + sg_init_one(&bottom_sg, req->buf + write_offset, len - write_offset);
> +
> + ret = virtqueue_add_sgs(vq, sg, 1, 1, req, GFP_ATOMIC);
> + if (ret == -ENOSPC) {
> + /* If the queue is full, sync and retry */
> + if (!__viommu_sync_req(viommu))
> + ret = virtqueue_add_sgs(vq, sg, 1, 1, req, GFP_ATOMIC);
> + }
> + if (ret)
> + goto err_free;
> +
> + list_add_tail(&req->list, &viommu->requests);
> + return 0;
> +
> +err_free:
> + kfree(req);
> + return ret;
> +}
> +
> +static int viommu_add_req(struct viommu_dev *viommu, void *buf, size_t len)
> +{
> + int ret;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&viommu->request_lock, flags);
> + ret = __viommu_add_req(viommu, buf, len, false);
> + if (ret)
> + dev_dbg(viommu->dev, "could not add request: %d\n", ret);
> + spin_unlock_irqrestore(&viommu->request_lock, flags);
> +
> + return ret;
> +}
> +
> +/*
> + * Send a request and wait for it to complete. Return the request status (as an
> + * errno)
> + */
> +static int viommu_send_req_sync(struct viommu_dev *viommu, void *buf,
> + size_t len)
> +{
> + int ret;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&viommu->request_lock, flags);
> +
> + ret = __viommu_add_req(viommu, buf, len, true);
> + if (ret) {
> + dev_dbg(viommu->dev, "could not add request (%d)\n", ret);
> + goto out_unlock;
> + }
> +
> + ret = __viommu_sync_req(viommu);
> + if (ret) {
> + dev_dbg(viommu->dev, "could not sync requests (%d)\n", ret);
> + /* Fall-through (get the actual request status) */
> + }
> +
> + ret = viommu_get_req_errno(buf, len);
> +out_unlock:
> + spin_unlock_irqrestore(&viommu->request_lock, flags);
> + return ret;
> +}
> +
> +/*
> + * viommu_add_mapping - add a mapping to the internal tree
> + *
> + * On success, return the new mapping. Otherwise return NULL.
> + */
> +static int viommu_add_mapping(struct viommu_domain *vdomain, unsigned long iova,
> + phys_addr_t paddr, size_t size, u32 flags)
> +{
> + unsigned long irqflags;
> + struct viommu_mapping *mapping;
> +
> + mapping = kzalloc(sizeof(*mapping), GFP_ATOMIC);
> + if (!mapping)
> + return -ENOMEM;
> +
> + mapping->paddr = paddr;
> + mapping->iova.start = iova;
> + mapping->iova.last = iova + size - 1;
> + mapping->flags = flags;
> +
> + spin_lock_irqsave(&vdomain->mappings_lock, irqflags);
> + interval_tree_insert(&mapping->iova, &vdomain->mappings);
> + spin_unlock_irqrestore(&vdomain->mappings_lock, irqflags);
> +
> + return 0;
> +}
> +
> +/*
> + * viommu_del_mappings - remove mappings from the internal tree
> + *
> + * @vdomain: the domain
> + * @iova: start of the range
> + * @size: size of the range. A size of 0 corresponds to the entire address
> + * space.
> + *
> + * On success, returns the number of unmapped bytes (>= size)
> + */
> +static size_t viommu_del_mappings(struct viommu_domain *vdomain,
> + unsigned long iova, size_t size)
> +{
> + size_t unmapped = 0;
> + unsigned long flags;
> + unsigned long last = iova + size - 1;
> + struct viommu_mapping *mapping = NULL;
> + struct interval_tree_node *node, *next;
> +
> + spin_lock_irqsave(&vdomain->mappings_lock, flags);
> + next = interval_tree_iter_first(&vdomain->mappings, iova, last);
> + while (next) {
> + node = next;
> + mapping = container_of(node, struct viommu_mapping, iova);
> + next = interval_tree_iter_next(node, iova, last);
> +
> + /* Trying to split a mapping? */
> + if (mapping->iova.start < iova)
> + break;
> +
> + /*
> + * Virtio-iommu doesn't allow UNMAP to split a mapping created
> + * with a single MAP request, so remove the full mapping.
> + */
> + unmapped += mapping->iova.last - mapping->iova.start + 1;
> +
> + interval_tree_remove(node, &vdomain->mappings);
> + kfree(mapping);
> + }
> + spin_unlock_irqrestore(&vdomain->mappings_lock, flags);
> +
> + return unmapped;
> +}
> +
> +/*
> + * viommu_replay_mappings - re-send MAP requests
> + *
> + * When reattaching a domain that was previously detached from all endpoints,
> + * mappings were deleted from the device. Re-create the mappings available in
> + * the internal tree.
> + */
> +static int viommu_replay_mappings(struct viommu_domain *vdomain)
> +{
> + int ret = 0;
> + unsigned long flags;
> + struct viommu_mapping *mapping;
> + struct interval_tree_node *node;
> + struct virtio_iommu_req_map map;
> +
> + spin_lock_irqsave(&vdomain->mappings_lock, flags);
> + node = interval_tree_iter_first(&vdomain->mappings, 0, -1UL);
> + while (node) {
> + mapping = container_of(node, struct viommu_mapping, iova);
> + map = (struct virtio_iommu_req_map) {
> + .head.type = VIRTIO_IOMMU_T_MAP,
> + .domain = cpu_to_le32(vdomain->id),
> + .virt_start = cpu_to_le64(mapping->iova.start),
> + .virt_end = cpu_to_le64(mapping->iova.last),
> + .phys_start = cpu_to_le64(mapping->paddr),
> + .flags = cpu_to_le32(mapping->flags),
> + };
> +
> + ret = viommu_send_req_sync(vdomain->viommu, &map, sizeof(map));
> + if (ret)
> + break;
> +
> + node = interval_tree_iter_next(node, 0, -1UL);
> + }
> + spin_unlock_irqrestore(&vdomain->mappings_lock, flags);
> +
> + return ret;
> +}
> +
> +/* IOMMU API */
> +
> +static struct iommu_domain *viommu_domain_alloc(unsigned type)
> +{
> + struct viommu_domain *vdomain;
> +
> + if (type != IOMMU_DOMAIN_UNMANAGED && type != IOMMU_DOMAIN_DMA)
> + return NULL;
> +
> + vdomain = kzalloc(sizeof(*vdomain), GFP_KERNEL);
> + if (!vdomain)
> + return NULL;
> +
> + mutex_init(&vdomain->mutex);
> + spin_lock_init(&vdomain->mappings_lock);
> + vdomain->mappings = RB_ROOT_CACHED;
> +
> + if (type == IOMMU_DOMAIN_DMA &&
> + iommu_get_dma_cookie(&vdomain->domain)) {
> + kfree(vdomain);
> + return NULL;
> + }
> +
> + return &vdomain->domain;
> +}
> +
> +static int viommu_domain_finalise(struct viommu_dev *viommu,
> + struct iommu_domain *domain)
> +{
> + int ret;
> + struct viommu_domain *vdomain = to_viommu_domain(domain);
> + unsigned int max_domain = viommu->domain_bits > 31 ? ~0 :
> + (1U << viommu->domain_bits) - 1;
> +
> + vdomain->viommu = viommu;
> +
> + domain->pgsize_bitmap = viommu->pgsize_bitmap;
> + domain->geometry = viommu->geometry;
> +
> + ret = ida_alloc_max(&viommu->domain_ids, max_domain, GFP_KERNEL);
> + if (ret >= 0)
> + vdomain->id = (unsigned int)ret;
> +
> + return ret > 0 ? 0 : ret;
> +}
> +
> +static void viommu_domain_free(struct iommu_domain *domain)
> +{
> + struct viommu_domain *vdomain = to_viommu_domain(domain);
> +
> + iommu_put_dma_cookie(domain);
> +
> + /* Free all remaining mappings (size 2^64) */
> + viommu_del_mappings(vdomain, 0, 0);
> +
> + if (vdomain->viommu)
> + ida_free(&vdomain->viommu->domain_ids, vdomain->id);
> +
> + kfree(vdomain);
> +}
> +
> +static int viommu_attach_dev(struct iommu_domain *domain, struct device *dev)
> +{
> + int i;
> + int ret = 0;
> + struct virtio_iommu_req_attach req;
> + struct iommu_fwspec *fwspec = dev->iommu_fwspec;
> + struct viommu_endpoint *vdev = fwspec->iommu_priv;
> + struct viommu_domain *vdomain = to_viommu_domain(domain);
> +
> + mutex_lock(&vdomain->mutex);
> + if (!vdomain->viommu) {
> + /*
> + * Properly initialize the domain now that we know which viommu
> + * owns it.
> + */
> + ret = viommu_domain_finalise(vdev->viommu, domain);
> + } else if (vdomain->viommu != vdev->viommu) {
> + dev_err(dev, "cannot attach to foreign vIOMMU\n");
> + ret = -EXDEV;
> + }
> + mutex_unlock(&vdomain->mutex);
> +
> + if (ret)
> + return ret;
> +
> + /*
> + * In the virtio-iommu device, when attaching the endpoint to a new
> + * domain, it is detached from the old one and, if as as a result the
> + * old domain isn't attached to any endpoint, all mappings are removed
> + * from the old domain and it is freed.
> + *
> + * In the driver the old domain still exists, and its mappings will be
> + * recreated if it gets reattached to an endpoint. Otherwise it will be
> + * freed explicitly.
> + *
> + * vdev->vdomain is protected by group->mutex
> + */
> + if (vdev->vdomain)
> + vdev->vdomain->nr_endpoints--;
> +
> + req = (struct virtio_iommu_req_attach) {
> + .head.type = VIRTIO_IOMMU_T_ATTACH,
> + .domain = cpu_to_le32(vdomain->id),
> + };
> +
> + for (i = 0; i < fwspec->num_ids; i++) {
> + req.endpoint = cpu_to_le32(fwspec->ids[i]);
> +
> + ret = viommu_send_req_sync(vdomain->viommu, &req, sizeof(req));
> + if (ret)
> + return ret;
> + }
> +
> + if (!vdomain->nr_endpoints) {
> + /*
> + * This endpoint is the first to be attached to the domain.
> + * Replay existing mappings (e.g. SW MSI).
> + */
> + ret = viommu_replay_mappings(vdomain);
> + if (ret)
> + return ret;
> + }
> +
> + vdomain->nr_endpoints++;
> + vdev->vdomain = vdomain;
> +
> + return 0;
> +}
> +
> +static int viommu_map(struct iommu_domain *domain, unsigned long iova,
> + phys_addr_t paddr, size_t size, int prot)
> +{
> + int ret;
> + int flags;
> + struct virtio_iommu_req_map map;
> + struct viommu_domain *vdomain = to_viommu_domain(domain);
> +
> + flags = (prot & IOMMU_READ ? VIRTIO_IOMMU_MAP_F_READ : 0) |
> + (prot & IOMMU_WRITE ? VIRTIO_IOMMU_MAP_F_WRITE : 0) |
> + (prot & IOMMU_MMIO ? VIRTIO_IOMMU_MAP_F_MMIO : 0);
> +
> + ret = viommu_add_mapping(vdomain, iova, paddr, size, flags);
> + if (ret)
> + return ret;
> +
> + map = (struct virtio_iommu_req_map) {
> + .head.type = VIRTIO_IOMMU_T_MAP,
> + .domain = cpu_to_le32(vdomain->id),
> + .virt_start = cpu_to_le64(iova),
> + .phys_start = cpu_to_le64(paddr),
> + .virt_end = cpu_to_le64(iova + size - 1),
> + .flags = cpu_to_le32(flags),
> + };
> +
> + if (!vdomain->nr_endpoints)
> + return 0;
> +
> + ret = viommu_send_req_sync(vdomain->viommu, &map, sizeof(map));
> + if (ret)
> + viommu_del_mappings(vdomain, iova, size);
> +
> + return ret;
> +}
> +
> +static size_t viommu_unmap(struct iommu_domain *domain, unsigned long iova,
> + size_t size)
> +{
> + int ret = 0;
> + size_t unmapped;
> + struct virtio_iommu_req_unmap unmap;
> + struct viommu_domain *vdomain = to_viommu_domain(domain);
> +
> + unmapped = viommu_del_mappings(vdomain, iova, size);
> + if (unmapped < size)
> + return 0;
> +
> + /* Device already removed all mappings after detach. */
> + if (!vdomain->nr_endpoints)
> + return unmapped;
> +
> + unmap = (struct virtio_iommu_req_unmap) {
> + .head.type = VIRTIO_IOMMU_T_UNMAP,
> + .domain = cpu_to_le32(vdomain->id),
> + .virt_start = cpu_to_le64(iova),
> + .virt_end = cpu_to_le64(iova + unmapped - 1),
> + };
> +
> + ret = viommu_add_req(vdomain->viommu, &unmap, sizeof(unmap));
> + return ret ? 0 : unmapped;
> +}
> +
> +static phys_addr_t viommu_iova_to_phys(struct iommu_domain *domain,
> + dma_addr_t iova)
> +{
> + u64 paddr = 0;
> + unsigned long flags;
> + struct viommu_mapping *mapping;
> + struct interval_tree_node *node;
> + struct viommu_domain *vdomain = to_viommu_domain(domain);
> +
> + spin_lock_irqsave(&vdomain->mappings_lock, flags);
> + node = interval_tree_iter_first(&vdomain->mappings, iova, iova);
> + if (node) {
> + mapping = container_of(node, struct viommu_mapping, iova);
> + paddr = mapping->paddr + (iova - mapping->iova.start);
> + }
> + spin_unlock_irqrestore(&vdomain->mappings_lock, flags);
> +
> + return paddr;
> +}
> +
> +static void viommu_iotlb_sync(struct iommu_domain *domain)
> +{
> + struct viommu_domain *vdomain = to_viommu_domain(domain);
> +
> + viommu_sync_req(vdomain->viommu);
> +}
> +
> +static void viommu_get_resv_regions(struct device *dev, struct list_head *head)
> +{
> + struct iommu_resv_region *region;
> + int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
> +
> + region = iommu_alloc_resv_region(MSI_IOVA_BASE, MSI_IOVA_LENGTH, prot,
> + IOMMU_RESV_SW_MSI);
> + if (!region)
> + return;
> +
> + list_add_tail(®ion->list, head);
> + iommu_dma_get_resv_regions(dev, head);
> +}
> +
> +static void viommu_put_resv_regions(struct device *dev, struct list_head *head)
> +{
> + struct iommu_resv_region *entry, *next;
> +
> + list_for_each_entry_safe(entry, next, head, list)
> + kfree(entry);
> +}
> +
> +static struct iommu_ops viommu_ops;
> +static struct virtio_driver virtio_iommu_drv;
> +
> +static int viommu_match_node(struct device *dev, void *data)
> +{
> + return dev->parent->fwnode == data;
> +}
> +
> +static struct viommu_dev *viommu_get_by_fwnode(struct fwnode_handle *fwnode)
> +{
> + struct device *dev = driver_find_device(&virtio_iommu_drv.driver, NULL,
> + fwnode, viommu_match_node);
> + put_device(dev);
> +
> + return dev ? dev_to_virtio(dev)->priv : NULL;
> +}
> +
> +static int viommu_add_device(struct device *dev)
> +{
> + int ret;
> + struct iommu_group *group;
> + struct viommu_endpoint *vdev;
> + struct viommu_dev *viommu = NULL;
> + struct iommu_fwspec *fwspec = dev->iommu_fwspec;
> +
> + if (!fwspec || fwspec->ops != &viommu_ops)
> + return -ENODEV;
> +
> + viommu = viommu_get_by_fwnode(fwspec->iommu_fwnode);
> + if (!viommu)
> + return -ENODEV;
> +
> + vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
> + if (!vdev)
> + return -ENOMEM;
> +
> + vdev->viommu = viommu;
> + fwspec->iommu_priv = vdev;
> +
> + ret = iommu_device_link(&viommu->iommu, dev);
> + if (ret)
> + goto err_free_dev;
> +
> + /*
> + * Last step creates a default domain and attaches to it. Everything
> + * must be ready.
> + */
> + group = iommu_group_get_for_dev(dev);
> + if (IS_ERR(group)) {
> + ret = PTR_ERR(group);
> + goto err_unlink_dev;
> + }
> +
> + iommu_group_put(group);
> +
> + return PTR_ERR_OR_ZERO(group);
> +
> +err_unlink_dev:
> + iommu_device_unlink(&viommu->iommu, dev);
> +err_free_dev:
> + kfree(vdev);
> +
> + return ret;
> +}
> +
> +static void viommu_remove_device(struct device *dev)
> +{
> + struct viommu_endpoint *vdev;
> + struct iommu_fwspec *fwspec = dev->iommu_fwspec;
> +
> + if (!fwspec || fwspec->ops != &viommu_ops)
> + return;
> +
> + vdev = fwspec->iommu_priv;
> +
> + iommu_group_remove_device(dev);
> + iommu_device_unlink(&vdev->viommu->iommu, dev);
> + kfree(vdev);
> +}
> +
> +static struct iommu_group *viommu_device_group(struct device *dev)
> +{
> + if (dev_is_pci(dev))
> + return pci_device_group(dev);
> + else
> + return generic_device_group(dev);
> +}
> +
> +static int viommu_of_xlate(struct device *dev, struct of_phandle_args *args)
> +{
> + return iommu_fwspec_add_ids(dev, args->args, 1);
> +}
> +
> +static struct iommu_ops viommu_ops = {
> + .domain_alloc = viommu_domain_alloc,
> + .domain_free = viommu_domain_free,
> + .attach_dev = viommu_attach_dev,
> + .map = viommu_map,
> + .unmap = viommu_unmap,
> + .iova_to_phys = viommu_iova_to_phys,
> + .iotlb_sync = viommu_iotlb_sync,
> + .add_device = viommu_add_device,
> + .remove_device = viommu_remove_device,
> + .device_group = viommu_device_group,
> + .get_resv_regions = viommu_get_resv_regions,
> + .put_resv_regions = viommu_put_resv_regions,
> + .of_xlate = viommu_of_xlate,
> +};
> +
> +static int viommu_init_vqs(struct viommu_dev *viommu)
> +{
> + struct virtio_device *vdev = dev_to_virtio(viommu->dev);
> + const char *name = "request";
> + void *ret;
> +
> + ret = virtio_find_single_vq(vdev, NULL, name);
> + if (IS_ERR(ret)) {
> + dev_err(viommu->dev, "cannot find VQ\n");
> + return PTR_ERR(ret);
> + }
> +
> + viommu->vqs[VIOMMU_REQUEST_VQ] = ret;
> +
> + return 0;
> +}
> +
> +static int viommu_probe(struct virtio_device *vdev)
> +{
> + struct device *parent_dev = vdev->dev.parent;
> + struct viommu_dev *viommu = NULL;
> + struct device *dev = &vdev->dev;
> + u64 input_start = 0;
> + u64 input_end = -1UL;
> + int ret;
> +
> + if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1) ||
> + !virtio_has_feature(vdev, VIRTIO_IOMMU_F_MAP_UNMAP))
> + return -ENODEV;
> +
> + viommu = devm_kzalloc(dev, sizeof(*viommu), GFP_KERNEL);
> + if (!viommu)
> + return -ENOMEM;
> +
> + spin_lock_init(&viommu->request_lock);
> + ida_init(&viommu->domain_ids);
> + viommu->dev = dev;
> + viommu->vdev = vdev;
> + INIT_LIST_HEAD(&viommu->requests);
> +
> + ret = viommu_init_vqs(viommu);
> + if (ret)
> + return ret;
> +
> + virtio_cread(vdev, struct virtio_iommu_config, page_size_mask,
> + &viommu->pgsize_bitmap);
> +
> + if (!viommu->pgsize_bitmap) {
> + ret = -EINVAL;
> + goto err_free_vqs;
> + }
> +
> + viommu->domain_bits = 32;
> +
> + /* Optional features */
> + virtio_cread_feature(vdev, VIRTIO_IOMMU_F_INPUT_RANGE,
> + struct virtio_iommu_config, input_range.start,
> + &input_start);
> +
> + virtio_cread_feature(vdev, VIRTIO_IOMMU_F_INPUT_RANGE,
> + struct virtio_iommu_config, input_range.end,
> + &input_end);
> +
> + virtio_cread_feature(vdev, VIRTIO_IOMMU_F_DOMAIN_BITS,
> + struct virtio_iommu_config, domain_bits,
> + &viommu->domain_bits);
> +
> + viommu->geometry = (struct iommu_domain_geometry) {
> + .aperture_start = input_start,
> + .aperture_end = input_end,
> + .force_aperture = true,
> + };
> +
> + viommu_ops.pgsize_bitmap = viommu->pgsize_bitmap;
> +
> + virtio_device_ready(vdev);
> +
> + ret = iommu_device_sysfs_add(&viommu->iommu, dev, NULL, "%s",
> + virtio_bus_name(vdev));
> + if (ret)
> + goto err_free_vqs;
> +
> + iommu_device_set_ops(&viommu->iommu, &viommu_ops);
> + iommu_device_set_fwnode(&viommu->iommu, parent_dev->fwnode);
> +
> + iommu_device_register(&viommu->iommu);
> +
> +#ifdef CONFIG_PCI
> + if (pci_bus_type.iommu_ops != &viommu_ops) {
> + pci_request_acs();
> + ret = bus_set_iommu(&pci_bus_type, &viommu_ops);
> + if (ret)
> + goto err_unregister;
> + }
> +#endif
> +#ifdef CONFIG_ARM_AMBA
> + if (amba_bustype.iommu_ops != &viommu_ops) {
> + ret = bus_set_iommu(&amba_bustype, &viommu_ops);
> + if (ret)
> + goto err_unregister;
> + }
> +#endif
> + if (platform_bus_type.iommu_ops != &viommu_ops) {
> + ret = bus_set_iommu(&platform_bus_type, &viommu_ops);
> + if (ret)
> + goto err_unregister;
> + }
> +
> + vdev->priv = viommu;
> +
> + dev_info(dev, "input address: %u bits\n",
> + order_base_2(viommu->geometry.aperture_end));
> + dev_info(dev, "page mask: %#llx\n", viommu->pgsize_bitmap);
> +
> + return 0;
> +
> +err_unregister:
> + iommu_device_sysfs_remove(&viommu->iommu);
> + iommu_device_unregister(&viommu->iommu);
> +err_free_vqs:
> + vdev->config->del_vqs(vdev);
> +
> + return ret;
> +}
> +
> +static void viommu_remove(struct virtio_device *vdev)
> +{
> + struct viommu_dev *viommu = vdev->priv;
> +
> + iommu_device_sysfs_remove(&viommu->iommu);
> + iommu_device_unregister(&viommu->iommu);
> +
> + /* Stop all virtqueues */
> + vdev->config->reset(vdev);
> + vdev->config->del_vqs(vdev);
> +
> + dev_info(&vdev->dev, "device removed\n");
> +}
> +
> +static void viommu_config_changed(struct virtio_device *vdev)
> +{
> + dev_warn(&vdev->dev, "config changed\n");
> +}
> +
> +static unsigned int features[] = {
> + VIRTIO_IOMMU_F_MAP_UNMAP,
> + VIRTIO_IOMMU_F_DOMAIN_BITS,
> + VIRTIO_IOMMU_F_INPUT_RANGE,
> +};
> +
> +static struct virtio_device_id id_table[] = {
> + { VIRTIO_ID_IOMMU, VIRTIO_DEV_ANY_ID },
> + { 0 },
> +};
> +
> +static struct virtio_driver virtio_iommu_drv = {
> + .driver.name = KBUILD_MODNAME,
> + .driver.owner = THIS_MODULE,
> + .id_table = id_table,
> + .feature_table = features,
> + .feature_table_size = ARRAY_SIZE(features),
> + .probe = viommu_probe,
> + .remove = viommu_remove,
> + .config_changed = viommu_config_changed,
> +};
> +
> +module_virtio_driver(virtio_iommu_drv);
> +
> +MODULE_DESCRIPTION("Virtio IOMMU driver");
> +MODULE_AUTHOR("Jean-Philippe Brucker <jean-philippe.brucker@arm.com>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h
> index 6d5c3b2d4f4d..cfe47c5d9a56 100644
> --- a/include/uapi/linux/virtio_ids.h
> +++ b/include/uapi/linux/virtio_ids.h
> @@ -43,5 +43,6 @@
> #define VIRTIO_ID_INPUT 18 /* virtio input */
> #define VIRTIO_ID_VSOCK 19 /* virtio vsock transport */
> #define VIRTIO_ID_CRYPTO 20 /* virtio crypto */
> +#define VIRTIO_ID_IOMMU 23 /* virtio IOMMU */
>
> #endif /* _LINUX_VIRTIO_IDS_H */
> diff --git a/include/uapi/linux/virtio_iommu.h b/include/uapi/linux/virtio_iommu.h
> new file mode 100644
> index 000000000000..e7c05e3afa44
> --- /dev/null
> +++ b/include/uapi/linux/virtio_iommu.h
> @@ -0,0 +1,104 @@
> +/* SPDX-License-Identifier: BSD-3-Clause */
> +/*
> + * Virtio-iommu definition v0.9
> + *
> + * Copyright (C) 2018 Arm Ltd.
> + */
> +#ifndef _UAPI_LINUX_VIRTIO_IOMMU_H
> +#define _UAPI_LINUX_VIRTIO_IOMMU_H
> +
> +#include <linux/types.h>
> +
> +/* Feature bits */
> +#define VIRTIO_IOMMU_F_INPUT_RANGE 0
> +#define VIRTIO_IOMMU_F_DOMAIN_BITS 1
> +#define VIRTIO_IOMMU_F_MAP_UNMAP 2
> +#define VIRTIO_IOMMU_F_BYPASS 3
> +
> +struct virtio_iommu_range {
> + __u64 start;
> + __u64 end;
> +};
> +
> +struct virtio_iommu_config {
> + /* Supported page sizes */
> + __u64 page_size_mask;
> + /* Supported IOVA range */
> + struct virtio_iommu_range input_range;
> + /* Max domain ID size */
> + __u8 domain_bits;
> + __u8 padding[3];
> +};
> +
> +/* Request types */
> +#define VIRTIO_IOMMU_T_ATTACH 0x01
> +#define VIRTIO_IOMMU_T_DETACH 0x02
> +#define VIRTIO_IOMMU_T_MAP 0x03
> +#define VIRTIO_IOMMU_T_UNMAP 0x04
> +
> +/* Status types */
> +#define VIRTIO_IOMMU_S_OK 0x00
> +#define VIRTIO_IOMMU_S_IOERR 0x01
> +#define VIRTIO_IOMMU_S_UNSUPP 0x02
> +#define VIRTIO_IOMMU_S_DEVERR 0x03
> +#define VIRTIO_IOMMU_S_INVAL 0x04
> +#define VIRTIO_IOMMU_S_RANGE 0x05
> +#define VIRTIO_IOMMU_S_NOENT 0x06
> +#define VIRTIO_IOMMU_S_FAULT 0x07
> +
> +struct virtio_iommu_req_head {
> + __u8 type;
> + __u8 reserved[3];
> +};
> +
> +struct virtio_iommu_req_tail {
> + __u8 status;
> + __u8 reserved[3];
> +};
> +
> +struct virtio_iommu_req_attach {
> + struct virtio_iommu_req_head head;
> + __le32 domain;
> + __le32 endpoint;
> + __u8 reserved[8];
> + struct virtio_iommu_req_tail tail;
> +};
> +
> +struct virtio_iommu_req_detach {
> + struct virtio_iommu_req_head head;
> + __le32 domain;
> + __le32 endpoint;
> + __u8 reserved[8];
> + struct virtio_iommu_req_tail tail;
> +};
> +
> +#define VIRTIO_IOMMU_MAP_F_READ (1 << 0)
> +#define VIRTIO_IOMMU_MAP_F_WRITE (1 << 1)
> +#define VIRTIO_IOMMU_MAP_F_EXEC (1 << 2)
> +#define VIRTIO_IOMMU_MAP_F_MMIO (1 << 3)
> +
> +#define VIRTIO_IOMMU_MAP_F_MASK (VIRTIO_IOMMU_MAP_F_READ | \
> + VIRTIO_IOMMU_MAP_F_WRITE | \
> + VIRTIO_IOMMU_MAP_F_EXEC | \
> + VIRTIO_IOMMU_MAP_F_MMIO)
> +
> +struct virtio_iommu_req_map {
> + struct virtio_iommu_req_head head;
> + __le32 domain;
> + __le64 virt_start;
> + __le64 virt_end;
> + __le64 phys_start;
> + __le32 flags;
> + struct virtio_iommu_req_tail tail;
> +};
> +
> +struct virtio_iommu_req_unmap {
> + struct virtio_iommu_req_head head;
> + __le32 domain;
> + __le64 virt_start;
> + __le64 virt_end;
> + __u8 reserved[4];
> + struct virtio_iommu_req_tail tail;
> +};
> +
> +#endif
> --
> 2.19.1
>
> _______________________________________________
> Virtualization mailing list
> Virtualization@lists.linux-foundation.org
> https://lists.linuxfoundation.org/mailman/listinfo/virtualization
^ permalink raw reply
* Re: [PATCH v5 5/7] iommu: Add virtio-iommu driver
From: Michael S. Tsirkin @ 2018-11-23 21:56 UTC (permalink / raw)
To: Jean-Philippe Brucker
Cc: mark.rutland, virtio-dev, lorenzo.pieralisi, tnowicki, devicetree,
marc.zyngier, linux-pci, joro, will.deacon, virtualization,
eric.auger, iommu, robh+dt, bhelgaas, robin.murphy, kvmarm
In-Reply-To: <20181122193801.50510-6-jean-philippe.brucker@arm.com>
On Thu, Nov 22, 2018 at 07:37:59PM +0000, Jean-Philippe Brucker wrote:
> The virtio IOMMU is a para-virtualized device, allowing to send IOMMU
> requests such as map/unmap over virtio transport without emulating page
> tables. This implementation handles ATTACH, DETACH, MAP and UNMAP
> requests.
>
> The bulk of the code transforms calls coming from the IOMMU API into
> corresponding virtio requests. Mappings are kept in an interval tree
> instead of page tables.
>
> Signed-off-by: Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
> ---
> MAINTAINERS | 7 +
> drivers/iommu/Kconfig | 11 +
> drivers/iommu/Makefile | 1 +
> drivers/iommu/virtio-iommu.c | 916 ++++++++++++++++++++++++++++++
> include/uapi/linux/virtio_ids.h | 1 +
> include/uapi/linux/virtio_iommu.h | 104 ++++
> 6 files changed, 1040 insertions(+)
> create mode 100644 drivers/iommu/virtio-iommu.c
> create mode 100644 include/uapi/linux/virtio_iommu.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 1689dcfec800..3d8550c76f4a 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -15946,6 +15946,13 @@ S: Maintained
> F: drivers/virtio/virtio_input.c
> F: include/uapi/linux/virtio_input.h
>
> +VIRTIO IOMMU DRIVER
> +M: Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
> +L: virtualization@lists.linux-foundation.org
> +S: Maintained
> +F: drivers/iommu/virtio-iommu.c
> +F: include/uapi/linux/virtio_iommu.h
> +
> VIRTUAL BOX GUEST DEVICE DRIVER
> M: Hans de Goede <hdegoede@redhat.com>
> M: Arnd Bergmann <arnd@arndb.de>
> diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
> index bf2bbfa2a399..db5f2b8c23f5 100644
> --- a/drivers/iommu/Kconfig
> +++ b/drivers/iommu/Kconfig
> @@ -464,4 +464,15 @@ config QCOM_IOMMU
> help
> Support for IOMMU on certain Qualcomm SoCs.
>
> +config VIRTIO_IOMMU
> + bool "Virtio IOMMU driver"
> + depends on VIRTIO=y
> + select IOMMU_API
> + select INTERVAL_TREE
> + select ARM_DMA_USE_IOMMU if ARM
> + help
> + Para-virtualised IOMMU driver with virtio.
> +
> + Say Y here if you intend to run this kernel as a guest.
> +
Given it is arm specific right now, shouldn't this depend on ARM?
E.g. there's a hack for x86 right now.
> endif # IOMMU_SUPPORT
> diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
> index 5481e5fe1f95..bd7e55751d09 100644
> --- a/drivers/iommu/Makefile
> +++ b/drivers/iommu/Makefile
> @@ -36,3 +36,4 @@ obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o
> obj-$(CONFIG_FSL_PAMU) += fsl_pamu.o fsl_pamu_domain.o
> obj-$(CONFIG_S390_IOMMU) += s390-iommu.o
> obj-$(CONFIG_QCOM_IOMMU) += qcom_iommu.o
> +obj-$(CONFIG_VIRTIO_IOMMU) += virtio-iommu.o
> diff --git a/drivers/iommu/virtio-iommu.c b/drivers/iommu/virtio-iommu.c
> new file mode 100644
> index 000000000000..7540dab9c8dc
> --- /dev/null
> +++ b/drivers/iommu/virtio-iommu.c
> @@ -0,0 +1,916 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Virtio driver for the paravirtualized IOMMU
> + *
> + * Copyright (C) 2018 Arm Limited
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include <linux/amba/bus.h>
> +#include <linux/delay.h>
> +#include <linux/dma-iommu.h>
> +#include <linux/freezer.h>
> +#include <linux/interval_tree.h>
> +#include <linux/iommu.h>
> +#include <linux/module.h>
> +#include <linux/of_iommu.h>
> +#include <linux/of_platform.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/virtio.h>
> +#include <linux/virtio_config.h>
> +#include <linux/virtio_ids.h>
> +#include <linux/wait.h>
> +
> +#include <uapi/linux/virtio_iommu.h>
> +
> +#define MSI_IOVA_BASE 0x8000000
> +#define MSI_IOVA_LENGTH 0x100000
> +
> +#define VIOMMU_REQUEST_VQ 0
> +#define VIOMMU_NR_VQS 1
> +
> +struct viommu_dev {
> + struct iommu_device iommu;
> + struct device *dev;
> + struct virtio_device *vdev;
> +
> + struct ida domain_ids;
> +
> + struct virtqueue *vqs[VIOMMU_NR_VQS];
> + spinlock_t request_lock;
> + struct list_head requests;
> +
> + /* Device configuration */
> + struct iommu_domain_geometry geometry;
> + u64 pgsize_bitmap;
> + u8 domain_bits;
> +};
> +
> +struct viommu_mapping {
> + phys_addr_t paddr;
> + struct interval_tree_node iova;
> + u32 flags;
> +};
> +
> +struct viommu_domain {
> + struct iommu_domain domain;
> + struct viommu_dev *viommu;
> + struct mutex mutex; /* protects viommu pointer */
> + unsigned int id;
> +
> + spinlock_t mappings_lock;
> + struct rb_root_cached mappings;
> +
> + unsigned long nr_endpoints;
> +};
> +
> +struct viommu_endpoint {
> + struct viommu_dev *viommu;
> + struct viommu_domain *vdomain;
> +};
> +
> +struct viommu_request {
> + struct list_head list;
> + void *writeback;
> + unsigned int write_offset;
> + unsigned int len;
> + char buf[];
> +};
> +
> +#define to_viommu_domain(domain) \
> + container_of(domain, struct viommu_domain, domain)
> +
> +static int viommu_get_req_errno(void *buf, size_t len)
> +{
> + struct virtio_iommu_req_tail *tail = buf + len - sizeof(*tail);
> +
> + switch (tail->status) {
> + case VIRTIO_IOMMU_S_OK:
> + return 0;
> + case VIRTIO_IOMMU_S_UNSUPP:
> + return -ENOSYS;
> + case VIRTIO_IOMMU_S_INVAL:
> + return -EINVAL;
> + case VIRTIO_IOMMU_S_RANGE:
> + return -ERANGE;
> + case VIRTIO_IOMMU_S_NOENT:
> + return -ENOENT;
> + case VIRTIO_IOMMU_S_FAULT:
> + return -EFAULT;
> + case VIRTIO_IOMMU_S_IOERR:
> + case VIRTIO_IOMMU_S_DEVERR:
> + default:
> + return -EIO;
> + }
> +}
> +
> +static void viommu_set_req_status(void *buf, size_t len, int status)
> +{
> + struct virtio_iommu_req_tail *tail = buf + len - sizeof(*tail);
> +
> + tail->status = status;
> +}
> +
> +static off_t viommu_get_write_desc_offset(struct viommu_dev *viommu,
> + struct virtio_iommu_req_head *req,
> + size_t len)
> +{
> + size_t tail_size = sizeof(struct virtio_iommu_req_tail);
> +
> + return len - tail_size;
> +}
> +
> +/*
> + * __viommu_sync_req - Complete all in-flight requests
> + *
> + * Wait for all added requests to complete. When this function returns, all
> + * requests that were in-flight at the time of the call have completed.
> + */
> +static int __viommu_sync_req(struct viommu_dev *viommu)
> +{
> + int ret = 0;
> + unsigned int len;
> + size_t write_len;
> + struct viommu_request *req;
> + struct virtqueue *vq = viommu->vqs[VIOMMU_REQUEST_VQ];
> +
> + assert_spin_locked(&viommu->request_lock);
> +
> + virtqueue_kick(vq);
> +
> + while (!list_empty(&viommu->requests)) {
> + len = 0;
> + req = virtqueue_get_buf(vq, &len);
> + if (!req)
> + continue;
> +
> + if (!len)
> + viommu_set_req_status(req->buf, req->len,
> + VIRTIO_IOMMU_S_IOERR);
> +
> + write_len = req->len - req->write_offset;
> + if (req->writeback && len == write_len)
> + memcpy(req->writeback, req->buf + req->write_offset,
> + write_len);
> +
> + list_del(&req->list);
> + kfree(req);
> + }
> +
> + return ret;
> +}
> +
> +static int viommu_sync_req(struct viommu_dev *viommu)
> +{
> + int ret;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&viommu->request_lock, flags);
> + ret = __viommu_sync_req(viommu);
> + if (ret)
> + dev_dbg(viommu->dev, "could not sync requests (%d)\n", ret);
> + spin_unlock_irqrestore(&viommu->request_lock, flags);
> +
> + return ret;
> +}
> +
> +/*
> + * __viommu_add_request - Add one request to the queue
> + * @buf: pointer to the request buffer
> + * @len: length of the request buffer
> + * @writeback: copy data back to the buffer when the request completes.
> + *
> + * Add a request to the queue. Only synchronize the queue if it's already full.
> + * Otherwise don't kick the queue nor wait for requests to complete.
> + *
> + * When @writeback is true, data written by the device, including the request
> + * status, is copied into @buf after the request completes. This is unsafe if
> + * the caller allocates @buf on stack and drops the lock between add_req() and
> + * sync_req().
> + *
> + * Return 0 if the request was successfully added to the queue.
> + */
> +static int __viommu_add_req(struct viommu_dev *viommu, void *buf, size_t len,
> + bool writeback)
> +{
> + int ret;
> + off_t write_offset;
> + struct viommu_request *req;
> + struct scatterlist top_sg, bottom_sg;
> + struct scatterlist *sg[2] = { &top_sg, &bottom_sg };
> + struct virtqueue *vq = viommu->vqs[VIOMMU_REQUEST_VQ];
> +
> + assert_spin_locked(&viommu->request_lock);
> +
> + write_offset = viommu_get_write_desc_offset(viommu, buf, len);
> + if (write_offset <= 0)
> + return -EINVAL;
> +
> + req = kzalloc(sizeof(*req) + len, GFP_ATOMIC);
> + if (!req)
> + return -ENOMEM;
> +
> + req->len = len;
> + if (writeback) {
> + req->writeback = buf + write_offset;
> + req->write_offset = write_offset;
> + }
> + memcpy(&req->buf, buf, write_offset);
> +
> + sg_init_one(&top_sg, req->buf, write_offset);
> + sg_init_one(&bottom_sg, req->buf + write_offset, len - write_offset);
> +
> + ret = virtqueue_add_sgs(vq, sg, 1, 1, req, GFP_ATOMIC);
> + if (ret == -ENOSPC) {
> + /* If the queue is full, sync and retry */
> + if (!__viommu_sync_req(viommu))
> + ret = virtqueue_add_sgs(vq, sg, 1, 1, req, GFP_ATOMIC);
> + }
> + if (ret)
> + goto err_free;
> +
> + list_add_tail(&req->list, &viommu->requests);
> + return 0;
> +
> +err_free:
> + kfree(req);
> + return ret;
> +}
> +
> +static int viommu_add_req(struct viommu_dev *viommu, void *buf, size_t len)
> +{
> + int ret;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&viommu->request_lock, flags);
> + ret = __viommu_add_req(viommu, buf, len, false);
> + if (ret)
> + dev_dbg(viommu->dev, "could not add request: %d\n", ret);
> + spin_unlock_irqrestore(&viommu->request_lock, flags);
> +
> + return ret;
> +}
> +
> +/*
> + * Send a request and wait for it to complete. Return the request status (as an
> + * errno)
> + */
> +static int viommu_send_req_sync(struct viommu_dev *viommu, void *buf,
> + size_t len)
> +{
> + int ret;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&viommu->request_lock, flags);
> +
> + ret = __viommu_add_req(viommu, buf, len, true);
> + if (ret) {
> + dev_dbg(viommu->dev, "could not add request (%d)\n", ret);
> + goto out_unlock;
> + }
> +
> + ret = __viommu_sync_req(viommu);
> + if (ret) {
> + dev_dbg(viommu->dev, "could not sync requests (%d)\n", ret);
> + /* Fall-through (get the actual request status) */
> + }
> +
> + ret = viommu_get_req_errno(buf, len);
> +out_unlock:
> + spin_unlock_irqrestore(&viommu->request_lock, flags);
> + return ret;
> +}
> +
> +/*
> + * viommu_add_mapping - add a mapping to the internal tree
> + *
> + * On success, return the new mapping. Otherwise return NULL.
> + */
> +static int viommu_add_mapping(struct viommu_domain *vdomain, unsigned long iova,
> + phys_addr_t paddr, size_t size, u32 flags)
> +{
> + unsigned long irqflags;
> + struct viommu_mapping *mapping;
> +
> + mapping = kzalloc(sizeof(*mapping), GFP_ATOMIC);
> + if (!mapping)
> + return -ENOMEM;
> +
> + mapping->paddr = paddr;
> + mapping->iova.start = iova;
> + mapping->iova.last = iova + size - 1;
> + mapping->flags = flags;
> +
> + spin_lock_irqsave(&vdomain->mappings_lock, irqflags);
> + interval_tree_insert(&mapping->iova, &vdomain->mappings);
> + spin_unlock_irqrestore(&vdomain->mappings_lock, irqflags);
> +
> + return 0;
> +}
> +
> +/*
> + * viommu_del_mappings - remove mappings from the internal tree
> + *
> + * @vdomain: the domain
> + * @iova: start of the range
> + * @size: size of the range. A size of 0 corresponds to the entire address
> + * space.
> + *
> + * On success, returns the number of unmapped bytes (>= size)
> + */
> +static size_t viommu_del_mappings(struct viommu_domain *vdomain,
> + unsigned long iova, size_t size)
> +{
> + size_t unmapped = 0;
> + unsigned long flags;
> + unsigned long last = iova + size - 1;
> + struct viommu_mapping *mapping = NULL;
> + struct interval_tree_node *node, *next;
> +
> + spin_lock_irqsave(&vdomain->mappings_lock, flags);
> + next = interval_tree_iter_first(&vdomain->mappings, iova, last);
> + while (next) {
> + node = next;
> + mapping = container_of(node, struct viommu_mapping, iova);
> + next = interval_tree_iter_next(node, iova, last);
> +
> + /* Trying to split a mapping? */
> + if (mapping->iova.start < iova)
> + break;
> +
> + /*
> + * Virtio-iommu doesn't allow UNMAP to split a mapping created
> + * with a single MAP request, so remove the full mapping.
> + */
> + unmapped += mapping->iova.last - mapping->iova.start + 1;
> +
> + interval_tree_remove(node, &vdomain->mappings);
> + kfree(mapping);
> + }
> + spin_unlock_irqrestore(&vdomain->mappings_lock, flags);
> +
> + return unmapped;
> +}
> +
> +/*
> + * viommu_replay_mappings - re-send MAP requests
> + *
> + * When reattaching a domain that was previously detached from all endpoints,
> + * mappings were deleted from the device. Re-create the mappings available in
> + * the internal tree.
> + */
> +static int viommu_replay_mappings(struct viommu_domain *vdomain)
> +{
> + int ret = 0;
> + unsigned long flags;
> + struct viommu_mapping *mapping;
> + struct interval_tree_node *node;
> + struct virtio_iommu_req_map map;
> +
> + spin_lock_irqsave(&vdomain->mappings_lock, flags);
> + node = interval_tree_iter_first(&vdomain->mappings, 0, -1UL);
> + while (node) {
> + mapping = container_of(node, struct viommu_mapping, iova);
> + map = (struct virtio_iommu_req_map) {
> + .head.type = VIRTIO_IOMMU_T_MAP,
> + .domain = cpu_to_le32(vdomain->id),
> + .virt_start = cpu_to_le64(mapping->iova.start),
> + .virt_end = cpu_to_le64(mapping->iova.last),
> + .phys_start = cpu_to_le64(mapping->paddr),
> + .flags = cpu_to_le32(mapping->flags),
> + };
> +
> + ret = viommu_send_req_sync(vdomain->viommu, &map, sizeof(map));
> + if (ret)
> + break;
> +
> + node = interval_tree_iter_next(node, 0, -1UL);
> + }
> + spin_unlock_irqrestore(&vdomain->mappings_lock, flags);
> +
> + return ret;
> +}
> +
> +/* IOMMU API */
> +
> +static struct iommu_domain *viommu_domain_alloc(unsigned type)
> +{
> + struct viommu_domain *vdomain;
> +
> + if (type != IOMMU_DOMAIN_UNMANAGED && type != IOMMU_DOMAIN_DMA)
> + return NULL;
> +
> + vdomain = kzalloc(sizeof(*vdomain), GFP_KERNEL);
> + if (!vdomain)
> + return NULL;
> +
> + mutex_init(&vdomain->mutex);
> + spin_lock_init(&vdomain->mappings_lock);
> + vdomain->mappings = RB_ROOT_CACHED;
> +
> + if (type == IOMMU_DOMAIN_DMA &&
> + iommu_get_dma_cookie(&vdomain->domain)) {
> + kfree(vdomain);
> + return NULL;
> + }
> +
> + return &vdomain->domain;
> +}
> +
> +static int viommu_domain_finalise(struct viommu_dev *viommu,
> + struct iommu_domain *domain)
> +{
> + int ret;
> + struct viommu_domain *vdomain = to_viommu_domain(domain);
> + unsigned int max_domain = viommu->domain_bits > 31 ? ~0 :
> + (1U << viommu->domain_bits) - 1;
> +
> + vdomain->viommu = viommu;
> +
> + domain->pgsize_bitmap = viommu->pgsize_bitmap;
> + domain->geometry = viommu->geometry;
> +
> + ret = ida_alloc_max(&viommu->domain_ids, max_domain, GFP_KERNEL);
> + if (ret >= 0)
> + vdomain->id = (unsigned int)ret;
> +
> + return ret > 0 ? 0 : ret;
> +}
> +
> +static void viommu_domain_free(struct iommu_domain *domain)
> +{
> + struct viommu_domain *vdomain = to_viommu_domain(domain);
> +
> + iommu_put_dma_cookie(domain);
> +
> + /* Free all remaining mappings (size 2^64) */
> + viommu_del_mappings(vdomain, 0, 0);
> +
> + if (vdomain->viommu)
> + ida_free(&vdomain->viommu->domain_ids, vdomain->id);
> +
> + kfree(vdomain);
> +}
> +
> +static int viommu_attach_dev(struct iommu_domain *domain, struct device *dev)
> +{
> + int i;
> + int ret = 0;
> + struct virtio_iommu_req_attach req;
> + struct iommu_fwspec *fwspec = dev->iommu_fwspec;
> + struct viommu_endpoint *vdev = fwspec->iommu_priv;
> + struct viommu_domain *vdomain = to_viommu_domain(domain);
> +
> + mutex_lock(&vdomain->mutex);
> + if (!vdomain->viommu) {
> + /*
> + * Properly initialize the domain now that we know which viommu
> + * owns it.
> + */
> + ret = viommu_domain_finalise(vdev->viommu, domain);
> + } else if (vdomain->viommu != vdev->viommu) {
> + dev_err(dev, "cannot attach to foreign vIOMMU\n");
> + ret = -EXDEV;
> + }
> + mutex_unlock(&vdomain->mutex);
> +
> + if (ret)
> + return ret;
> +
> + /*
> + * In the virtio-iommu device, when attaching the endpoint to a new
> + * domain, it is detached from the old one and, if as as a result the
> + * old domain isn't attached to any endpoint, all mappings are removed
> + * from the old domain and it is freed.
> + *
> + * In the driver the old domain still exists, and its mappings will be
> + * recreated if it gets reattached to an endpoint. Otherwise it will be
> + * freed explicitly.
> + *
> + * vdev->vdomain is protected by group->mutex
> + */
> + if (vdev->vdomain)
> + vdev->vdomain->nr_endpoints--;
> +
> + req = (struct virtio_iommu_req_attach) {
> + .head.type = VIRTIO_IOMMU_T_ATTACH,
> + .domain = cpu_to_le32(vdomain->id),
> + };
> +
> + for (i = 0; i < fwspec->num_ids; i++) {
> + req.endpoint = cpu_to_le32(fwspec->ids[i]);
> +
> + ret = viommu_send_req_sync(vdomain->viommu, &req, sizeof(req));
> + if (ret)
> + return ret;
> + }
> +
> + if (!vdomain->nr_endpoints) {
> + /*
> + * This endpoint is the first to be attached to the domain.
> + * Replay existing mappings (e.g. SW MSI).
> + */
> + ret = viommu_replay_mappings(vdomain);
> + if (ret)
> + return ret;
> + }
> +
> + vdomain->nr_endpoints++;
> + vdev->vdomain = vdomain;
> +
> + return 0;
> +}
> +
> +static int viommu_map(struct iommu_domain *domain, unsigned long iova,
> + phys_addr_t paddr, size_t size, int prot)
> +{
> + int ret;
> + int flags;
> + struct virtio_iommu_req_map map;
> + struct viommu_domain *vdomain = to_viommu_domain(domain);
> +
> + flags = (prot & IOMMU_READ ? VIRTIO_IOMMU_MAP_F_READ : 0) |
> + (prot & IOMMU_WRITE ? VIRTIO_IOMMU_MAP_F_WRITE : 0) |
> + (prot & IOMMU_MMIO ? VIRTIO_IOMMU_MAP_F_MMIO : 0);
> +
> + ret = viommu_add_mapping(vdomain, iova, paddr, size, flags);
> + if (ret)
> + return ret;
> +
> + map = (struct virtio_iommu_req_map) {
> + .head.type = VIRTIO_IOMMU_T_MAP,
> + .domain = cpu_to_le32(vdomain->id),
> + .virt_start = cpu_to_le64(iova),
> + .phys_start = cpu_to_le64(paddr),
> + .virt_end = cpu_to_le64(iova + size - 1),
> + .flags = cpu_to_le32(flags),
> + };
> +
> + if (!vdomain->nr_endpoints)
> + return 0;
> +
> + ret = viommu_send_req_sync(vdomain->viommu, &map, sizeof(map));
> + if (ret)
> + viommu_del_mappings(vdomain, iova, size);
> +
> + return ret;
> +}
> +
> +static size_t viommu_unmap(struct iommu_domain *domain, unsigned long iova,
> + size_t size)
> +{
> + int ret = 0;
> + size_t unmapped;
> + struct virtio_iommu_req_unmap unmap;
> + struct viommu_domain *vdomain = to_viommu_domain(domain);
> +
> + unmapped = viommu_del_mappings(vdomain, iova, size);
> + if (unmapped < size)
> + return 0;
> +
> + /* Device already removed all mappings after detach. */
> + if (!vdomain->nr_endpoints)
> + return unmapped;
> +
> + unmap = (struct virtio_iommu_req_unmap) {
> + .head.type = VIRTIO_IOMMU_T_UNMAP,
> + .domain = cpu_to_le32(vdomain->id),
> + .virt_start = cpu_to_le64(iova),
> + .virt_end = cpu_to_le64(iova + unmapped - 1),
> + };
> +
> + ret = viommu_add_req(vdomain->viommu, &unmap, sizeof(unmap));
> + return ret ? 0 : unmapped;
> +}
> +
> +static phys_addr_t viommu_iova_to_phys(struct iommu_domain *domain,
> + dma_addr_t iova)
> +{
> + u64 paddr = 0;
> + unsigned long flags;
> + struct viommu_mapping *mapping;
> + struct interval_tree_node *node;
> + struct viommu_domain *vdomain = to_viommu_domain(domain);
> +
> + spin_lock_irqsave(&vdomain->mappings_lock, flags);
> + node = interval_tree_iter_first(&vdomain->mappings, iova, iova);
> + if (node) {
> + mapping = container_of(node, struct viommu_mapping, iova);
> + paddr = mapping->paddr + (iova - mapping->iova.start);
> + }
> + spin_unlock_irqrestore(&vdomain->mappings_lock, flags);
> +
> + return paddr;
> +}
> +
> +static void viommu_iotlb_sync(struct iommu_domain *domain)
> +{
> + struct viommu_domain *vdomain = to_viommu_domain(domain);
> +
> + viommu_sync_req(vdomain->viommu);
> +}
> +
> +static void viommu_get_resv_regions(struct device *dev, struct list_head *head)
> +{
> + struct iommu_resv_region *region;
> + int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
> +
> + region = iommu_alloc_resv_region(MSI_IOVA_BASE, MSI_IOVA_LENGTH, prot,
> + IOMMU_RESV_SW_MSI);
> + if (!region)
> + return;
> +
> + list_add_tail(®ion->list, head);
> + iommu_dma_get_resv_regions(dev, head);
> +}
> +
> +static void viommu_put_resv_regions(struct device *dev, struct list_head *head)
> +{
> + struct iommu_resv_region *entry, *next;
> +
> + list_for_each_entry_safe(entry, next, head, list)
> + kfree(entry);
> +}
> +
> +static struct iommu_ops viommu_ops;
> +static struct virtio_driver virtio_iommu_drv;
> +
> +static int viommu_match_node(struct device *dev, void *data)
> +{
> + return dev->parent->fwnode == data;
> +}
> +
> +static struct viommu_dev *viommu_get_by_fwnode(struct fwnode_handle *fwnode)
> +{
> + struct device *dev = driver_find_device(&virtio_iommu_drv.driver, NULL,
> + fwnode, viommu_match_node);
> + put_device(dev);
> +
> + return dev ? dev_to_virtio(dev)->priv : NULL;
> +}
> +
> +static int viommu_add_device(struct device *dev)
> +{
> + int ret;
> + struct iommu_group *group;
> + struct viommu_endpoint *vdev;
> + struct viommu_dev *viommu = NULL;
> + struct iommu_fwspec *fwspec = dev->iommu_fwspec;
> +
> + if (!fwspec || fwspec->ops != &viommu_ops)
> + return -ENODEV;
> +
> + viommu = viommu_get_by_fwnode(fwspec->iommu_fwnode);
> + if (!viommu)
> + return -ENODEV;
> +
> + vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
> + if (!vdev)
> + return -ENOMEM;
> +
> + vdev->viommu = viommu;
> + fwspec->iommu_priv = vdev;
> +
> + ret = iommu_device_link(&viommu->iommu, dev);
> + if (ret)
> + goto err_free_dev;
> +
> + /*
> + * Last step creates a default domain and attaches to it. Everything
> + * must be ready.
> + */
> + group = iommu_group_get_for_dev(dev);
> + if (IS_ERR(group)) {
> + ret = PTR_ERR(group);
> + goto err_unlink_dev;
> + }
> +
> + iommu_group_put(group);
> +
> + return PTR_ERR_OR_ZERO(group);
> +
> +err_unlink_dev:
> + iommu_device_unlink(&viommu->iommu, dev);
> +err_free_dev:
> + kfree(vdev);
> +
> + return ret;
> +}
> +
> +static void viommu_remove_device(struct device *dev)
> +{
> + struct viommu_endpoint *vdev;
> + struct iommu_fwspec *fwspec = dev->iommu_fwspec;
> +
> + if (!fwspec || fwspec->ops != &viommu_ops)
> + return;
> +
> + vdev = fwspec->iommu_priv;
> +
> + iommu_group_remove_device(dev);
> + iommu_device_unlink(&vdev->viommu->iommu, dev);
> + kfree(vdev);
> +}
> +
> +static struct iommu_group *viommu_device_group(struct device *dev)
> +{
> + if (dev_is_pci(dev))
> + return pci_device_group(dev);
> + else
> + return generic_device_group(dev);
> +}
> +
> +static int viommu_of_xlate(struct device *dev, struct of_phandle_args *args)
> +{
> + return iommu_fwspec_add_ids(dev, args->args, 1);
> +}
> +
> +static struct iommu_ops viommu_ops = {
> + .domain_alloc = viommu_domain_alloc,
> + .domain_free = viommu_domain_free,
> + .attach_dev = viommu_attach_dev,
> + .map = viommu_map,
> + .unmap = viommu_unmap,
> + .iova_to_phys = viommu_iova_to_phys,
> + .iotlb_sync = viommu_iotlb_sync,
> + .add_device = viommu_add_device,
> + .remove_device = viommu_remove_device,
> + .device_group = viommu_device_group,
> + .get_resv_regions = viommu_get_resv_regions,
> + .put_resv_regions = viommu_put_resv_regions,
> + .of_xlate = viommu_of_xlate,
> +};
> +
> +static int viommu_init_vqs(struct viommu_dev *viommu)
> +{
> + struct virtio_device *vdev = dev_to_virtio(viommu->dev);
> + const char *name = "request";
> + void *ret;
> +
> + ret = virtio_find_single_vq(vdev, NULL, name);
> + if (IS_ERR(ret)) {
> + dev_err(viommu->dev, "cannot find VQ\n");
> + return PTR_ERR(ret);
> + }
> +
> + viommu->vqs[VIOMMU_REQUEST_VQ] = ret;
> +
> + return 0;
> +}
> +
> +static int viommu_probe(struct virtio_device *vdev)
> +{
> + struct device *parent_dev = vdev->dev.parent;
> + struct viommu_dev *viommu = NULL;
> + struct device *dev = &vdev->dev;
> + u64 input_start = 0;
> + u64 input_end = -1UL;
> + int ret;
> +
> + if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1) ||
> + !virtio_has_feature(vdev, VIRTIO_IOMMU_F_MAP_UNMAP))
Why bother with a feature bit for this then btw?
> + return -ENODEV;
> +
> + viommu = devm_kzalloc(dev, sizeof(*viommu), GFP_KERNEL);
> + if (!viommu)
> + return -ENOMEM;
> +
> + spin_lock_init(&viommu->request_lock);
> + ida_init(&viommu->domain_ids);
> + viommu->dev = dev;
> + viommu->vdev = vdev;
> + INIT_LIST_HEAD(&viommu->requests);
> +
> + ret = viommu_init_vqs(viommu);
> + if (ret)
> + return ret;
> +
> + virtio_cread(vdev, struct virtio_iommu_config, page_size_mask,
> + &viommu->pgsize_bitmap);
> +
> + if (!viommu->pgsize_bitmap) {
> + ret = -EINVAL;
> + goto err_free_vqs;
> + }
> +
> + viommu->domain_bits = 32;
> +
> + /* Optional features */
> + virtio_cread_feature(vdev, VIRTIO_IOMMU_F_INPUT_RANGE,
> + struct virtio_iommu_config, input_range.start,
> + &input_start);
> +
> + virtio_cread_feature(vdev, VIRTIO_IOMMU_F_INPUT_RANGE,
> + struct virtio_iommu_config, input_range.end,
> + &input_end);
> +
> + virtio_cread_feature(vdev, VIRTIO_IOMMU_F_DOMAIN_BITS,
> + struct virtio_iommu_config, domain_bits,
> + &viommu->domain_bits);
> +
> + viommu->geometry = (struct iommu_domain_geometry) {
> + .aperture_start = input_start,
> + .aperture_end = input_end,
> + .force_aperture = true,
> + };
> +
> + viommu_ops.pgsize_bitmap = viommu->pgsize_bitmap;
> +
> + virtio_device_ready(vdev);
> +
> + ret = iommu_device_sysfs_add(&viommu->iommu, dev, NULL, "%s",
> + virtio_bus_name(vdev));
> + if (ret)
> + goto err_free_vqs;
> +
> + iommu_device_set_ops(&viommu->iommu, &viommu_ops);
> + iommu_device_set_fwnode(&viommu->iommu, parent_dev->fwnode);
> +
> + iommu_device_register(&viommu->iommu);
> +
> +#ifdef CONFIG_PCI
> + if (pci_bus_type.iommu_ops != &viommu_ops) {
> + pci_request_acs();
> + ret = bus_set_iommu(&pci_bus_type, &viommu_ops);
> + if (ret)
> + goto err_unregister;
> + }
> +#endif
> +#ifdef CONFIG_ARM_AMBA
> + if (amba_bustype.iommu_ops != &viommu_ops) {
> + ret = bus_set_iommu(&amba_bustype, &viommu_ops);
> + if (ret)
> + goto err_unregister;
> + }
> +#endif
> + if (platform_bus_type.iommu_ops != &viommu_ops) {
> + ret = bus_set_iommu(&platform_bus_type, &viommu_ops);
> + if (ret)
> + goto err_unregister;
> + }
> +
> + vdev->priv = viommu;
> +
> + dev_info(dev, "input address: %u bits\n",
> + order_base_2(viommu->geometry.aperture_end));
> + dev_info(dev, "page mask: %#llx\n", viommu->pgsize_bitmap);
> +
> + return 0;
> +
> +err_unregister:
> + iommu_device_sysfs_remove(&viommu->iommu);
> + iommu_device_unregister(&viommu->iommu);
> +err_free_vqs:
> + vdev->config->del_vqs(vdev);
> +
> + return ret;
> +}
> +
> +static void viommu_remove(struct virtio_device *vdev)
> +{
> + struct viommu_dev *viommu = vdev->priv;
> +
> + iommu_device_sysfs_remove(&viommu->iommu);
> + iommu_device_unregister(&viommu->iommu);
> +
> + /* Stop all virtqueues */
> + vdev->config->reset(vdev);
> + vdev->config->del_vqs(vdev);
> +
> + dev_info(&vdev->dev, "device removed\n");
> +}
> +
> +static void viommu_config_changed(struct virtio_device *vdev)
> +{
> + dev_warn(&vdev->dev, "config changed\n");
> +}
> +
> +static unsigned int features[] = {
> + VIRTIO_IOMMU_F_MAP_UNMAP,
> + VIRTIO_IOMMU_F_DOMAIN_BITS,
> + VIRTIO_IOMMU_F_INPUT_RANGE,
> +};
> +
> +static struct virtio_device_id id_table[] = {
> + { VIRTIO_ID_IOMMU, VIRTIO_DEV_ANY_ID },
> + { 0 },
> +};
> +
> +static struct virtio_driver virtio_iommu_drv = {
> + .driver.name = KBUILD_MODNAME,
> + .driver.owner = THIS_MODULE,
> + .id_table = id_table,
> + .feature_table = features,
> + .feature_table_size = ARRAY_SIZE(features),
> + .probe = viommu_probe,
> + .remove = viommu_remove,
> + .config_changed = viommu_config_changed,
> +};
> +
> +module_virtio_driver(virtio_iommu_drv);
> +
> +MODULE_DESCRIPTION("Virtio IOMMU driver");
> +MODULE_AUTHOR("Jean-Philippe Brucker <jean-philippe.brucker@arm.com>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h
> index 6d5c3b2d4f4d..cfe47c5d9a56 100644
> --- a/include/uapi/linux/virtio_ids.h
> +++ b/include/uapi/linux/virtio_ids.h
> @@ -43,5 +43,6 @@
> #define VIRTIO_ID_INPUT 18 /* virtio input */
> #define VIRTIO_ID_VSOCK 19 /* virtio vsock transport */
> #define VIRTIO_ID_CRYPTO 20 /* virtio crypto */
> +#define VIRTIO_ID_IOMMU 23 /* virtio IOMMU */
>
> #endif /* _LINUX_VIRTIO_IDS_H */
> diff --git a/include/uapi/linux/virtio_iommu.h b/include/uapi/linux/virtio_iommu.h
> new file mode 100644
> index 000000000000..e7c05e3afa44
> --- /dev/null
> +++ b/include/uapi/linux/virtio_iommu.h
> @@ -0,0 +1,104 @@
> +/* SPDX-License-Identifier: BSD-3-Clause */
> +/*
> + * Virtio-iommu definition v0.9
> + *
> + * Copyright (C) 2018 Arm Ltd.
> + */
> +#ifndef _UAPI_LINUX_VIRTIO_IOMMU_H
> +#define _UAPI_LINUX_VIRTIO_IOMMU_H
> +
> +#include <linux/types.h>
> +
> +/* Feature bits */
> +#define VIRTIO_IOMMU_F_INPUT_RANGE 0
> +#define VIRTIO_IOMMU_F_DOMAIN_BITS 1
> +#define VIRTIO_IOMMU_F_MAP_UNMAP 2
> +#define VIRTIO_IOMMU_F_BYPASS 3
> +
> +struct virtio_iommu_range {
> + __u64 start;
> + __u64 end;
> +};
> +
> +struct virtio_iommu_config {
> + /* Supported page sizes */
> + __u64 page_size_mask;
> + /* Supported IOVA range */
> + struct virtio_iommu_range input_range;
> + /* Max domain ID size */
> + __u8 domain_bits;
> + __u8 padding[3];
> +};
> +
> +/* Request types */
> +#define VIRTIO_IOMMU_T_ATTACH 0x01
> +#define VIRTIO_IOMMU_T_DETACH 0x02
> +#define VIRTIO_IOMMU_T_MAP 0x03
> +#define VIRTIO_IOMMU_T_UNMAP 0x04
> +
> +/* Status types */
> +#define VIRTIO_IOMMU_S_OK 0x00
> +#define VIRTIO_IOMMU_S_IOERR 0x01
> +#define VIRTIO_IOMMU_S_UNSUPP 0x02
> +#define VIRTIO_IOMMU_S_DEVERR 0x03
> +#define VIRTIO_IOMMU_S_INVAL 0x04
> +#define VIRTIO_IOMMU_S_RANGE 0x05
> +#define VIRTIO_IOMMU_S_NOENT 0x06
> +#define VIRTIO_IOMMU_S_FAULT 0x07
> +
> +struct virtio_iommu_req_head {
> + __u8 type;
> + __u8 reserved[3];
> +};
> +
> +struct virtio_iommu_req_tail {
> + __u8 status;
> + __u8 reserved[3];
> +};
> +
> +struct virtio_iommu_req_attach {
> + struct virtio_iommu_req_head head;
> + __le32 domain;
> + __le32 endpoint;
> + __u8 reserved[8];
> + struct virtio_iommu_req_tail tail;
> +};
> +
> +struct virtio_iommu_req_detach {
> + struct virtio_iommu_req_head head;
> + __le32 domain;
> + __le32 endpoint;
> + __u8 reserved[8];
> + struct virtio_iommu_req_tail tail;
> +};
> +
> +#define VIRTIO_IOMMU_MAP_F_READ (1 << 0)
> +#define VIRTIO_IOMMU_MAP_F_WRITE (1 << 1)
> +#define VIRTIO_IOMMU_MAP_F_EXEC (1 << 2)
> +#define VIRTIO_IOMMU_MAP_F_MMIO (1 << 3)
> +
> +#define VIRTIO_IOMMU_MAP_F_MASK (VIRTIO_IOMMU_MAP_F_READ | \
> + VIRTIO_IOMMU_MAP_F_WRITE | \
> + VIRTIO_IOMMU_MAP_F_EXEC | \
> + VIRTIO_IOMMU_MAP_F_MMIO)
> +
> +struct virtio_iommu_req_map {
> + struct virtio_iommu_req_head head;
> + __le32 domain;
> + __le64 virt_start;
> + __le64 virt_end;
> + __le64 phys_start;
> + __le32 flags;
> + struct virtio_iommu_req_tail tail;
> +};
> +
> +struct virtio_iommu_req_unmap {
> + struct virtio_iommu_req_head head;
> + __le32 domain;
> + __le64 virt_start;
> + __le64 virt_end;
> + __u8 reserved[4];
> + struct virtio_iommu_req_tail tail;
> +};
> +
> +#endif
> --
> 2.19.1
>
> _______________________________________________
> Virtualization mailing list
> Virtualization@lists.linux-foundation.org
> https://lists.linuxfoundation.org/mailman/listinfo/virtualization
^ permalink raw reply
* Re: [PATCH v5 5/7] iommu: Add virtio-iommu driver
From: Michael S. Tsirkin @ 2018-11-23 21:48 UTC (permalink / raw)
To: Jean-Philippe Brucker
Cc: mark.rutland, virtio-dev, lorenzo.pieralisi, tnowicki, devicetree,
marc.zyngier, linux-pci, joro, will.deacon, virtualization,
eric.auger, iommu, robh+dt, bhelgaas, robin.murphy, kvmarm
In-Reply-To: <20181122193801.50510-6-jean-philippe.brucker@arm.com>
On Thu, Nov 22, 2018 at 07:37:59PM +0000, Jean-Philippe Brucker wrote:
> The virtio IOMMU is a para-virtualized device, allowing to send IOMMU
> requests such as map/unmap over virtio transport without emulating page
> tables. This implementation handles ATTACH, DETACH, MAP and UNMAP
> requests.
>
> The bulk of the code transforms calls coming from the IOMMU API into
> corresponding virtio requests. Mappings are kept in an interval tree
> instead of page tables.
>
> Signed-off-by: Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
> ---
> MAINTAINERS | 7 +
> drivers/iommu/Kconfig | 11 +
> drivers/iommu/Makefile | 1 +
> drivers/iommu/virtio-iommu.c | 916 ++++++++++++++++++++++++++++++
> include/uapi/linux/virtio_ids.h | 1 +
> include/uapi/linux/virtio_iommu.h | 104 ++++
> 6 files changed, 1040 insertions(+)
> create mode 100644 drivers/iommu/virtio-iommu.c
> create mode 100644 include/uapi/linux/virtio_iommu.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 1689dcfec800..3d8550c76f4a 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -15946,6 +15946,13 @@ S: Maintained
> F: drivers/virtio/virtio_input.c
> F: include/uapi/linux/virtio_input.h
>
> +VIRTIO IOMMU DRIVER
> +M: Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
> +L: virtualization@lists.linux-foundation.org
> +S: Maintained
> +F: drivers/iommu/virtio-iommu.c
> +F: include/uapi/linux/virtio_iommu.h
> +
> VIRTUAL BOX GUEST DEVICE DRIVER
> M: Hans de Goede <hdegoede@redhat.com>
> M: Arnd Bergmann <arnd@arndb.de>
> diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
> index bf2bbfa2a399..db5f2b8c23f5 100644
> --- a/drivers/iommu/Kconfig
> +++ b/drivers/iommu/Kconfig
> @@ -464,4 +464,15 @@ config QCOM_IOMMU
> help
> Support for IOMMU on certain Qualcomm SoCs.
>
> +config VIRTIO_IOMMU
> + bool "Virtio IOMMU driver"
> + depends on VIRTIO=y
> + select IOMMU_API
> + select INTERVAL_TREE
> + select ARM_DMA_USE_IOMMU if ARM
> + help
> + Para-virtualised IOMMU driver with virtio.
> +
> + Say Y here if you intend to run this kernel as a guest.
> +
> endif # IOMMU_SUPPORT
> diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
> index 5481e5fe1f95..bd7e55751d09 100644
> --- a/drivers/iommu/Makefile
> +++ b/drivers/iommu/Makefile
> @@ -36,3 +36,4 @@ obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o
> obj-$(CONFIG_FSL_PAMU) += fsl_pamu.o fsl_pamu_domain.o
> obj-$(CONFIG_S390_IOMMU) += s390-iommu.o
> obj-$(CONFIG_QCOM_IOMMU) += qcom_iommu.o
> +obj-$(CONFIG_VIRTIO_IOMMU) += virtio-iommu.o
> diff --git a/drivers/iommu/virtio-iommu.c b/drivers/iommu/virtio-iommu.c
> new file mode 100644
> index 000000000000..7540dab9c8dc
> --- /dev/null
> +++ b/drivers/iommu/virtio-iommu.c
> @@ -0,0 +1,916 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Virtio driver for the paravirtualized IOMMU
> + *
> + * Copyright (C) 2018 Arm Limited
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include <linux/amba/bus.h>
> +#include <linux/delay.h>
> +#include <linux/dma-iommu.h>
> +#include <linux/freezer.h>
> +#include <linux/interval_tree.h>
> +#include <linux/iommu.h>
> +#include <linux/module.h>
> +#include <linux/of_iommu.h>
> +#include <linux/of_platform.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/virtio.h>
> +#include <linux/virtio_config.h>
> +#include <linux/virtio_ids.h>
> +#include <linux/wait.h>
> +
> +#include <uapi/linux/virtio_iommu.h>
> +
> +#define MSI_IOVA_BASE 0x8000000
> +#define MSI_IOVA_LENGTH 0x100000
> +
> +#define VIOMMU_REQUEST_VQ 0
> +#define VIOMMU_NR_VQS 1
> +
> +struct viommu_dev {
> + struct iommu_device iommu;
> + struct device *dev;
> + struct virtio_device *vdev;
> +
> + struct ida domain_ids;
> +
> + struct virtqueue *vqs[VIOMMU_NR_VQS];
> + spinlock_t request_lock;
> + struct list_head requests;
> +
> + /* Device configuration */
> + struct iommu_domain_geometry geometry;
> + u64 pgsize_bitmap;
> + u8 domain_bits;
> +};
> +
> +struct viommu_mapping {
> + phys_addr_t paddr;
> + struct interval_tree_node iova;
> + u32 flags;
> +};
> +
> +struct viommu_domain {
> + struct iommu_domain domain;
> + struct viommu_dev *viommu;
> + struct mutex mutex; /* protects viommu pointer */
> + unsigned int id;
> +
> + spinlock_t mappings_lock;
> + struct rb_root_cached mappings;
> +
> + unsigned long nr_endpoints;
> +};
> +
> +struct viommu_endpoint {
> + struct viommu_dev *viommu;
> + struct viommu_domain *vdomain;
> +};
> +
> +struct viommu_request {
> + struct list_head list;
> + void *writeback;
> + unsigned int write_offset;
> + unsigned int len;
> + char buf[];
> +};
> +
> +#define to_viommu_domain(domain) \
> + container_of(domain, struct viommu_domain, domain)
> +
> +static int viommu_get_req_errno(void *buf, size_t len)
> +{
> + struct virtio_iommu_req_tail *tail = buf + len - sizeof(*tail);
> +
> + switch (tail->status) {
> + case VIRTIO_IOMMU_S_OK:
> + return 0;
> + case VIRTIO_IOMMU_S_UNSUPP:
> + return -ENOSYS;
> + case VIRTIO_IOMMU_S_INVAL:
> + return -EINVAL;
> + case VIRTIO_IOMMU_S_RANGE:
> + return -ERANGE;
> + case VIRTIO_IOMMU_S_NOENT:
> + return -ENOENT;
> + case VIRTIO_IOMMU_S_FAULT:
> + return -EFAULT;
> + case VIRTIO_IOMMU_S_IOERR:
> + case VIRTIO_IOMMU_S_DEVERR:
> + default:
> + return -EIO;
> + }
> +}
> +
> +static void viommu_set_req_status(void *buf, size_t len, int status)
> +{
> + struct virtio_iommu_req_tail *tail = buf + len - sizeof(*tail);
> +
> + tail->status = status;
> +}
> +
> +static off_t viommu_get_write_desc_offset(struct viommu_dev *viommu,
> + struct virtio_iommu_req_head *req,
> + size_t len)
> +{
> + size_t tail_size = sizeof(struct virtio_iommu_req_tail);
> +
> + return len - tail_size;
> +}
> +
> +/*
> + * __viommu_sync_req - Complete all in-flight requests
> + *
> + * Wait for all added requests to complete. When this function returns, all
> + * requests that were in-flight at the time of the call have completed.
> + */
> +static int __viommu_sync_req(struct viommu_dev *viommu)
> +{
> + int ret = 0;
> + unsigned int len;
> + size_t write_len;
> + struct viommu_request *req;
> + struct virtqueue *vq = viommu->vqs[VIOMMU_REQUEST_VQ];
> +
> + assert_spin_locked(&viommu->request_lock);
> +
> + virtqueue_kick(vq);
> +
> + while (!list_empty(&viommu->requests)) {
> + len = 0;
> + req = virtqueue_get_buf(vq, &len);
> + if (!req)
> + continue;
> +
> + if (!len)
> + viommu_set_req_status(req->buf, req->len,
> + VIRTIO_IOMMU_S_IOERR);
> +
> + write_len = req->len - req->write_offset;
> + if (req->writeback && len == write_len)
> + memcpy(req->writeback, req->buf + req->write_offset,
> + write_len);
> +
> + list_del(&req->list);
> + kfree(req);
> + }
> +
> + return ret;
> +}
> +
> +static int viommu_sync_req(struct viommu_dev *viommu)
> +{
> + int ret;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&viommu->request_lock, flags);
> + ret = __viommu_sync_req(viommu);
> + if (ret)
> + dev_dbg(viommu->dev, "could not sync requests (%d)\n", ret);
> + spin_unlock_irqrestore(&viommu->request_lock, flags);
> +
> + return ret;
> +}
> +
> +/*
> + * __viommu_add_request - Add one request to the queue
> + * @buf: pointer to the request buffer
> + * @len: length of the request buffer
> + * @writeback: copy data back to the buffer when the request completes.
> + *
> + * Add a request to the queue. Only synchronize the queue if it's already full.
> + * Otherwise don't kick the queue nor wait for requests to complete.
> + *
> + * When @writeback is true, data written by the device, including the request
> + * status, is copied into @buf after the request completes. This is unsafe if
> + * the caller allocates @buf on stack and drops the lock between add_req() and
> + * sync_req().
> + *
> + * Return 0 if the request was successfully added to the queue.
> + */
> +static int __viommu_add_req(struct viommu_dev *viommu, void *buf, size_t len,
> + bool writeback)
> +{
> + int ret;
> + off_t write_offset;
> + struct viommu_request *req;
> + struct scatterlist top_sg, bottom_sg;
> + struct scatterlist *sg[2] = { &top_sg, &bottom_sg };
> + struct virtqueue *vq = viommu->vqs[VIOMMU_REQUEST_VQ];
> +
> + assert_spin_locked(&viommu->request_lock);
> +
> + write_offset = viommu_get_write_desc_offset(viommu, buf, len);
> + if (write_offset <= 0)
> + return -EINVAL;
> +
> + req = kzalloc(sizeof(*req) + len, GFP_ATOMIC);
> + if (!req)
> + return -ENOMEM;
> +
> + req->len = len;
> + if (writeback) {
> + req->writeback = buf + write_offset;
> + req->write_offset = write_offset;
> + }
> + memcpy(&req->buf, buf, write_offset);
> +
> + sg_init_one(&top_sg, req->buf, write_offset);
> + sg_init_one(&bottom_sg, req->buf + write_offset, len - write_offset);
> +
> + ret = virtqueue_add_sgs(vq, sg, 1, 1, req, GFP_ATOMIC);
> + if (ret == -ENOSPC) {
> + /* If the queue is full, sync and retry */
> + if (!__viommu_sync_req(viommu))
> + ret = virtqueue_add_sgs(vq, sg, 1, 1, req, GFP_ATOMIC);
> + }
> + if (ret)
> + goto err_free;
> +
> + list_add_tail(&req->list, &viommu->requests);
> + return 0;
> +
> +err_free:
> + kfree(req);
> + return ret;
> +}
> +
> +static int viommu_add_req(struct viommu_dev *viommu, void *buf, size_t len)
> +{
> + int ret;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&viommu->request_lock, flags);
> + ret = __viommu_add_req(viommu, buf, len, false);
> + if (ret)
> + dev_dbg(viommu->dev, "could not add request: %d\n", ret);
> + spin_unlock_irqrestore(&viommu->request_lock, flags);
> +
> + return ret;
> +}
> +
> +/*
> + * Send a request and wait for it to complete. Return the request status (as an
> + * errno)
> + */
> +static int viommu_send_req_sync(struct viommu_dev *viommu, void *buf,
> + size_t len)
> +{
> + int ret;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&viommu->request_lock, flags);
> +
> + ret = __viommu_add_req(viommu, buf, len, true);
> + if (ret) {
> + dev_dbg(viommu->dev, "could not add request (%d)\n", ret);
> + goto out_unlock;
> + }
> +
> + ret = __viommu_sync_req(viommu);
> + if (ret) {
> + dev_dbg(viommu->dev, "could not sync requests (%d)\n", ret);
> + /* Fall-through (get the actual request status) */
> + }
> +
> + ret = viommu_get_req_errno(buf, len);
> +out_unlock:
> + spin_unlock_irqrestore(&viommu->request_lock, flags);
> + return ret;
> +}
> +
> +/*
> + * viommu_add_mapping - add a mapping to the internal tree
> + *
> + * On success, return the new mapping. Otherwise return NULL.
> + */
> +static int viommu_add_mapping(struct viommu_domain *vdomain, unsigned long iova,
> + phys_addr_t paddr, size_t size, u32 flags)
> +{
> + unsigned long irqflags;
> + struct viommu_mapping *mapping;
> +
> + mapping = kzalloc(sizeof(*mapping), GFP_ATOMIC);
> + if (!mapping)
> + return -ENOMEM;
> +
> + mapping->paddr = paddr;
> + mapping->iova.start = iova;
> + mapping->iova.last = iova + size - 1;
> + mapping->flags = flags;
> +
> + spin_lock_irqsave(&vdomain->mappings_lock, irqflags);
> + interval_tree_insert(&mapping->iova, &vdomain->mappings);
> + spin_unlock_irqrestore(&vdomain->mappings_lock, irqflags);
> +
> + return 0;
> +}
> +
> +/*
> + * viommu_del_mappings - remove mappings from the internal tree
> + *
> + * @vdomain: the domain
> + * @iova: start of the range
> + * @size: size of the range. A size of 0 corresponds to the entire address
> + * space.
> + *
> + * On success, returns the number of unmapped bytes (>= size)
> + */
> +static size_t viommu_del_mappings(struct viommu_domain *vdomain,
> + unsigned long iova, size_t size)
> +{
> + size_t unmapped = 0;
> + unsigned long flags;
> + unsigned long last = iova + size - 1;
> + struct viommu_mapping *mapping = NULL;
> + struct interval_tree_node *node, *next;
> +
> + spin_lock_irqsave(&vdomain->mappings_lock, flags);
> + next = interval_tree_iter_first(&vdomain->mappings, iova, last);
> + while (next) {
> + node = next;
> + mapping = container_of(node, struct viommu_mapping, iova);
> + next = interval_tree_iter_next(node, iova, last);
> +
> + /* Trying to split a mapping? */
> + if (mapping->iova.start < iova)
> + break;
> +
> + /*
> + * Virtio-iommu doesn't allow UNMAP to split a mapping created
> + * with a single MAP request, so remove the full mapping.
> + */
> + unmapped += mapping->iova.last - mapping->iova.start + 1;
> +
> + interval_tree_remove(node, &vdomain->mappings);
> + kfree(mapping);
> + }
> + spin_unlock_irqrestore(&vdomain->mappings_lock, flags);
> +
> + return unmapped;
> +}
> +
> +/*
> + * viommu_replay_mappings - re-send MAP requests
> + *
> + * When reattaching a domain that was previously detached from all endpoints,
> + * mappings were deleted from the device. Re-create the mappings available in
> + * the internal tree.
> + */
> +static int viommu_replay_mappings(struct viommu_domain *vdomain)
> +{
> + int ret = 0;
> + unsigned long flags;
> + struct viommu_mapping *mapping;
> + struct interval_tree_node *node;
> + struct virtio_iommu_req_map map;
> +
> + spin_lock_irqsave(&vdomain->mappings_lock, flags);
> + node = interval_tree_iter_first(&vdomain->mappings, 0, -1UL);
> + while (node) {
> + mapping = container_of(node, struct viommu_mapping, iova);
> + map = (struct virtio_iommu_req_map) {
> + .head.type = VIRTIO_IOMMU_T_MAP,
> + .domain = cpu_to_le32(vdomain->id),
> + .virt_start = cpu_to_le64(mapping->iova.start),
> + .virt_end = cpu_to_le64(mapping->iova.last),
> + .phys_start = cpu_to_le64(mapping->paddr),
> + .flags = cpu_to_le32(mapping->flags),
> + };
> +
> + ret = viommu_send_req_sync(vdomain->viommu, &map, sizeof(map));
> + if (ret)
> + break;
> +
> + node = interval_tree_iter_next(node, 0, -1UL);
> + }
> + spin_unlock_irqrestore(&vdomain->mappings_lock, flags);
> +
> + return ret;
> +}
> +
> +/* IOMMU API */
> +
> +static struct iommu_domain *viommu_domain_alloc(unsigned type)
> +{
> + struct viommu_domain *vdomain;
> +
> + if (type != IOMMU_DOMAIN_UNMANAGED && type != IOMMU_DOMAIN_DMA)
> + return NULL;
> +
> + vdomain = kzalloc(sizeof(*vdomain), GFP_KERNEL);
> + if (!vdomain)
> + return NULL;
> +
> + mutex_init(&vdomain->mutex);
> + spin_lock_init(&vdomain->mappings_lock);
> + vdomain->mappings = RB_ROOT_CACHED;
> +
> + if (type == IOMMU_DOMAIN_DMA &&
> + iommu_get_dma_cookie(&vdomain->domain)) {
> + kfree(vdomain);
> + return NULL;
> + }
> +
> + return &vdomain->domain;
> +}
> +
> +static int viommu_domain_finalise(struct viommu_dev *viommu,
> + struct iommu_domain *domain)
> +{
> + int ret;
> + struct viommu_domain *vdomain = to_viommu_domain(domain);
> + unsigned int max_domain = viommu->domain_bits > 31 ? ~0 :
> + (1U << viommu->domain_bits) - 1;
> +
> + vdomain->viommu = viommu;
> +
> + domain->pgsize_bitmap = viommu->pgsize_bitmap;
> + domain->geometry = viommu->geometry;
> +
> + ret = ida_alloc_max(&viommu->domain_ids, max_domain, GFP_KERNEL);
> + if (ret >= 0)
> + vdomain->id = (unsigned int)ret;
> +
> + return ret > 0 ? 0 : ret;
> +}
> +
> +static void viommu_domain_free(struct iommu_domain *domain)
> +{
> + struct viommu_domain *vdomain = to_viommu_domain(domain);
> +
> + iommu_put_dma_cookie(domain);
> +
> + /* Free all remaining mappings (size 2^64) */
> + viommu_del_mappings(vdomain, 0, 0);
> +
> + if (vdomain->viommu)
> + ida_free(&vdomain->viommu->domain_ids, vdomain->id);
> +
> + kfree(vdomain);
> +}
> +
> +static int viommu_attach_dev(struct iommu_domain *domain, struct device *dev)
> +{
> + int i;
> + int ret = 0;
> + struct virtio_iommu_req_attach req;
> + struct iommu_fwspec *fwspec = dev->iommu_fwspec;
> + struct viommu_endpoint *vdev = fwspec->iommu_priv;
> + struct viommu_domain *vdomain = to_viommu_domain(domain);
> +
> + mutex_lock(&vdomain->mutex);
> + if (!vdomain->viommu) {
> + /*
> + * Properly initialize the domain now that we know which viommu
> + * owns it.
> + */
> + ret = viommu_domain_finalise(vdev->viommu, domain);
> + } else if (vdomain->viommu != vdev->viommu) {
> + dev_err(dev, "cannot attach to foreign vIOMMU\n");
> + ret = -EXDEV;
> + }
> + mutex_unlock(&vdomain->mutex);
> +
> + if (ret)
> + return ret;
> +
> + /*
> + * In the virtio-iommu device, when attaching the endpoint to a new
> + * domain, it is detached from the old one and, if as as a result the
> + * old domain isn't attached to any endpoint, all mappings are removed
> + * from the old domain and it is freed.
> + *
> + * In the driver the old domain still exists, and its mappings will be
> + * recreated if it gets reattached to an endpoint. Otherwise it will be
> + * freed explicitly.
> + *
> + * vdev->vdomain is protected by group->mutex
> + */
> + if (vdev->vdomain)
> + vdev->vdomain->nr_endpoints--;
> +
> + req = (struct virtio_iommu_req_attach) {
> + .head.type = VIRTIO_IOMMU_T_ATTACH,
> + .domain = cpu_to_le32(vdomain->id),
> + };
> +
> + for (i = 0; i < fwspec->num_ids; i++) {
> + req.endpoint = cpu_to_le32(fwspec->ids[i]);
> +
> + ret = viommu_send_req_sync(vdomain->viommu, &req, sizeof(req));
> + if (ret)
> + return ret;
> + }
> +
> + if (!vdomain->nr_endpoints) {
> + /*
> + * This endpoint is the first to be attached to the domain.
> + * Replay existing mappings (e.g. SW MSI).
> + */
> + ret = viommu_replay_mappings(vdomain);
> + if (ret)
> + return ret;
> + }
> +
> + vdomain->nr_endpoints++;
> + vdev->vdomain = vdomain;
> +
> + return 0;
> +}
> +
> +static int viommu_map(struct iommu_domain *domain, unsigned long iova,
> + phys_addr_t paddr, size_t size, int prot)
> +{
> + int ret;
> + int flags;
> + struct virtio_iommu_req_map map;
> + struct viommu_domain *vdomain = to_viommu_domain(domain);
> +
> + flags = (prot & IOMMU_READ ? VIRTIO_IOMMU_MAP_F_READ : 0) |
> + (prot & IOMMU_WRITE ? VIRTIO_IOMMU_MAP_F_WRITE : 0) |
> + (prot & IOMMU_MMIO ? VIRTIO_IOMMU_MAP_F_MMIO : 0);
> +
> + ret = viommu_add_mapping(vdomain, iova, paddr, size, flags);
> + if (ret)
> + return ret;
> +
> + map = (struct virtio_iommu_req_map) {
> + .head.type = VIRTIO_IOMMU_T_MAP,
> + .domain = cpu_to_le32(vdomain->id),
> + .virt_start = cpu_to_le64(iova),
> + .phys_start = cpu_to_le64(paddr),
> + .virt_end = cpu_to_le64(iova + size - 1),
> + .flags = cpu_to_le32(flags),
> + };
> +
> + if (!vdomain->nr_endpoints)
> + return 0;
> +
> + ret = viommu_send_req_sync(vdomain->viommu, &map, sizeof(map));
> + if (ret)
> + viommu_del_mappings(vdomain, iova, size);
> +
> + return ret;
> +}
> +
> +static size_t viommu_unmap(struct iommu_domain *domain, unsigned long iova,
> + size_t size)
> +{
> + int ret = 0;
> + size_t unmapped;
> + struct virtio_iommu_req_unmap unmap;
> + struct viommu_domain *vdomain = to_viommu_domain(domain);
> +
> + unmapped = viommu_del_mappings(vdomain, iova, size);
> + if (unmapped < size)
> + return 0;
> +
> + /* Device already removed all mappings after detach. */
> + if (!vdomain->nr_endpoints)
> + return unmapped;
> +
> + unmap = (struct virtio_iommu_req_unmap) {
> + .head.type = VIRTIO_IOMMU_T_UNMAP,
> + .domain = cpu_to_le32(vdomain->id),
> + .virt_start = cpu_to_le64(iova),
> + .virt_end = cpu_to_le64(iova + unmapped - 1),
> + };
> +
> + ret = viommu_add_req(vdomain->viommu, &unmap, sizeof(unmap));
> + return ret ? 0 : unmapped;
> +}
> +
> +static phys_addr_t viommu_iova_to_phys(struct iommu_domain *domain,
> + dma_addr_t iova)
> +{
> + u64 paddr = 0;
> + unsigned long flags;
> + struct viommu_mapping *mapping;
> + struct interval_tree_node *node;
> + struct viommu_domain *vdomain = to_viommu_domain(domain);
> +
> + spin_lock_irqsave(&vdomain->mappings_lock, flags);
> + node = interval_tree_iter_first(&vdomain->mappings, iova, iova);
> + if (node) {
> + mapping = container_of(node, struct viommu_mapping, iova);
> + paddr = mapping->paddr + (iova - mapping->iova.start);
> + }
> + spin_unlock_irqrestore(&vdomain->mappings_lock, flags);
> +
> + return paddr;
> +}
> +
> +static void viommu_iotlb_sync(struct iommu_domain *domain)
> +{
> + struct viommu_domain *vdomain = to_viommu_domain(domain);
> +
> + viommu_sync_req(vdomain->viommu);
> +}
> +
> +static void viommu_get_resv_regions(struct device *dev, struct list_head *head)
> +{
> + struct iommu_resv_region *region;
> + int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
> +
> + region = iommu_alloc_resv_region(MSI_IOVA_BASE, MSI_IOVA_LENGTH, prot,
> + IOMMU_RESV_SW_MSI);
> + if (!region)
> + return;
> +
> + list_add_tail(®ion->list, head);
> + iommu_dma_get_resv_regions(dev, head);
> +}
> +
> +static void viommu_put_resv_regions(struct device *dev, struct list_head *head)
> +{
> + struct iommu_resv_region *entry, *next;
> +
> + list_for_each_entry_safe(entry, next, head, list)
> + kfree(entry);
> +}
> +
> +static struct iommu_ops viommu_ops;
> +static struct virtio_driver virtio_iommu_drv;
> +
> +static int viommu_match_node(struct device *dev, void *data)
> +{
> + return dev->parent->fwnode == data;
> +}
> +
> +static struct viommu_dev *viommu_get_by_fwnode(struct fwnode_handle *fwnode)
> +{
> + struct device *dev = driver_find_device(&virtio_iommu_drv.driver, NULL,
> + fwnode, viommu_match_node);
> + put_device(dev);
> +
> + return dev ? dev_to_virtio(dev)->priv : NULL;
> +}
> +
> +static int viommu_add_device(struct device *dev)
> +{
> + int ret;
> + struct iommu_group *group;
> + struct viommu_endpoint *vdev;
> + struct viommu_dev *viommu = NULL;
> + struct iommu_fwspec *fwspec = dev->iommu_fwspec;
> +
> + if (!fwspec || fwspec->ops != &viommu_ops)
> + return -ENODEV;
> +
> + viommu = viommu_get_by_fwnode(fwspec->iommu_fwnode);
> + if (!viommu)
> + return -ENODEV;
> +
> + vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
> + if (!vdev)
> + return -ENOMEM;
> +
> + vdev->viommu = viommu;
> + fwspec->iommu_priv = vdev;
> +
> + ret = iommu_device_link(&viommu->iommu, dev);
> + if (ret)
> + goto err_free_dev;
> +
> + /*
> + * Last step creates a default domain and attaches to it. Everything
> + * must be ready.
> + */
> + group = iommu_group_get_for_dev(dev);
> + if (IS_ERR(group)) {
> + ret = PTR_ERR(group);
> + goto err_unlink_dev;
> + }
> +
> + iommu_group_put(group);
> +
> + return PTR_ERR_OR_ZERO(group);
> +
> +err_unlink_dev:
> + iommu_device_unlink(&viommu->iommu, dev);
> +err_free_dev:
> + kfree(vdev);
> +
> + return ret;
> +}
> +
> +static void viommu_remove_device(struct device *dev)
> +{
> + struct viommu_endpoint *vdev;
> + struct iommu_fwspec *fwspec = dev->iommu_fwspec;
> +
> + if (!fwspec || fwspec->ops != &viommu_ops)
> + return;
> +
> + vdev = fwspec->iommu_priv;
> +
> + iommu_group_remove_device(dev);
> + iommu_device_unlink(&vdev->viommu->iommu, dev);
> + kfree(vdev);
> +}
> +
> +static struct iommu_group *viommu_device_group(struct device *dev)
> +{
> + if (dev_is_pci(dev))
> + return pci_device_group(dev);
> + else
> + return generic_device_group(dev);
> +}
> +
> +static int viommu_of_xlate(struct device *dev, struct of_phandle_args *args)
> +{
> + return iommu_fwspec_add_ids(dev, args->args, 1);
> +}
> +
> +static struct iommu_ops viommu_ops = {
> + .domain_alloc = viommu_domain_alloc,
> + .domain_free = viommu_domain_free,
> + .attach_dev = viommu_attach_dev,
> + .map = viommu_map,
> + .unmap = viommu_unmap,
> + .iova_to_phys = viommu_iova_to_phys,
> + .iotlb_sync = viommu_iotlb_sync,
> + .add_device = viommu_add_device,
> + .remove_device = viommu_remove_device,
> + .device_group = viommu_device_group,
> + .get_resv_regions = viommu_get_resv_regions,
> + .put_resv_regions = viommu_put_resv_regions,
> + .of_xlate = viommu_of_xlate,
> +};
> +
> +static int viommu_init_vqs(struct viommu_dev *viommu)
> +{
> + struct virtio_device *vdev = dev_to_virtio(viommu->dev);
> + const char *name = "request";
> + void *ret;
> +
> + ret = virtio_find_single_vq(vdev, NULL, name);
> + if (IS_ERR(ret)) {
> + dev_err(viommu->dev, "cannot find VQ\n");
> + return PTR_ERR(ret);
> + }
> +
> + viommu->vqs[VIOMMU_REQUEST_VQ] = ret;
> +
> + return 0;
> +}
> +
> +static int viommu_probe(struct virtio_device *vdev)
> +{
> + struct device *parent_dev = vdev->dev.parent;
> + struct viommu_dev *viommu = NULL;
> + struct device *dev = &vdev->dev;
> + u64 input_start = 0;
> + u64 input_end = -1UL;
> + int ret;
> +
> + if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1) ||
> + !virtio_has_feature(vdev, VIRTIO_IOMMU_F_MAP_UNMAP))
> + return -ENODEV;
> +
> + viommu = devm_kzalloc(dev, sizeof(*viommu), GFP_KERNEL);
> + if (!viommu)
> + return -ENOMEM;
> +
> + spin_lock_init(&viommu->request_lock);
> + ida_init(&viommu->domain_ids);
> + viommu->dev = dev;
> + viommu->vdev = vdev;
> + INIT_LIST_HEAD(&viommu->requests);
> +
> + ret = viommu_init_vqs(viommu);
> + if (ret)
> + return ret;
> +
> + virtio_cread(vdev, struct virtio_iommu_config, page_size_mask,
> + &viommu->pgsize_bitmap);
> +
> + if (!viommu->pgsize_bitmap) {
> + ret = -EINVAL;
> + goto err_free_vqs;
> + }
> +
> + viommu->domain_bits = 32;
> +
> + /* Optional features */
> + virtio_cread_feature(vdev, VIRTIO_IOMMU_F_INPUT_RANGE,
> + struct virtio_iommu_config, input_range.start,
> + &input_start);
> +
> + virtio_cread_feature(vdev, VIRTIO_IOMMU_F_INPUT_RANGE,
> + struct virtio_iommu_config, input_range.end,
> + &input_end);
> +
> + virtio_cread_feature(vdev, VIRTIO_IOMMU_F_DOMAIN_BITS,
> + struct virtio_iommu_config, domain_bits,
> + &viommu->domain_bits);
> +
> + viommu->geometry = (struct iommu_domain_geometry) {
> + .aperture_start = input_start,
> + .aperture_end = input_end,
> + .force_aperture = true,
> + };
> +
> + viommu_ops.pgsize_bitmap = viommu->pgsize_bitmap;
> +
> + virtio_device_ready(vdev);
> +
> + ret = iommu_device_sysfs_add(&viommu->iommu, dev, NULL, "%s",
> + virtio_bus_name(vdev));
> + if (ret)
> + goto err_free_vqs;
> +
> + iommu_device_set_ops(&viommu->iommu, &viommu_ops);
> + iommu_device_set_fwnode(&viommu->iommu, parent_dev->fwnode);
> +
> + iommu_device_register(&viommu->iommu);
> +
> +#ifdef CONFIG_PCI
> + if (pci_bus_type.iommu_ops != &viommu_ops) {
> + pci_request_acs();
> + ret = bus_set_iommu(&pci_bus_type, &viommu_ops);
> + if (ret)
> + goto err_unregister;
> + }
> +#endif
> +#ifdef CONFIG_ARM_AMBA
> + if (amba_bustype.iommu_ops != &viommu_ops) {
> + ret = bus_set_iommu(&amba_bustype, &viommu_ops);
> + if (ret)
> + goto err_unregister;
> + }
> +#endif
> + if (platform_bus_type.iommu_ops != &viommu_ops) {
> + ret = bus_set_iommu(&platform_bus_type, &viommu_ops);
> + if (ret)
> + goto err_unregister;
> + }
> +
> + vdev->priv = viommu;
> +
> + dev_info(dev, "input address: %u bits\n",
> + order_base_2(viommu->geometry.aperture_end));
> + dev_info(dev, "page mask: %#llx\n", viommu->pgsize_bitmap);
> +
> + return 0;
> +
> +err_unregister:
> + iommu_device_sysfs_remove(&viommu->iommu);
> + iommu_device_unregister(&viommu->iommu);
> +err_free_vqs:
> + vdev->config->del_vqs(vdev);
> +
> + return ret;
> +}
> +
> +static void viommu_remove(struct virtio_device *vdev)
> +{
> + struct viommu_dev *viommu = vdev->priv;
> +
> + iommu_device_sysfs_remove(&viommu->iommu);
> + iommu_device_unregister(&viommu->iommu);
> +
> + /* Stop all virtqueues */
> + vdev->config->reset(vdev);
> + vdev->config->del_vqs(vdev);
> +
> + dev_info(&vdev->dev, "device removed\n");
> +}
> +
> +static void viommu_config_changed(struct virtio_device *vdev)
> +{
> + dev_warn(&vdev->dev, "config changed\n");
> +}
> +
> +static unsigned int features[] = {
> + VIRTIO_IOMMU_F_MAP_UNMAP,
> + VIRTIO_IOMMU_F_DOMAIN_BITS,
> + VIRTIO_IOMMU_F_INPUT_RANGE,
> +};
> +
> +static struct virtio_device_id id_table[] = {
> + { VIRTIO_ID_IOMMU, VIRTIO_DEV_ANY_ID },
> + { 0 },
> +};
> +
> +static struct virtio_driver virtio_iommu_drv = {
> + .driver.name = KBUILD_MODNAME,
> + .driver.owner = THIS_MODULE,
> + .id_table = id_table,
> + .feature_table = features,
> + .feature_table_size = ARRAY_SIZE(features),
> + .probe = viommu_probe,
> + .remove = viommu_remove,
> + .config_changed = viommu_config_changed,
> +};
> +
> +module_virtio_driver(virtio_iommu_drv);
> +
> +MODULE_DESCRIPTION("Virtio IOMMU driver");
> +MODULE_AUTHOR("Jean-Philippe Brucker <jean-philippe.brucker@arm.com>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h
> index 6d5c3b2d4f4d..cfe47c5d9a56 100644
> --- a/include/uapi/linux/virtio_ids.h
> +++ b/include/uapi/linux/virtio_ids.h
> @@ -43,5 +43,6 @@
> #define VIRTIO_ID_INPUT 18 /* virtio input */
> #define VIRTIO_ID_VSOCK 19 /* virtio vsock transport */
> #define VIRTIO_ID_CRYPTO 20 /* virtio crypto */
> +#define VIRTIO_ID_IOMMU 23 /* virtio IOMMU */
>
> #endif /* _LINUX_VIRTIO_IDS_H */
> diff --git a/include/uapi/linux/virtio_iommu.h b/include/uapi/linux/virtio_iommu.h
> new file mode 100644
> index 000000000000..e7c05e3afa44
> --- /dev/null
> +++ b/include/uapi/linux/virtio_iommu.h
> @@ -0,0 +1,104 @@
> +/* SPDX-License-Identifier: BSD-3-Clause */
> +/*
> + * Virtio-iommu definition v0.9
> + *
> + * Copyright (C) 2018 Arm Ltd.
> + */
> +#ifndef _UAPI_LINUX_VIRTIO_IOMMU_H
> +#define _UAPI_LINUX_VIRTIO_IOMMU_H
> +
> +#include <linux/types.h>
> +
> +/* Feature bits */
> +#define VIRTIO_IOMMU_F_INPUT_RANGE 0
> +#define VIRTIO_IOMMU_F_DOMAIN_BITS 1
> +#define VIRTIO_IOMMU_F_MAP_UNMAP 2
> +#define VIRTIO_IOMMU_F_BYPASS 3
> +
> +struct virtio_iommu_range {
> + __u64 start;
> + __u64 end;
> +};
> +
> +struct virtio_iommu_config {
> + /* Supported page sizes */
> + __u64 page_size_mask;
> + /* Supported IOVA range */
> + struct virtio_iommu_range input_range;
> + /* Max domain ID size */
> + __u8 domain_bits;
> + __u8 padding[3];
Not enough padding here it seems. Structure is 8 byte
aligned on 64 bit systems.
> +};
> +
> +/* Request types */
> +#define VIRTIO_IOMMU_T_ATTACH 0x01
> +#define VIRTIO_IOMMU_T_DETACH 0x02
> +#define VIRTIO_IOMMU_T_MAP 0x03
> +#define VIRTIO_IOMMU_T_UNMAP 0x04
> +
> +/* Status types */
> +#define VIRTIO_IOMMU_S_OK 0x00
> +#define VIRTIO_IOMMU_S_IOERR 0x01
> +#define VIRTIO_IOMMU_S_UNSUPP 0x02
> +#define VIRTIO_IOMMU_S_DEVERR 0x03
> +#define VIRTIO_IOMMU_S_INVAL 0x04
> +#define VIRTIO_IOMMU_S_RANGE 0x05
> +#define VIRTIO_IOMMU_S_NOENT 0x06
> +#define VIRTIO_IOMMU_S_FAULT 0x07
> +
> +struct virtio_iommu_req_head {
> + __u8 type;
> + __u8 reserved[3];
> +};
> +
> +struct virtio_iommu_req_tail {
> + __u8 status;
> + __u8 reserved[3];
> +};
> +
> +struct virtio_iommu_req_attach {
> + struct virtio_iommu_req_head head;
> + __le32 domain;
> + __le32 endpoint;
> + __u8 reserved[8];
> + struct virtio_iommu_req_tail tail;
> +};
> +
> +struct virtio_iommu_req_detach {
> + struct virtio_iommu_req_head head;
> + __le32 domain;
> + __le32 endpoint;
> + __u8 reserved[8];
> + struct virtio_iommu_req_tail tail;
> +};
> +
> +#define VIRTIO_IOMMU_MAP_F_READ (1 << 0)
> +#define VIRTIO_IOMMU_MAP_F_WRITE (1 << 1)
> +#define VIRTIO_IOMMU_MAP_F_EXEC (1 << 2)
> +#define VIRTIO_IOMMU_MAP_F_MMIO (1 << 3)
> +
> +#define VIRTIO_IOMMU_MAP_F_MASK (VIRTIO_IOMMU_MAP_F_READ | \
> + VIRTIO_IOMMU_MAP_F_WRITE | \
> + VIRTIO_IOMMU_MAP_F_EXEC | \
> + VIRTIO_IOMMU_MAP_F_MMIO)
> +
> +struct virtio_iommu_req_map {
> + struct virtio_iommu_req_head head;
> + __le32 domain;
> + __le64 virt_start;
> + __le64 virt_end;
> + __le64 phys_start;
> + __le32 flags;
> + struct virtio_iommu_req_tail tail;
> +};
> +
> +struct virtio_iommu_req_unmap {
> + struct virtio_iommu_req_head head;
> + __le32 domain;
> + __le64 virt_start;
> + __le64 virt_end;
> + __u8 reserved[4];
> + struct virtio_iommu_req_tail tail;
> +};
> +
> +#endif
> --
> 2.19.1
>
> _______________________________________________
> Virtualization mailing list
> Virtualization@lists.linux-foundation.org
> https://lists.linuxfoundation.org/mailman/listinfo/virtualization
^ permalink raw reply
* Re: [PATCH net 2/2] virtio-net: fail XDP set if guest csum is negotiated
From: David Miller @ 2018-11-23 20:01 UTC (permalink / raw)
To: jasowang
Cc: mst, netdev, dsahern, linux-kernel, virtualization, pashinho1990,
brouer
In-Reply-To: <20181122063631.14452-2-jasowang@redhat.com>
From: Jason Wang <jasowang@redhat.com>
Date: Thu, 22 Nov 2018 14:36:31 +0800
> We don't support partial csumed packet since its metadata will be lost
> or incorrect during XDP processing. So fail the XDP set if guest_csum
> feature is negotiated.
>
> Fixes: f600b6905015 ("virtio_net: Add XDP support")
> Reported-by: Jesper Dangaard Brouer <brouer@redhat.com>
> Cc: Jesper Dangaard Brouer <brouer@redhat.com>
> Cc: Pavel Popa <pashinho1990@gmail.com>
> Cc: David Ahern <dsahern@gmail.com>
> Signed-off-by: Jason Wang <jasowang@redhat.com>
Applied and queued up for -stable.
Same comments as for patch #1.
^ permalink raw reply
* Re: [PATCH net 1/2] virtio-net: disable guest csum during XDP set
From: David Miller @ 2018-11-23 20:01 UTC (permalink / raw)
To: jasowang
Cc: mst, netdev, dsahern, linux-kernel, virtualization, pashinho1990,
brouer
In-Reply-To: <20181122063631.14452-1-jasowang@redhat.com>
From: Jason Wang <jasowang@redhat.com>
Date: Thu, 22 Nov 2018 14:36:30 +0800
> We don't disable VIRTIO_NET_F_GUEST_CSUM if XDP was set. This means we
> can receive partial csumed packets with metadata kept in the
> vnet_hdr. This may have several side effects:
>
> - It could be overridden by header adjustment, thus is might be not
> correct after XDP processing.
> - There's no way to pass such metadata information through
> XDP_REDIRECT to another driver.
> - XDP does not support checksum offload right now.
>
> So simply disable guest csum if possible in this the case of XDP.
>
> Fixes: 3f93522ffab2d ("virtio-net: switch off offloads on demand if possible on XDP set")
> Reported-by: Jesper Dangaard Brouer <brouer@redhat.com>
> Cc: Jesper Dangaard Brouer <brouer@redhat.com>
> Cc: Pavel Popa <pashinho1990@gmail.com>
> Cc: David Ahern <dsahern@gmail.com>
> Signed-off-by: Jason Wang <jasowang@redhat.com>
Applied and queued up for -stable.
We really should have a way to use the checksum provided if the XDP
program returns XDP_PASS and does not modify the packet contents
or size.
^ permalink raw reply
* Re: [PATCH net-next 2/3] vhost_net: support in order feature
From: Michael S. Tsirkin @ 2018-11-23 15:49 UTC (permalink / raw)
To: Jason Wang; +Cc: netdev, linux-kernel, kvm, virtualization
In-Reply-To: <20181123030016.4924-3-jasowang@redhat.com>
On Fri, Nov 23, 2018 at 11:00:15AM +0800, Jason Wang wrote:
> This makes vhost_net to support in order feature. This is as simple as
> use datacopy path when it was negotiated. An alternative is not to
> advertise in order when zerocopy is enabled which tends to be
> suboptimal consider zerocopy may suffer from e.g HOL issues.
Well IIRC vhost_zerocopy_signal_used is used to
actually reorder used ring to match available ring.
So with a big comment explaining why it is so,
we could just enable IN_ORDER there too.
>
> Signed-off-by: Jason Wang <jasowang@redhat.com>
> ---
> drivers/vhost/net.c | 6 ++++--
> 1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
> index d919284f103b..bdf5de5a7eb2 100644
> --- a/drivers/vhost/net.c
> +++ b/drivers/vhost/net.c
> @@ -74,7 +74,8 @@ enum {
> VHOST_NET_FEATURES = VHOST_FEATURES |
> (1ULL << VHOST_NET_F_VIRTIO_NET_HDR) |
> (1ULL << VIRTIO_NET_F_MRG_RXBUF) |
> - (1ULL << VIRTIO_F_IOMMU_PLATFORM)
> + (1ULL << VIRTIO_F_IOMMU_PLATFORM) |
> + (1ULL << VIRTIO_F_IN_ORDER)
> };
>
> enum {
> @@ -971,7 +972,8 @@ static void handle_tx(struct vhost_net *net)
> vhost_disable_notify(&net->dev, vq);
> vhost_net_disable_vq(net, vq);
>
> - if (vhost_sock_zcopy(sock))
> + if (vhost_sock_zcopy(sock) &&
> + !vhost_has_feature(vq, VIRTIO_F_IN_ORDER))
> handle_tx_zerocopy(net, sock);
> else
> handle_tx_copy(net, sock);
> --
> 2.17.1
^ permalink raw reply
* Re: [PATCH net-next 3/3] vhost: don't touch avail ring if in_order is negotiated
From: Michael S. Tsirkin @ 2018-11-23 15:41 UTC (permalink / raw)
To: Jason Wang; +Cc: netdev, linux-kernel, kvm, virtualization
In-Reply-To: <20181123030016.4924-4-jasowang@redhat.com>
On Fri, Nov 23, 2018 at 11:00:16AM +0800, Jason Wang wrote:
> Device use descriptors table in order, so there's no need to read
> index from available ring. This eliminate the cache contention on
> avail ring completely.
Well this isn't what the in order feature says in the spec.
It forces the used ring to be in the same order as
the available ring. So I don't think you can skip
checking the available ring. And in fact depending on
ring size and workload, using all of descriptor buffer might
cause a slowdown.
Rather you should be able to get
about the same speedup, but from skipping checking
the used ring in virtio.
> Virito-user + vhost_kernel + XDP_DROP gives about ~10% improvement on
> TX from 4.8Mpps to 5.3Mpps on Intel(R) Core(TM) i7-5600U CPU @
> 2.60GHz.
>
> Signed-off-by: Jason Wang <jasowang@redhat.com>
> ---
> drivers/vhost/vhost.c | 19 ++++++++++++-------
> 1 file changed, 12 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
> index 3a5f81a66d34..c8be151bc897 100644
> --- a/drivers/vhost/vhost.c
> +++ b/drivers/vhost/vhost.c
> @@ -2002,6 +2002,7 @@ int vhost_get_vq_desc(struct vhost_virtqueue *vq,
> __virtio16 avail_idx;
> __virtio16 ring_head;
> int ret, access;
> + bool in_order = vhost_has_feature(vq, VIRTIO_F_IN_ORDER);
>
> /* Check it isn't doing very strange things with descriptor numbers. */
> last_avail_idx = vq->last_avail_idx;
> @@ -2034,15 +2035,19 @@ int vhost_get_vq_desc(struct vhost_virtqueue *vq,
>
> /* Grab the next descriptor number they're advertising, and increment
> * the index we've seen. */
> - if (unlikely(vhost_get_avail(vq, ring_head,
> - &vq->avail->ring[last_avail_idx & (vq->num - 1)]))) {
> - vq_err(vq, "Failed to read head: idx %d address %p\n",
> - last_avail_idx,
> - &vq->avail->ring[last_avail_idx % vq->num]);
> - return -EFAULT;
> + if (!in_order) {
> + if (unlikely(vhost_get_avail(vq, ring_head,
> + &vq->avail->ring[last_avail_idx & (vq->num - 1)]))) {
> + vq_err(vq, "Failed to read head: idx %d address %p\n",
> + last_avail_idx,
> + &vq->avail->ring[last_avail_idx % vq->num]);
> + return -EFAULT;
> + }
> + head = vhost16_to_cpu(vq, ring_head);
> + } else {
> + head = last_avail_idx & (vq->num - 1);
> }
>
> - head = vhost16_to_cpu(vq, ring_head);
>
> /* If their number is silly, that's an error. */
> if (unlikely(head >= vq->num)) {
> --
> 2.17.1
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox