[PATCH 08/25] block/nvme: Simplify device reset

Philippe Mathieu-Daudé posted 25 patches 5 years ago
There is a newer version of this series
[PATCH 08/25] block/nvme: Simplify device reset
Posted by Philippe Mathieu-Daudé 5 years ago
Avoid multiple endianess conversion by using device endianess.

Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
---
 block/nvme.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/block/nvme.c b/block/nvme.c
index e95d59d3126..be14350f959 100644
--- a/block/nvme.c
+++ b/block/nvme.c
@@ -755,7 +755,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
     timeout_ms = MIN(500 * NVME_CAP_TO(cap), 30000);
 
     /* Reset device to get a clean state. */
-    regs->cc = cpu_to_le32(le32_to_cpu(regs->cc) & 0xFE);
+    regs->cc &= const_le32(0xFE);
     /* Wait for CSTS.RDY = 0. */
     deadline = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + timeout_ms * SCALE_MS;
     while (NVME_CSTS_RDY(le32_to_cpu(regs->csts))) {
-- 
2.26.2

Re: [PATCH 08/25] block/nvme: Simplify device reset
Posted by Keith Busch 5 years ago
On Tue, Oct 27, 2020 at 02:55:30PM +0100, Philippe Mathieu-Daudé wrote:
> Avoid multiple endianess conversion by using device endianess.
> 
> Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
> ---
>  block/nvme.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/block/nvme.c b/block/nvme.c
> index e95d59d3126..be14350f959 100644
> --- a/block/nvme.c
> +++ b/block/nvme.c
> @@ -755,7 +755,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
>      timeout_ms = MIN(500 * NVME_CAP_TO(cap), 30000);
>  
>      /* Reset device to get a clean state. */
> -    regs->cc = cpu_to_le32(le32_to_cpu(regs->cc) & 0xFE);
> +    regs->cc &= const_le32(0xFE);

This doesn't look right. The 'regs' is an MMIO address, correct? Memory
mappings use the CPU native access.

Re: [PATCH 08/25] block/nvme: Simplify device reset
Posted by Philippe Mathieu-Daudé 5 years ago
On 10/27/20 3:58 PM, Keith Busch wrote:
> On Tue, Oct 27, 2020 at 02:55:30PM +0100, Philippe Mathieu-Daudé wrote:
>> Avoid multiple endianess conversion by using device endianess.
>>
>> Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
>> ---
>>  block/nvme.c | 2 +-
>>  1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/block/nvme.c b/block/nvme.c
>> index e95d59d3126..be14350f959 100644
>> --- a/block/nvme.c
>> +++ b/block/nvme.c
>> @@ -755,7 +755,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
>>      timeout_ms = MIN(500 * NVME_CAP_TO(cap), 30000);
>>  
>>      /* Reset device to get a clean state. */
>> -    regs->cc = cpu_to_le32(le32_to_cpu(regs->cc) & 0xFE);
>> +    regs->cc &= const_le32(0xFE);
> 
> This doesn't look right. The 'regs' is an MMIO address, correct? Memory
> mappings use the CPU native access.

cc is little-endian uint32_t.

on big-endian: const_le32(0xFE) = 0xfe000000;
so: regs->cc &= 0xfe000000.

Anyway this is an example of unproductive patch, as it makes
things more confuse to you. Let's ignore it.


Re: [PATCH 08/25] block/nvme: Simplify device reset
Posted by Keith Busch 5 years ago
On Tue, Oct 27, 2020 at 04:53:31PM +0100, Philippe Mathieu-Daudé wrote:
> On 10/27/20 3:58 PM, Keith Busch wrote:
> > On Tue, Oct 27, 2020 at 02:55:30PM +0100, Philippe Mathieu-Daudé wrote:
> >> Avoid multiple endianess conversion by using device endianess.
> >>
> >> Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
> >> ---
> >>  block/nvme.c | 2 +-
> >>  1 file changed, 1 insertion(+), 1 deletion(-)
> >>
> >> diff --git a/block/nvme.c b/block/nvme.c
> >> index e95d59d3126..be14350f959 100644
> >> --- a/block/nvme.c
> >> +++ b/block/nvme.c
> >> @@ -755,7 +755,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
> >>      timeout_ms = MIN(500 * NVME_CAP_TO(cap), 30000);
> >>  
> >>      /* Reset device to get a clean state. */
> >> -    regs->cc = cpu_to_le32(le32_to_cpu(regs->cc) & 0xFE);
> >> +    regs->cc &= const_le32(0xFE);
> > 
> > This doesn't look right. The 'regs' is an MMIO address, correct? Memory
> > mappings use the CPU native access.
> 
> cc is little-endian uint32_t.

Well, yes and no. PCI is defined as a little endian transport, so all
CPUs have to automatically convert from their native format when
accessing memory mapped addresses over that transport, so you always use
the arch native format from the host software.

This isn't just for CC. This includes all memory mapped registers, so
this driver's CSTS, AQA, doorbells, etc... shouldn't have any endian
swapping.

See also: every other nvme driver. :)

Re: [PATCH 08/25] block/nvme: Simplify device reset
Posted by Stefan Hajnoczi 5 years ago
On Tue, Oct 27, 2020 at 09:55:34AM -0700, Keith Busch wrote:
> On Tue, Oct 27, 2020 at 04:53:31PM +0100, Philippe Mathieu-Daudé wrote:
> > On 10/27/20 3:58 PM, Keith Busch wrote:
> > > On Tue, Oct 27, 2020 at 02:55:30PM +0100, Philippe Mathieu-Daudé wrote:
> > >> Avoid multiple endianess conversion by using device endianess.
> > >>
> > >> Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
> > >> ---
> > >>  block/nvme.c | 2 +-
> > >>  1 file changed, 1 insertion(+), 1 deletion(-)
> > >>
> > >> diff --git a/block/nvme.c b/block/nvme.c
> > >> index e95d59d3126..be14350f959 100644
> > >> --- a/block/nvme.c
> > >> +++ b/block/nvme.c
> > >> @@ -755,7 +755,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
> > >>      timeout_ms = MIN(500 * NVME_CAP_TO(cap), 30000);
> > >>  
> > >>      /* Reset device to get a clean state. */
> > >> -    regs->cc = cpu_to_le32(le32_to_cpu(regs->cc) & 0xFE);
> > >> +    regs->cc &= const_le32(0xFE);
> > > 
> > > This doesn't look right. The 'regs' is an MMIO address, correct? Memory
> > > mappings use the CPU native access.
> > 
> > cc is little-endian uint32_t.
> 
> Well, yes and no. PCI is defined as a little endian transport, so all
> CPUs have to automatically convert from their native format when
> accessing memory mapped addresses over that transport, so you always use
> the arch native format from the host software.
> 
> This isn't just for CC. This includes all memory mapped registers, so
> this driver's CSTS, AQA, doorbells, etc... shouldn't have any endian
> swapping.
> 
> See also: every other nvme driver. :)

I don't see the opposite in Linux. The Linux NVMe drivers use byteswap
instructions because readl()/writel() and friends perform little-endian
memory accesses, not native endian memory accesses:

  static int nvme_pci_reg_write32(struct nvme_ctrl *ctrl, u32 off, u32 val)
  {
  	writel(val, to_nvme_dev(ctrl)->bar + off);
  	return 0;
  }

arch/arm/include/asm/io.h:

  #define writel(v,c)     ({ __iowmb(); writel_relaxed(v,c); })

where the byteswap happens here:

  #define writel_relaxed(v,c) __raw_writel((__force u32) cpu_to_le32(v),c)

The CPU is using explicit byteswaps, which matches what the QEMU driver
is doing. Am I missing something?

Stefan
Re: [PATCH 08/25] block/nvme: Simplify device reset
Posted by Keith Busch 5 years ago
On Wed, Oct 28, 2020 at 03:02:09PM +0000, Stefan Hajnoczi wrote:
> On Tue, Oct 27, 2020 at 09:55:34AM -0700, Keith Busch wrote:
> > On Tue, Oct 27, 2020 at 04:53:31PM +0100, Philippe Mathieu-Daudé wrote:
> > > On 10/27/20 3:58 PM, Keith Busch wrote:
> > > > On Tue, Oct 27, 2020 at 02:55:30PM +0100, Philippe Mathieu-Daudé wrote:
> > > >> Avoid multiple endianess conversion by using device endianess.
> > > >>
> > > >> Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
> > > >> ---
> > > >>  block/nvme.c | 2 +-
> > > >>  1 file changed, 1 insertion(+), 1 deletion(-)
> > > >>
> > > >> diff --git a/block/nvme.c b/block/nvme.c
> > > >> index e95d59d3126..be14350f959 100644
> > > >> --- a/block/nvme.c
> > > >> +++ b/block/nvme.c
> > > >> @@ -755,7 +755,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
> > > >>      timeout_ms = MIN(500 * NVME_CAP_TO(cap), 30000);
> > > >>  
> > > >>      /* Reset device to get a clean state. */
> > > >> -    regs->cc = cpu_to_le32(le32_to_cpu(regs->cc) & 0xFE);
> > > >> +    regs->cc &= const_le32(0xFE);
> > > > 
> > > > This doesn't look right. The 'regs' is an MMIO address, correct? Memory
> > > > mappings use the CPU native access.
> > > 
> > > cc is little-endian uint32_t.
> > 
> > Well, yes and no. PCI is defined as a little endian transport, so all
> > CPUs have to automatically convert from their native format when
> > accessing memory mapped addresses over that transport, so you always use
> > the arch native format from the host software.
> > 
> > This isn't just for CC. This includes all memory mapped registers, so
> > this driver's CSTS, AQA, doorbells, etc... shouldn't have any endian
> > swapping.
> > 
> > See also: every other nvme driver. :)
> 
> I don't see the opposite in Linux. The Linux NVMe drivers use byteswap
> instructions because readl()/writel() and friends perform little-endian
> memory accesses, not native endian memory accesses:
> 
>   static int nvme_pci_reg_write32(struct nvme_ctrl *ctrl, u32 off, u32 val)
>   {
>   	writel(val, to_nvme_dev(ctrl)->bar + off);
>   	return 0;
>   }
> 
> arch/arm/include/asm/io.h:
> 
>   #define writel(v,c)     ({ __iowmb(); writel_relaxed(v,c); })
> 
> where the byteswap happens here:
> 
>   #define writel_relaxed(v,c) __raw_writel((__force u32) cpu_to_le32(v),c)
> 
> The CPU is using explicit byteswaps, which matches what the QEMU driver
> is doing. Am I missing something?

You're not missing anything. I just made a mistake. Looks like I never
followed the implementation that far along for the BE archs.

Re: [PATCH 08/25] block/nvme: Simplify device reset
Posted by Keith Busch 5 years ago
On Tue, Oct 27, 2020 at 02:55:30PM +0100, Philippe Mathieu-Daudé wrote:
> Avoid multiple endianess conversion by using device endianess.
> 
> Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
> ---
>  block/nvme.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/block/nvme.c b/block/nvme.c
> index e95d59d3126..be14350f959 100644
> --- a/block/nvme.c
> +++ b/block/nvme.c
> @@ -755,7 +755,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
>      timeout_ms = MIN(500 * NVME_CAP_TO(cap), 30000);
>  
>      /* Reset device to get a clean state. */
> -    regs->cc = cpu_to_le32(le32_to_cpu(regs->cc) & 0xFE);
> +    regs->cc &= const_le32(0xFE);

This doesn't look right. The 'regs' is an MMIO address, correct? Memory
mappings use the CPU native.