|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH 1/9] AMD/IOMMU: redo awaiting of command completion
On 09/06/2021 10:26, Jan Beulich wrote:
> The present abuse of the completion interrupt does not only stand in the
> way of, down the road, using it for its actual purpose, but also
> requires holding the IOMMU lock while waiting for command completion,
> limiting parallelism and keeping interrupts off for non-negligible
> periods of time. Have the IOMMU do an ordinary memory write instead of
> signaling an otherwise disabled interrupt (by just updating a status
> register bit).
>
> Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx>
> Reviewed-by: Paul Durrant <paul@xxxxxxx>
While I agree with the direction of the patch, some of the details could
do with improvement.
>
> --- a/xen/drivers/passthrough/amd/iommu_cmd.c
> +++ b/xen/drivers/passthrough/amd/iommu_cmd.c
> @@ -20,6 +20,9 @@
> #include "iommu.h"
> #include "../ats.h"
>
> +#define CMD_COMPLETION_INIT 0
> +#define CMD_COMPLETION_DONE 1
> +
> static void send_iommu_command(struct amd_iommu *iommu,
> const uint32_t cmd[4])
> {
> @@ -49,28 +52,31 @@ static void send_iommu_command(struct am
> static void flush_command_buffer(struct amd_iommu *iommu,
> unsigned int timeout_base)
> {
> + static DEFINE_PER_CPU(uint64_t, poll_slot);
> + uint64_t *this_poll_slot = &this_cpu(poll_slot);
> + paddr_t addr = virt_to_maddr(this_poll_slot);
> uint32_t cmd[4];
> s_time_t start, timeout;
> static unsigned int __read_mostly threshold = 1;
>
> - /* RW1C 'ComWaitInt' in status register */
> - writel(IOMMU_STATUS_COMP_WAIT_INT,
> - iommu->mmio_base + IOMMU_STATUS_MMIO_OFFSET);
> -
> - /* send an empty COMPLETION_WAIT command to flush command buffer */
> - cmd[3] = cmd[2] = 0;
> - set_field_in_reg_u32(IOMMU_CMD_COMPLETION_WAIT, 0,
> + ACCESS_ONCE(*this_poll_slot) = CMD_COMPLETION_INIT;
> +
> + /* send a COMPLETION_WAIT command to flush command buffer */
> + cmd[0] = addr;
> + set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, cmd[0],
> + IOMMU_COMP_WAIT_S_FLAG_MASK,
> + IOMMU_COMP_WAIT_S_FLAG_SHIFT, &cmd[0]);
set_field_in_reg_u32() is a disaster of a function - both in terms of
semantics, and code gen - and needs to be purged from the code.
It is a shame we don't have a real struct for objects in the command
buffer, but in lieu of that, this is just
cmd[0] = addr | IOMMU_COMP_WAIT_S_FLAG_MASK;
which is the direction that previous cleanup has gone.
There are no current users of IOMMU_COMP_WAIT_S_FLAG_SHIFT, and ...
> + cmd[1] = addr >> 32;
> + set_field_in_reg_u32(IOMMU_CMD_COMPLETION_WAIT, cmd[1],
> IOMMU_CMD_OPCODE_MASK,
> IOMMU_CMD_OPCODE_SHIFT, &cmd[1]);
> - set_field_in_reg_u32(IOMMU_CONTROL_ENABLED, 0,
> - IOMMU_COMP_WAIT_I_FLAG_MASK,
> - IOMMU_COMP_WAIT_I_FLAG_SHIFT, &cmd[0]);
... this drops the final use of IOMMU_COMP_WAIT_I_FLAG_SHIFT, so both
should be dropped.
As for IOMMU_CMD_OPCODE_SHIFT, that can't be dropped yet, but it would
still be better to use
cmd[1] = (addr >> 32) | MASK_INSR(IOMMU_CMD_COMPLETION_WAIT,
IOMMU_CMD_COMPLETION_WAIT);
in the short term.
~Andrew
P.S. an observation of cmd[1] means that AMD IOMMUs don't actually work
for a physaddr width of >52, despite some support along these lines
elsewhere in the spec.
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |