diff mbox

[v5,3/6] iomap: introduce io{read|write}64_{lo_hi|hi_lo}

Message ID 20170726231917.6073-4-logang@deltatee.com (mailing list archive)
State Not Applicable
Delegated to: Herbert Xu
Headers show

Commit Message

Logan Gunthorpe July 26, 2017, 11:19 p.m. UTC
In order to provide non-atomic functions for io{read|write}64 that will
use readq and writeq when appropriate. We define a number of variants
of these functions in the generic iomap that will do non-atomic
operations on pio but atomic operations on mmio.

These functions are only defined if readq and writeq are defined. If
they are not, then the wrappers that always use non-atomic operations
from include/linux/io-64-nonatomic*.h will be used.

Signed-off-by: Logan Gunthorpe <logang@deltatee.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Suresh Warrier <warrier@linux.vnet.ibm.com>
Cc: Nicholas Piggin <npiggin@gmail.com>
---
 arch/powerpc/include/asm/io.h |   2 +
 include/asm-generic/iomap.h   |  26 +++++++--
 lib/iomap.c                   | 132 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 154 insertions(+), 6 deletions(-)

Comments

Andy Shevchenko July 30, 2017, 4:03 p.m. UTC | #1
On Thu, Jul 27, 2017 at 2:19 AM, Logan Gunthorpe <logang@deltatee.com> wrote:
> In order to provide non-atomic functions for io{read|write}64 that will
> use readq and writeq when appropriate. We define a number of variants
> of these functions in the generic iomap that will do non-atomic
> operations on pio but atomic operations on mmio.
>
> These functions are only defined if readq and writeq are defined. If
> they are not, then the wrappers that always use non-atomic operations
> from include/linux/io-64-nonatomic*.h will be used.

Don't you see here a slight problem?

In some cases we want to substitute atomic in favour of non-atomic
when both are defined.
So, please don't do this "smartly".

> +u64 ioread64_lo_hi(void __iomem *addr)
> +{
> +       IO_COND(addr, return pio_read64_lo_hi(port), return readq(addr));
> +       return 0xffffffffffffffffLL;
> +}

U missed u.
Logan Gunthorpe July 31, 2017, 3:55 p.m. UTC | #2
On 30/07/17 10:03 AM, Andy Shevchenko wrote:
> On Thu, Jul 27, 2017 at 2:19 AM, Logan Gunthorpe <logang@deltatee.com> wrote:
>> In order to provide non-atomic functions for io{read|write}64 that will
>> use readq and writeq when appropriate. We define a number of variants
>> of these functions in the generic iomap that will do non-atomic
>> operations on pio but atomic operations on mmio.
>>
>> These functions are only defined if readq and writeq are defined. If
>> they are not, then the wrappers that always use non-atomic operations
>> from include/linux/io-64-nonatomic*.h will be used.
> 
> Don't you see here a slight problem?
> 
> In some cases we want to substitute atomic in favour of non-atomic
> when both are defined.
> So, please don't do this "smartly".

I'm not sure what you mean here. The driver should use ioread64 and
include an io-64-nonatomic header. Then there are three cases:

1) The arch has no atomic 64 bit io operations defined. In this case it
uses the non-atomic inline function in the io-64-nonatomic header.

2) The arch uses CONFIG_GENERIC_IOMAP and has readq defined, but not
ioread64 defined (likely because pio can't do atomic 64 bit operations
but mmio can). In this case we need to use the ioread64_xx functions
defined in iomap.c which do atomic mmio and non-atomic pio.

3) The arch has ioread64 defined so the atomic operation is used.

>> +u64 ioread64_lo_hi(void __iomem *addr)
>> +{
>> +       IO_COND(addr, return pio_read64_lo_hi(port), return readq(addr));
>> +       return 0xffffffffffffffffLL;
>> +}
> 
> U missed u.

I'll fix this in the next revision.

Thanks,

Logan
Andy Shevchenko July 31, 2017, 4:10 p.m. UTC | #3
On Mon, Jul 31, 2017 at 6:55 PM, Logan Gunthorpe <logang@deltatee.com> wrote:
> On 30/07/17 10:03 AM, Andy Shevchenko wrote:
>> On Thu, Jul 27, 2017 at 2:19 AM, Logan Gunthorpe <logang@deltatee.com> wrote:
>>> In order to provide non-atomic functions for io{read|write}64 that will
>>> use readq and writeq when appropriate. We define a number of variants
>>> of these functions in the generic iomap that will do non-atomic
>>> operations on pio but atomic operations on mmio.
>>>
>>> These functions are only defined if readq and writeq are defined. If
>>> they are not, then the wrappers that always use non-atomic operations
>>> from include/linux/io-64-nonatomic*.h will be used.
>>
>> Don't you see here a slight problem?
>>
>> In some cases we want to substitute atomic in favour of non-atomic
>> when both are defined.
>> So, please don't do this "smartly".
>
> I'm not sure what you mean here. The driver should use ioread64 and
> include an io-64-nonatomic header. Then there are three cases:
>
> 1) The arch has no atomic 64 bit io operations defined. In this case it
> uses the non-atomic inline function in the io-64-nonatomic header.

Okay

> 2) The arch uses CONFIG_GENERIC_IOMAP and has readq defined, but not
> ioread64 defined (likely because pio can't do atomic 64 bit operations
> but mmio can). In this case we need to use the ioread64_xx functions
> defined in iomap.c which do atomic mmio and non-atomic pio.

Not okay.

Some drivers (hardware) would like to have non-atomic MMIO accesses
when readq() defined

> 3) The arch has ioread64 defined so the atomic operation is used.

Not okay. Same reason as above.

In case of readq() / writeq() it's defined by the order of inclusion:

1)
include <...non-atomic...>
include <linux/io.h>

Always non-atomic will be used.

2)
include <linux/io.h>
include <...non-atomic...>

Auto switch like you described.

I don't like above solution, since it's fragile, but few drivers depend on that.

If you wish to do it always like 2) perhaps we need to split accessors
to ones for fixed bus width and ones for atomic/non-atomic cases.
OTOH, it would be done by introducing
memcpyXX_fromio()
memcpyXX_toio()
memsetXX_io()

Where XX = 64, 32, 16, 8.

Note, that ioreadXX_rep() is not the same as above.

P.S. I have done a table of comparison between IO accessors in Linux
kernel and it looks hell out of being consistent.
Logan Gunthorpe July 31, 2017, 4:31 p.m. UTC | #4
On 31/07/17 10:10 AM, Andy Shevchenko wrote:
> Some drivers (hardware) would like to have non-atomic MMIO accesses
> when readq() defined

Huh? But that's the whole point of the io64-nonatomic header. If a
driver wants a specific non-atomic access they should just code two 32
bit accesses.

> In case of readq() / writeq() it's defined by the order of inclusion:
> 
> 1)
> include <...non-atomic...>
> include <linux/io.h>
> 
> Always non-atomic will be used.

I'm afraid you're wrong about this. The io-64-nonatomic-xx header
includes linux/io.h. Thus the order of the includes doesn't matter and
it will always auto switch. In any case, making an interface do
different things depending on the order of include files is *completely*
insane.

> P.S. I have done a table of comparison between IO accessors in Linux
> kernel and it looks hell out of being consistent.

There are a few corner oddities but it's really not that bad. Most
things are done for a reason if you dig into them.

Logan
Andy Shevchenko July 31, 2017, 5:58 p.m. UTC | #5
On Mon, Jul 31, 2017 at 7:31 PM, Logan Gunthorpe <logang@deltatee.com> wrote:
> On 31/07/17 10:10 AM, Andy Shevchenko wrote:
>> Some drivers (hardware) would like to have non-atomic MMIO accesses
>> when readq() defined
>
> Huh? But that's the whole point of the io64-nonatomic header. If a
> driver wants a specific non-atomic access they should just code two 32
> bit accesses.

You mean to call them directly as lo_hi_XXX() or hi_lo_XXX() ?
Yes it would work.

>> In case of readq() / writeq() it's defined by the order of inclusion:
>>
>> 1)
>> include <...non-atomic...>
>> include <linux/io.h>
>>
>> Always non-atomic will be used.
>
> I'm afraid you're wrong about this. The io-64-nonatomic-xx header
> includes linux/io.h. Thus the order of the includes doesn't matter and
> it will always auto switch. In any case, making an interface do
> different things depending on the order of include files is *completely*
> insane.

Yes, you are right. I was thinking about something unrelated.
Logan Gunthorpe July 31, 2017, 6 p.m. UTC | #6
On 31/07/17 11:58 AM, Andy Shevchenko wrote:
> On Mon, Jul 31, 2017 at 7:31 PM, Logan Gunthorpe <logang@deltatee.com> wrote:
>> On 31/07/17 10:10 AM, Andy Shevchenko wrote:
>>> Some drivers (hardware) would like to have non-atomic MMIO accesses
>>> when readq() defined
>>
>> Huh? But that's the whole point of the io64-nonatomic header. If a
>> driver wants a specific non-atomic access they should just code two 32
>> bit accesses.

> You mean to call them directly as lo_hi_XXX() or hi_lo_XXX() ?
> Yes it would work.

I suppose you could do that too but I really meant just using two io32
calls. That's the most explicit way to indicate you want a non-atomic
access.

Logan
Andy Shevchenko July 31, 2017, 6:03 p.m. UTC | #7
On Mon, Jul 31, 2017 at 9:00 PM, Logan Gunthorpe <logang@deltatee.com> wrote:
> On 31/07/17 11:58 AM, Andy Shevchenko wrote:
>> On Mon, Jul 31, 2017 at 7:31 PM, Logan Gunthorpe <logang@deltatee.com> wrote:
>>> On 31/07/17 10:10 AM, Andy Shevchenko wrote:
>>>> Some drivers (hardware) would like to have non-atomic MMIO accesses
>>>> when readq() defined
>>>
>>> Huh? But that's the whole point of the io64-nonatomic header. If a
>>> driver wants a specific non-atomic access they should just code two 32
>>> bit accesses.
>
>> You mean to call them directly as lo_hi_XXX() or hi_lo_XXX() ?
>> Yes it would work.
>
> I suppose you could do that too but I really meant just using two io32
> calls. That's the most explicit way to indicate you want a non-atomic
> access.

Per commit 3a044178cccf they are exactly created for this kind of cases.
Logan Gunthorpe July 31, 2017, 6:04 p.m. UTC | #8
On 31/07/17 12:03 PM, Andy Shevchenko wrote:
> 
> Per commit 3a044178cccf they are exactly created for this kind of cases.
> 

Sure, ok, and my patchset provides the same set of functions to satisfy
such a use.

Logan
Andy Shevchenko July 31, 2017, 6:11 p.m. UTC | #9
On Mon, Jul 31, 2017 at 9:04 PM, Logan Gunthorpe <logang@deltatee.com> wrote:
>
>
> On 31/07/17 12:03 PM, Andy Shevchenko wrote:
>>
>> Per commit 3a044178cccf they are exactly created for this kind of cases.
>>
>
> Sure, ok, and my patchset provides the same set of functions to satisfy
> such a use.

Okay, please, Cc me for next version, I think I need fresh view on it.
Thanks!
diff mbox

Patch

diff --git a/arch/powerpc/include/asm/io.h b/arch/powerpc/include/asm/io.h
index af074923d598..4cc420cfaa78 100644
--- a/arch/powerpc/include/asm/io.h
+++ b/arch/powerpc/include/asm/io.h
@@ -788,8 +788,10 @@  extern void __iounmap_at(void *ea, unsigned long size);
 
 #define mmio_read16be(addr)		readw_be(addr)
 #define mmio_read32be(addr)		readl_be(addr)
+#define mmio_read64be(addr)		readq_be(addr)
 #define mmio_write16be(val, addr)	writew_be(val, addr)
 #define mmio_write32be(val, addr)	writel_be(val, addr)
+#define mmio_write64be(val, addr)	writeq_be(val, addr)
 #define mmio_insb(addr, dst, count)	readsb(addr, dst, count)
 #define mmio_insw(addr, dst, count)	readsw(addr, dst, count)
 #define mmio_insl(addr, dst, count)	readsl(addr, dst, count)
diff --git a/include/asm-generic/iomap.h b/include/asm-generic/iomap.h
index 650fede33c25..30edebf627fe 100644
--- a/include/asm-generic/iomap.h
+++ b/include/asm-generic/iomap.h
@@ -30,9 +30,16 @@  extern unsigned int ioread16(void __iomem *);
 extern unsigned int ioread16be(void __iomem *);
 extern unsigned int ioread32(void __iomem *);
 extern unsigned int ioread32be(void __iomem *);
-#ifdef CONFIG_64BIT
-extern u64 ioread64(void __iomem *);
-extern u64 ioread64be(void __iomem *);
+
+#ifdef readq
+#define ioread64_lo_hi ioread64_lo_hi
+#define ioread64_hi_lo ioread64_hi_lo
+#define ioread64be_lo_hi ioread64be_lo_hi
+#define ioread64be_hi_lo ioread64be_hi_lo
+extern u64 ioread64_lo_hi(void __iomem *addr);
+extern u64 ioread64_hi_lo(void __iomem *addr);
+extern u64 ioread64be_lo_hi(void __iomem *addr);
+extern u64 ioread64be_hi_lo(void __iomem *addr);
 #endif
 
 extern void iowrite8(u8, void __iomem *);
@@ -40,9 +47,16 @@  extern void iowrite16(u16, void __iomem *);
 extern void iowrite16be(u16, void __iomem *);
 extern void iowrite32(u32, void __iomem *);
 extern void iowrite32be(u32, void __iomem *);
-#ifdef CONFIG_64BIT
-extern void iowrite64(u64, void __iomem *);
-extern void iowrite64be(u64, void __iomem *);
+
+#ifdef writeq
+#define iowrite64_lo_hi iowrite64_lo_hi
+#define iowrite64_hi_lo iowrite64_hi_lo
+#define iowrite64be_lo_hi iowrite64be_lo_hi
+#define iowrite64be_hi_lo iowrite64be_hi_lo
+extern void iowrite64_lo_hi(u64 val, void __iomem *addr);
+extern void iowrite64_hi_lo(u64 val, void __iomem *addr);
+extern void iowrite64be_lo_hi(u64 val, void __iomem *addr);
+extern void iowrite64be_hi_lo(u64 val, void __iomem *addr);
 #endif
 
 /*
diff --git a/lib/iomap.c b/lib/iomap.c
index fc3dcb4b238e..b993400d60bd 100644
--- a/lib/iomap.c
+++ b/lib/iomap.c
@@ -66,6 +66,7 @@  static void bad_io_access(unsigned long port, const char *access)
 #ifndef mmio_read16be
 #define mmio_read16be(addr) be16_to_cpu(__raw_readw(addr))
 #define mmio_read32be(addr) be32_to_cpu(__raw_readl(addr))
+#define mmio_read64be(addr) be64_to_cpu(__raw_readq(addr))
 #endif
 
 unsigned int ioread8(void __iomem *addr)
@@ -99,6 +100,80 @@  EXPORT_SYMBOL(ioread16be);
 EXPORT_SYMBOL(ioread32);
 EXPORT_SYMBOL(ioread32be);
 
+#ifdef readq
+static u64 pio_read64_lo_hi(unsigned long port)
+{
+	u64 lo, hi;
+
+	lo = inl(port);
+	hi = inl(port + sizeof(u32));
+
+	return lo | (hi << 32);
+}
+
+static u64 pio_read64_hi_lo(unsigned long port)
+{
+	u64 lo, hi;
+
+	hi = inl(port + sizeof(u32));
+	lo = inl(port);
+
+	return lo | (hi << 32);
+}
+
+static u64 pio_read64be_lo_hi(unsigned long port)
+{
+	u64 lo, hi;
+
+	lo = pio_read32be(port + sizeof(u32));
+	hi = pio_read32be(port);
+
+	return lo | (hi << 32);
+}
+
+static u64 pio_read64be_hi_lo(unsigned long port)
+{
+	u64 lo, hi;
+
+	hi = pio_read32be(port);
+	lo = pio_read32be(port + sizeof(u32));
+
+	return lo | (hi << 32);
+}
+
+u64 ioread64_lo_hi(void __iomem *addr)
+{
+	IO_COND(addr, return pio_read64_lo_hi(port), return readq(addr));
+	return 0xffffffffffffffffLL;
+}
+
+u64 ioread64_hi_lo(void __iomem *addr)
+{
+	IO_COND(addr, return pio_read64_hi_lo(port), return readq(addr));
+	return 0xffffffffffffffffLL;
+}
+
+u64 ioread64be_lo_hi(void __iomem *addr)
+{
+	IO_COND(addr, return pio_read64be_lo_hi(port),
+		return mmio_read64be(addr));
+	return 0xffffffffffffffffLL;
+}
+
+u64 ioread64be_hi_lo(void __iomem *addr)
+{
+	IO_COND(addr, return pio_read64be_hi_lo(port),
+		return mmio_read64be(addr));
+	return 0xffffffffffffffffLL;
+}
+
+EXPORT_SYMBOL(ioread64_lo_hi);
+EXPORT_SYMBOL(ioread64_hi_lo);
+EXPORT_SYMBOL(ioread64be_lo_hi);
+EXPORT_SYMBOL(ioread64be_hi_lo);
+
+#endif /* readq */
+
 #ifndef pio_write16be
 #define pio_write16be(val,port) outw(swab16(val),port)
 #define pio_write32be(val,port) outl(swab32(val),port)
@@ -107,6 +182,7 @@  EXPORT_SYMBOL(ioread32be);
 #ifndef mmio_write16be
 #define mmio_write16be(val,port) __raw_writew(be16_to_cpu(val),port)
 #define mmio_write32be(val,port) __raw_writel(be32_to_cpu(val),port)
+#define mmio_write64be(val,port) __raw_writeq(be64_to_cpu(val),port)
 #endif
 
 void iowrite8(u8 val, void __iomem *addr)
@@ -135,6 +211,62 @@  EXPORT_SYMBOL(iowrite16be);
 EXPORT_SYMBOL(iowrite32);
 EXPORT_SYMBOL(iowrite32be);
 
+#ifdef writeq
+static void pio_write64_lo_hi(u64 val, unsigned long port)
+{
+	outl(val, port);
+	outl(val >> 32, port + sizeof(u32));
+}
+
+static void pio_write64_hi_lo(u64 val, unsigned long port)
+{
+	outl(val >> 32, port + sizeof(u32));
+	outl(val, port);
+}
+
+static void pio_write64be_lo_hi(u64 val, unsigned long port)
+{
+	pio_write32be(val, port + sizeof(u32));
+	pio_write32be(val >> 32, port);
+}
+
+static void pio_write64be_hi_lo(u64 val, unsigned long port)
+{
+	pio_write32be(val >> 32, port);
+	pio_write32be(val, port + sizeof(u32));
+}
+
+void iowrite64_lo_hi(u64 val, void __iomem *addr)
+{
+	IO_COND(addr, pio_write64_lo_hi(val, port),
+		writeq(val, addr));
+}
+
+void iowrite64_hi_lo(u64 val, void __iomem *addr)
+{
+	IO_COND(addr, pio_write64_hi_lo(val, port),
+		writeq(val, addr));
+}
+
+void iowrite64be_lo_hi(u64 val, void __iomem *addr)
+{
+	IO_COND(addr, pio_write64be_lo_hi(val, port),
+		mmio_write64be(val, addr));
+}
+
+void iowrite64be_hi_lo(u64 val, void __iomem *addr)
+{
+	IO_COND(addr, pio_write64be_hi_lo(val, port),
+		mmio_write64be(val, addr));
+}
+
+EXPORT_SYMBOL(iowrite64_lo_hi);
+EXPORT_SYMBOL(iowrite64_hi_lo);
+EXPORT_SYMBOL(iowrite64be_lo_hi);
+EXPORT_SYMBOL(iowrite64be_hi_lo);
+
+#endif /* readq */
+
 /*
  * These are the "repeat MMIO read/write" functions.
  * Note the "__raw" accesses, since we don't want to