* Re: [PATCH v1 1/1] misc: IBM Virtual Management Channel Driver
From: Greg KH @ 2018-04-23 19:53 UTC (permalink / raw)
To: Randy Dunlap
Cc: Bryant G. Ly, benh, mpe, arnd, corbet, seroyer, mrochs, adreznec,
fbarrat, davem, linus.walleij, akpm, mikey, pombredanne, tlfalcon,
msuchanek, linux-doc, linuxppc-dev
In-Reply-To: <c07b14b7-8f34-2ba5-536f-85896ff21661@infradead.org>
On Mon, Apr 23, 2018 at 11:38:18AM -0700, Randy Dunlap wrote:
> On 04/23/18 07:46, Bryant G. Ly wrote:
> > This driver is a logical device which provides an
> > interface between the hypervisor and a management
> > partition.
> >
> > This driver is to be used for the POWER Virtual
> > Management Channel Virtual Adapter on the PowerVM
> > platform. It provides both request/response and
> > async message support through the /dev/ibmvmc node.
> >
> > Signed-off-by: Bryant G. Ly <bryantly@linux.vnet.ibm.com>
> > Reviewed-by: Steven Royer <seroyer@linux.vnet.ibm.com>
> > Reviewed-by: Adam Reznechek <adreznec@linux.vnet.ibm.com>
> > Tested-by: Taylor Jakobson <tjakobs@us.ibm.com>
> > Tested-by: Brad Warrum <bwarrum@us.ibm.com>
> > Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> > Cc: Arnd Bergmann <arnd@arndb.de>
> > Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> > Cc: Michael Ellerman <mpe@ellerman.id.au>
> > ---
> > Documentation/ioctl/ioctl-number.txt | 1 +
> > Documentation/misc-devices/ibmvmc.txt | 161 +++
> > MAINTAINERS | 6 +
> > arch/powerpc/include/asm/hvcall.h | 1 +
> > drivers/misc/Kconfig | 14 +
> > drivers/misc/Makefile | 1 +
> > drivers/misc/ibmvmc.c | 2415 +++++++++++++++++++++++++++++++++
> > drivers/misc/ibmvmc.h | 209 +++
> > 8 files changed, 2808 insertions(+)
> > create mode 100644 Documentation/misc-devices/ibmvmc.txt
> > create mode 100644 drivers/misc/ibmvmc.c
> > create mode 100644 drivers/misc/ibmvmc.h
>
> > diff --git a/Documentation/misc-devices/ibmvmc.txt b/Documentation/misc-devices/ibmvmc.txt
> > new file mode 100644
> > index 0000000..bae1064
> > --- /dev/null
> > +++ b/Documentation/misc-devices/ibmvmc.txt
Aren't we doing new documentation in .rst format instead of .txt?
thanks,
greg k-h
^ permalink raw reply
* [PATCH] powerpc/wii: Make hlwd_pic_init function static
From: Mathieu Malaterre @ 2018-04-23 19:45 UTC (permalink / raw)
To: Michael Ellerman
Cc: Mathieu Malaterre, Benjamin Herrenschmidt, Paul Mackerras,
linuxppc-dev, linux-kernel
The function hlwd_pic_init can be made static, so do it. Fix the following
warning treated as error (W=1):
../arch/powerpc/platforms/embedded6xx/hlwd-pic.c:158:20: error: no previous prototype for ‘hlwd_pic_init’ [-Werror=missing-prototypes]
Signed-off-by: Mathieu Malaterre <malat@debian.org>
---
arch/powerpc/platforms/embedded6xx/hlwd-pic.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/powerpc/platforms/embedded6xx/hlwd-pic.c b/arch/powerpc/platforms/embedded6xx/hlwd-pic.c
index 89c54de88b7a..e3e3af73e9d8 100644
--- a/arch/powerpc/platforms/embedded6xx/hlwd-pic.c
+++ b/arch/powerpc/platforms/embedded6xx/hlwd-pic.c
@@ -155,7 +155,7 @@ static void __hlwd_quiesce(void __iomem *io_base)
out_be32(io_base + HW_BROADWAY_ICR, 0xffffffff);
}
-struct irq_domain *hlwd_pic_init(struct device_node *np)
+static struct irq_domain *hlwd_pic_init(struct device_node *np)
{
struct irq_domain *irq_domain;
struct resource res;
--
2.11.0
^ permalink raw reply related
* [PATCH v2] powerpc/mm/radix: use do/while(0) trick for single statement block
From: Mathieu Malaterre @ 2018-04-23 19:36 UTC (permalink / raw)
To: Michael Ellerman
Cc: Randy Dunlap, Mathieu Malaterre, Benjamin Herrenschmidt,
Paul Mackerras, linuxppc-dev, linux-kernel
In-Reply-To: <20180408194424.8719-1-malat@debian.org>
In commit 7a22d6321c3d ("powerpc/mm/radix: Update command line parsing for
disable_radix") an `if` statement was added for a possible empty body
(prom_debug).
Fix the following warning, treated as error with W=1:
arch/powerpc/kernel/prom_init.c:656:46: error: suggest braces around empty body in an ‘if’ statement [-Werror=empty-body]
Suggested-by: Randy Dunlap <rdunlap@infradead.org>
Signed-off-by: Mathieu Malaterre <malat@debian.org>
---
v2: update macro directly
arch/powerpc/kernel/prom_init.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c
index 5ae153b97d0a..7edf3aa5bc6d 100644
--- a/arch/powerpc/kernel/prom_init.c
+++ b/arch/powerpc/kernel/prom_init.c
@@ -103,7 +103,7 @@ int of_workarounds;
#ifdef DEBUG_PROM
#define prom_debug(x...) prom_printf(x)
#else
-#define prom_debug(x...)
+#define prom_debug(x...) do { } while (0)
#endif
--
2.11.0
^ permalink raw reply related
* Re: [PATCH] powerpc/mm/radix: add missing braces for single statement block
From: Mathieu Malaterre @ 2018-04-23 19:34 UTC (permalink / raw)
To: Randy Dunlap
Cc: Michael Ellerman, Benjamin Herrenschmidt, Paul Mackerras,
linuxppc-dev, LKML, Joe Perches
In-Reply-To: <CA+7wUsz6m14gCd79NKzKbad1e+1P_=t1-B4na7g5ob16ORQ_8g@mail.gmail.com>
On Mon, Apr 23, 2018 at 9:31 PM, Mathieu Malaterre <malat@debian.org> wrote=
:
> On Sun, Apr 8, 2018 at 10:34 PM, Randy Dunlap <rdunlap@infradead.org> wro=
te:
>> On 04/08/2018 12:44 PM, Mathieu Malaterre wrote:
>>> In commit 7a22d6321c3d ("powerpc/mm/radix: Update command line parsing =
for
>>> disable_radix") an `if` statement was added for a possible empty body
>>> (prom_debug).
>>>
>>> Fix the following warning, treated as error with W=3D1:
>>>
>>> arch/powerpc/kernel/prom_init.c:656:46: error: suggest braces around =
empty body in an =E2=80=98if=E2=80=99 statement [-Werror=3Dempty-body]
>>>
>>> Signed-off-by: Mathieu Malaterre <malat@debian.org>
>>> ---
>>> arch/powerpc/kernel/prom_init.c | 3 ++-
>>> 1 file changed, 2 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom=
_init.c
>>> index 5ae153b97d0a..f0e802495530 100644
>>> --- a/arch/powerpc/kernel/prom_init.c
>>> +++ b/arch/powerpc/kernel/prom_init.c
>>> @@ -652,8 +652,9 @@ static void __init early_cmdline_parse(void)
>>> } else
>>> prom_radix_disable =3D true;
>>> }
>>> - if (prom_radix_disable)
>>> + if (prom_radix_disable) {
>>> prom_debug("Radix disabled from cmdline\n");
>>
>> Looks like the macro for #prom_debug() should be fixed instead.
>
> Well if I try instead:
>
> @@ -101,9 +101,9 @@ int of_workarounds;
> } while (0)
>
> #ifdef DEBUG_PROM
> -#define prom_debug(x...) prom_printf(x)
> +#define prom_debug(x...) do { prom_printf(x); } while (0)
> #else
> -#define prom_debug(x...)
> +#define prom_debug(x...) do { } while (0)
> #endif
>
>
> the checkpatch script returns:
>
> WARNING: Single statement macros should not use a do {} while (0) loop
> #33: FILE: arch/powerpc/kernel/prom_init.c:104:
> +#define prom_debug(x...) do { prom_printf(x); } while (0)
>
>
> So I suspect we cannot do much better than my original patch unfortunatel=
y.
Right, it seems I cannot read the correct line number for a warning.
An updated patch is coming.
>>> + }
>>> }
>>>
>>> #if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV)
>>>
>>
>>
>> --
>> ~Randy
^ permalink raw reply
* Re: [PATCH] powerpc/mm/radix: add missing braces for single statement block
From: Mathieu Malaterre @ 2018-04-23 19:31 UTC (permalink / raw)
To: Randy Dunlap
Cc: Michael Ellerman, Benjamin Herrenschmidt, Paul Mackerras,
linuxppc-dev, LKML, Joe Perches
In-Reply-To: <a21ced59-8cf6-b8e0-2305-fb9d040dfc5c@infradead.org>
On Sun, Apr 8, 2018 at 10:34 PM, Randy Dunlap <rdunlap@infradead.org> wrote=
:
> On 04/08/2018 12:44 PM, Mathieu Malaterre wrote:
>> In commit 7a22d6321c3d ("powerpc/mm/radix: Update command line parsing f=
or
>> disable_radix") an `if` statement was added for a possible empty body
>> (prom_debug).
>>
>> Fix the following warning, treated as error with W=3D1:
>>
>> arch/powerpc/kernel/prom_init.c:656:46: error: suggest braces around e=
mpty body in an =E2=80=98if=E2=80=99 statement [-Werror=3Dempty-body]
>>
>> Signed-off-by: Mathieu Malaterre <malat@debian.org>
>> ---
>> arch/powerpc/kernel/prom_init.c | 3 ++-
>> 1 file changed, 2 insertions(+), 1 deletion(-)
>>
>> diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_=
init.c
>> index 5ae153b97d0a..f0e802495530 100644
>> --- a/arch/powerpc/kernel/prom_init.c
>> +++ b/arch/powerpc/kernel/prom_init.c
>> @@ -652,8 +652,9 @@ static void __init early_cmdline_parse(void)
>> } else
>> prom_radix_disable =3D true;
>> }
>> - if (prom_radix_disable)
>> + if (prom_radix_disable) {
>> prom_debug("Radix disabled from cmdline\n");
>
> Looks like the macro for #prom_debug() should be fixed instead.
Well if I try instead:
@@ -101,9 +101,9 @@ int of_workarounds;
} while (0)
#ifdef DEBUG_PROM
-#define prom_debug(x...) prom_printf(x)
+#define prom_debug(x...) do { prom_printf(x); } while (0)
#else
-#define prom_debug(x...)
+#define prom_debug(x...) do { } while (0)
#endif
the checkpatch script returns:
WARNING: Single statement macros should not use a do {} while (0) loop
#33: FILE: arch/powerpc/kernel/prom_init.c:104:
+#define prom_debug(x...) do { prom_printf(x); } while (0)
So I suspect we cannot do much better than my original patch unfortunately.
>> + }
>> }
>>
>> #if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV)
>>
>
>
> --
> ~Randy
^ permalink raw reply
* Re: [PATCH v1 1/1] misc: IBM Virtual Management Channel Driver
From: Randy Dunlap @ 2018-04-23 18:38 UTC (permalink / raw)
To: Bryant G. Ly, benh, mpe, gregkh, arnd
Cc: corbet, seroyer, mrochs, adreznec, fbarrat, davem, linus.walleij,
akpm, mikey, pombredanne, tlfalcon, msuchanek, linux-doc,
linuxppc-dev
In-Reply-To: <1524494812-60150-2-git-send-email-bryantly@linux.vnet.ibm.com>
On 04/23/18 07:46, Bryant G. Ly wrote:
> This driver is a logical device which provides an
> interface between the hypervisor and a management
> partition.
>
> This driver is to be used for the POWER Virtual
> Management Channel Virtual Adapter on the PowerVM
> platform. It provides both request/response and
> async message support through the /dev/ibmvmc node.
>
> Signed-off-by: Bryant G. Ly <bryantly@linux.vnet.ibm.com>
> Reviewed-by: Steven Royer <seroyer@linux.vnet.ibm.com>
> Reviewed-by: Adam Reznechek <adreznec@linux.vnet.ibm.com>
> Tested-by: Taylor Jakobson <tjakobs@us.ibm.com>
> Tested-by: Brad Warrum <bwarrum@us.ibm.com>
> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Cc: Arnd Bergmann <arnd@arndb.de>
> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> Cc: Michael Ellerman <mpe@ellerman.id.au>
> ---
> Documentation/ioctl/ioctl-number.txt | 1 +
> Documentation/misc-devices/ibmvmc.txt | 161 +++
> MAINTAINERS | 6 +
> arch/powerpc/include/asm/hvcall.h | 1 +
> drivers/misc/Kconfig | 14 +
> drivers/misc/Makefile | 1 +
> drivers/misc/ibmvmc.c | 2415 +++++++++++++++++++++++++++++++++
> drivers/misc/ibmvmc.h | 209 +++
> 8 files changed, 2808 insertions(+)
> create mode 100644 Documentation/misc-devices/ibmvmc.txt
> create mode 100644 drivers/misc/ibmvmc.c
> create mode 100644 drivers/misc/ibmvmc.h
> diff --git a/Documentation/misc-devices/ibmvmc.txt b/Documentation/misc-devices/ibmvmc.txt
> new file mode 100644
> index 0000000..bae1064
> --- /dev/null
> +++ b/Documentation/misc-devices/ibmvmc.txt
> @@ -0,0 +1,161 @@
> +Kernel Driver ibmvmc
> +====================
> +
> +Authors:
> + Dave Engebretsen <engebret@us.ibm.com>
> + Adam Reznechek <adreznec@linux.vnet.ibm.com>
> + Steven Royer <seroyer@linux.vnet.ibm.com>
> + Bryant G. Ly <bryantly@linux.vnet.ibm.com>
> +
> +Description
> +===========
> +
...
> +
> +Virtual Management Channel (VMC)
> +A logical device, called the virtual management channel (VMC), is defined
> +for communicating between the Novalink application and the hypervisor.
> +This device, similar to a VSCSI server device, is presented to a designated
> +management partition as a virtual device and is only presented when the
> +system is not HMC managed.
> +This communication device borrows aspects from both VSCSI and ILLAN devices
> +and is implemented using the CRQ and the RDMA interfaces. A three-way
> +handshake is defined that must take place to establish that both the
> +hypervisor and management partition sides of the channel are running prior
> +to sending/receiving any of the protocol messages.
> +This driver also utilizes Transport Event CRQs. CRQ messages that are sent
Drop "that" ?
otherwise the sentence construction is ... odd.
> +when the hypervisor detects one of the peer partitions has abnormally
> +terminated, or one side has called H_FREE_CRQ to close their CRQ.
> +Two new classes of CRQ messages are introduced for the VMC device. VMC
> +Administrative messages are used for each partition using the VMC to
> +communicate capabilities to their partner. HMC Interface messages are used
> +for the actual flow of HMC messages between the management partition and
> +the hypervisor. As most HMC messages far exceed the size of a CRQ bugger,
what is a bugger?
[reads more]
oh, buffer?
> +a virtual DMA (RMDA) of the HMC message data is done prior to each HMC
> +Interface CRQ message. Only the management partition drives RDMA
> +operations; hypervisors never directly causes the movement of message data.
> +
> +Example Management Partition VMC Driver Interface
> +=================================================
...
Looks pretty good. In general (IMO), it could use more white space (like blank
lines after section names.)
If you fix those small items (buggers) above, you can add:
Reviewed-by: Randy Dunlap <rdunlap@infradead.org>
thanks,
--
~Randy
^ permalink raw reply
* Re: [PATCH v10 01/25] mm: introduce CONFIG_SPECULATIVE_PAGE_FAULT
From: Laurent Dufour @ 2018-04-23 15:10 UTC (permalink / raw)
To: Minchan Kim
Cc: akpm, mhocko, peterz, kirill, ak, dave, jack, Matthew Wilcox,
benh, mpe, paulus, Thomas Gleixner, Ingo Molnar, hpa, Will Deacon,
Sergey Senozhatsky, Andrea Arcangeli, Alexei Starovoitov,
kemi.wang, sergey.senozhatsky.work, Daniel Jordan, David Rientjes,
Jerome Glisse, Ganesh Mahendran, linux-kernel, linux-mm, haren,
khandual, npiggin, bsingharora, paulmck, Tim Chen, linuxppc-dev,
x86
In-Reply-To: <20180423055809.GA114098@rodete-desktop-imager.corp.google.com>
On 23/04/2018 07:58, Minchan Kim wrote:
> Hi Laurent,
>
> I guess it's good timing to review. Guess LSF/MM goes so might change
> a lot since then. :) Anyway, I grap a time to review.
Hi,
Thanks a lot for reviewing this series.
> On Tue, Apr 17, 2018 at 04:33:07PM +0200, Laurent Dufour wrote:
>> This configuration variable will be used to build the code needed to
>> handle speculative page fault.
>>
>> By default it is turned off, and activated depending on architecture
>> support, SMP and MMU.
>
> Can we have description in here why it depends on architecture?
The reason is that the per architecture page fault code must handle the
speculative page fault. It is done in this series for x86 and ppc64.
I'll make it explicit here.
>
>>
>> Suggested-by: Thomas Gleixner <tglx@linutronix.de>
>> Suggested-by: David Rientjes <rientjes@google.com>
>> Signed-off-by: Laurent Dufour <ldufour@linux.vnet.ibm.com>
>> ---
>> mm/Kconfig | 22 ++++++++++++++++++++++
>> 1 file changed, 22 insertions(+)
>>
>> diff --git a/mm/Kconfig b/mm/Kconfig
>> index d5004d82a1d6..5484dca11199 100644
>> --- a/mm/Kconfig
>> +++ b/mm/Kconfig
>> @@ -752,3 +752,25 @@ config GUP_BENCHMARK
>> performance of get_user_pages_fast().
>>
>> See tools/testing/selftests/vm/gup_benchmark.c
>> +
>> +config ARCH_SUPPORTS_SPECULATIVE_PAGE_FAULT
>> + def_bool n
>> +
>> +config SPECULATIVE_PAGE_FAULT
>> + bool "Speculative page faults"
>> + default y
>> + depends on ARCH_SUPPORTS_SPECULATIVE_PAGE_FAULT
>> + depends on MMU && SMP
>> + help
>> + Try to handle user space page faults without holding the mmap_sem.
>> +
>> + This should allow better concurrency for massively threaded process
>> + since the page fault handler will not wait for other threads memory
>> + layout change to be done, assuming that this change is done in another
>> + part of the process's memory space. This type of page fault is named
>> + speculative page fault.
>> +
>> + If the speculative page fault fails because of a concurrency is
>> + detected or because underlying PMD or PTE tables are not yet
>> + allocating, it is failing its processing and a classic page fault
>> + is then tried.
>> --
>> 2.7.4
>>
>
^ permalink raw reply
* [PATCH v1 1/1] misc: IBM Virtual Management Channel Driver
From: Bryant G. Ly @ 2018-04-23 14:46 UTC (permalink / raw)
To: benh, mpe, gregkh, arnd
Cc: corbet, seroyer, mrochs, adreznec, fbarrat, davem, linus.walleij,
akpm, rdunlap, mikey, pombredanne, tlfalcon, msuchanek, linux-doc,
linuxppc-dev, Bryant G. Ly
In-Reply-To: <1524494812-60150-1-git-send-email-bryantly@linux.vnet.ibm.com>
This driver is a logical device which provides an
interface between the hypervisor and a management
partition.
This driver is to be used for the POWER Virtual
Management Channel Virtual Adapter on the PowerVM
platform. It provides both request/response and
async message support through the /dev/ibmvmc node.
Signed-off-by: Bryant G. Ly <bryantly@linux.vnet.ibm.com>
Reviewed-by: Steven Royer <seroyer@linux.vnet.ibm.com>
Reviewed-by: Adam Reznechek <adreznec@linux.vnet.ibm.com>
Tested-by: Taylor Jakobson <tjakobs@us.ibm.com>
Tested-by: Brad Warrum <bwarrum@us.ibm.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
---
Documentation/ioctl/ioctl-number.txt | 1 +
Documentation/misc-devices/ibmvmc.txt | 161 +++
MAINTAINERS | 6 +
arch/powerpc/include/asm/hvcall.h | 1 +
drivers/misc/Kconfig | 14 +
drivers/misc/Makefile | 1 +
drivers/misc/ibmvmc.c | 2415 +++++++++++++++++++++++++++++++++
drivers/misc/ibmvmc.h | 209 +++
8 files changed, 2808 insertions(+)
create mode 100644 Documentation/misc-devices/ibmvmc.txt
create mode 100644 drivers/misc/ibmvmc.c
create mode 100644 drivers/misc/ibmvmc.h
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 84bb74d..9851cee 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -329,6 +329,7 @@ Code Seq#(hex) Include File Comments
0xCA 80-BF uapi/scsi/cxlflash_ioctl.h
0xCB 00-1F CBM serial IEC bus in development:
<mailto:michael.klein@puffin.lb.shuttle.de>
+0xCC 00-0F drivers/misc/ibmvmc.h pseries VMC driver
0xCD 01 linux/reiserfs_fs.h
0xCF 02 fs/cifs/ioctl.c
0xDB 00-0F drivers/char/mwave/mwavepub.h
diff --git a/Documentation/misc-devices/ibmvmc.txt b/Documentation/misc-devices/ibmvmc.txt
new file mode 100644
index 0000000..bae1064
--- /dev/null
+++ b/Documentation/misc-devices/ibmvmc.txt
@@ -0,0 +1,161 @@
+Kernel Driver ibmvmc
+====================
+
+Authors:
+ Dave Engebretsen <engebret@us.ibm.com>
+ Adam Reznechek <adreznec@linux.vnet.ibm.com>
+ Steven Royer <seroyer@linux.vnet.ibm.com>
+ Bryant G. Ly <bryantly@linux.vnet.ibm.com>
+
+Description
+===========
+
+The Virtual Management Channel (VMC) is a logical device which provides an
+interface between the hypervisor and a management partition. This
+management partition is intended to provide an alternative to HMC-based
+system management. In the management partition, a Novalink application
+exists which enables a system administrator to configure the system’s
+partitioning characteristics via a command line interface (CLI) or
+Representational State Transfer Application (REST). You can also manage the
+server by using PowerVC or other OpenStack solution. Support for
+conventional HMC management of the system may still be provided on a
+system; however, when an HMC is attached to the system, the VMC
+interface is disabled by the hypervisor.
+
+NovaLink runs on a Linux logical partition on a POWER8 or newer processor-
+based server that is virtualized by PowerVM. System configuration,
+maintenance, and control functions which traditionally require an HMC can
+be implemented in the Novalink using a combination of HMC to hypervisor
+interfaces and existing operating system methods. This tool provides a
+subset of the functions implemented by the HMC and enables basic partition
+configuration. The set of HMC to hypervisor messages supported by the
+Novalink component are passed to the hypervisor over a VMC interface, which
+is defined below.
+
+Virtual Management Channel (VMC)
+A logical device, called the virtual management channel (VMC), is defined
+for communicating between the Novalink application and the hypervisor.
+This device, similar to a VSCSI server device, is presented to a designated
+management partition as a virtual device and is only presented when the
+system is not HMC managed.
+This communication device borrows aspects from both VSCSI and ILLAN devices
+and is implemented using the CRQ and the RDMA interfaces. A three-way
+handshake is defined that must take place to establish that both the
+hypervisor and management partition sides of the channel are running prior
+to sending/receiving any of the protocol messages.
+This driver also utilizes Transport Event CRQs. CRQ messages that are sent
+when the hypervisor detects one of the peer partitions has abnormally
+terminated, or one side has called H_FREE_CRQ to close their CRQ.
+Two new classes of CRQ messages are introduced for the VMC device. VMC
+Administrative messages are used for each partition using the VMC to
+communicate capabilities to their partner. HMC Interface messages are used
+for the actual flow of HMC messages between the management partition and
+the hypervisor. As most HMC messages far exceed the size of a CRQ bugger,
+a virtual DMA (RMDA) of the HMC message data is done prior to each HMC
+Interface CRQ message. Only the management partition drives RDMA
+operations; hypervisors never directly causes the movement of message data.
+
+Example Management Partition VMC Driver Interface
+=================================================
+This section provides an example for the Novalink implementation where a
+device driver is used to interface to the VMC device. This driver
+consists of a new device, for example /dev/lparvmc, which provides
+interfaces to open, close, read, write, and perform ioctl’s against the
+VMC device.
+
+VMC Interface Initialization
+The device driver is responsible for initializing the VMC when the driver
+is loaded. It first creates and initializes the CRQ. Next, an exchange of
+VMC capabilities is performed to indicate the code version and number of
+resources available in both the management partition and the hypervisor.
+Finally, the hypervisor requests that the management partition create an
+initial pool of VMC buffers, one buffer for each possible HMC connection,
+which will be used for Novalink session initialization. Prior to completion
+of this initialization sequence, the device returns EBUSY to open() calls.
+EIO is returned for all open() failures.
+
+Management Partition Hypervisor
+ CRQ INIT
+---------------------------------------->
+ CRQ INIT COMPLETE
+<----------------------------------------
+ CAPABILITIES
+---------------------------------------->
+ CAPABILITIES RESPONSE
+<----------------------------------------
+ ADD BUFFER (MHC IDX=0,1,..) _
+<---------------------------------------- |
+ ADD BUFFER RESPONSE | - Perform # HMCs Iterations
+----------------------------------------> -
+
+VMC Interface Open
+After the basic VMC channel has been initialized, an HMC session level
+connection can be established. The application layer performs an open() to
+the VMC device and executes an ioctl() against it, indicating the HMC ID
+(32 bytes of data) for this session. If the VMC device is in an invalid
+state, EIO will be returned for the ioctl(). The device driver creates a
+new HMC session value (ranging from 1 to 255) and HMC index value (starting
+at index 0 and potentially ranging to 254 in future releases) for this HMC
+ID. The driver then does an RDMA of the HMC ID to the hypervisor, and then
+sends an Interface Open message to the hypervisor to establish the session
+over the VMC. After the hypervisor receives this information, it sends Add
+Buffer messages to the management partition to seed an initial pool of
+buffers for the new HMC connection. Finally, the hypervisor sends an
+Interface Open Response message, to indicate that it is ready for normal
+runtime messaging. The following illustrates this VMC flow:
+
+Management Partition Hypervisor
+ RDMA HMC ID
+---------------------------------------->
+ Interface Open
+---------------------------------------->
+ Add Buffer _
+<---------------------------------------- |
+ Add Buffer Response | - Perform N Iterations
+----------------------------------------> -
+ Interface Open Response
+<----------------------------------------
+
+VMC Interface Runtime
+During normal runtime, the Novalink application and the hypervisor
+exchange HMC messages via the Signal VMC message and RDMA operations. When
+sending data to the hypervisor, the Novalink application performs a
+write() to the VMC device, and the driver RDMA’s the data to the hypervisor
+and then sends a Signal Message. If a write() is attempted before VMC
+device buffers have been made available by the hypervisor, or no buffers
+are currently available, EBUSY is returned in response to the write(). A
+write() will return EIO for all other errors, such as an invalid device
+state. When the hypervisor sends a message to the Novalink, the data is
+put into a VMC buffer and an Signal Message is sent to the VMC driver in
+the management partition. The driver RDMA’s the buffer into the partition
+and passes the data up to the appropriate Novalink application via a
+read() to the VMC device. The read() request blocks if there is no buffer
+available to read. The Novalink application may use select() to wait for
+the VMC device to become ready with data to read.
+
+Management Partition Hypervisor
+ MSG RDMA
+---------------------------------------->
+ SIGNAL MSG
+---------------------------------------->
+ SIGNAL MSG
+<----------------------------------------
+ MSG RDMA
+<----------------------------------------
+
+VMC Interface Close
+HMC session level connections are closed by the management partition when
+the application layer performs a close() against the device. This action
+results in an Interface Close message flowing to the hypervisor, which
+causes the session to be terminated. The device driver must free any
+storage allocated for buffers for this HMC connection.
+
+Management Partition Hypervisor
+ INTERFACE CLOSE
+---------------------------------------->
+ INTERFACE CLOSE RESPONSE
+<----------------------------------------
+
+For more information on the documentation for CRQ Messages, VMC Messages,
+HMC interface Buffers, and signal messages please refer to the Linux on
+Power Architecture Platform Reference. Section F.
diff --git a/MAINTAINERS b/MAINTAINERS
index c7182d2..79969f9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6721,6 +6721,12 @@ L: linux-scsi@vger.kernel.org
S: Supported
F: drivers/scsi/ibmvscsi/ibmvfc*
+IBM Power Virtual Management Channel Driver
+M: Bryant G. Ly <bryantly@linux.vnet.ibm.com>
+M: Steven Royer <seroyer@linux.vnet.ibm.com>
+S: Supported
+F: drivers/misc/ibmvmc.*
+
IBM Power Virtual SCSI Device Drivers
M: Tyrel Datwyler <tyreld@linux.vnet.ibm.com>
L: linux-scsi@vger.kernel.org
diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h
index 2e2ddda..662c834 100644
--- a/arch/powerpc/include/asm/hvcall.h
+++ b/arch/powerpc/include/asm/hvcall.h
@@ -279,6 +279,7 @@
#define H_GET_MPP_X 0x314
#define H_SET_MODE 0x31C
#define H_CLEAR_HPT 0x358
+#define H_REQUEST_VMC 0x360
#define H_RESIZE_HPT_PREPARE 0x36C
#define H_RESIZE_HPT_COMMIT 0x370
#define H_REGISTER_PROC_TBL 0x37C
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 5d71300..3726eac 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -113,6 +113,20 @@ config IBM_ASM
for information on the specific driver level and support statement
for your IBM server.
+config IBMVMC
+ tristate "IBM Virtual Management Channel support"
+ depends on PPC_PSERIES
+ help
+ This is the IBM POWER Virtual Management Channel
+
+ This driver is to be used for the POWER Virtual
+ Management Channel virtual adapter on the PowerVM
+ platform. It provides both request/response and
+ async message support through the /dev/ibmvmc node.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ibmvmc.
+
config PHANTOM
tristate "Sensable PHANToM (PCI)"
depends on PCI
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 20be70c..af22bbc 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -4,6 +4,7 @@
#
obj-$(CONFIG_IBM_ASM) += ibmasm/
+obj-$(CONFIG_IBMVMC) += ibmvmc.o
obj-$(CONFIG_AD525X_DPOT) += ad525x_dpot.o
obj-$(CONFIG_AD525X_DPOT_I2C) += ad525x_dpot-i2c.o
obj-$(CONFIG_AD525X_DPOT_SPI) += ad525x_dpot-spi.o
diff --git a/drivers/misc/ibmvmc.c b/drivers/misc/ibmvmc.c
new file mode 100644
index 0000000..ae9cea3
--- /dev/null
+++ b/drivers/misc/ibmvmc.c
@@ -0,0 +1,2415 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * IBM Power Systems Virtual Management Channel Support.
+ *
+ * Copyright (c) 2004, 2018 IBM Corp.
+ * Dave Engebretsen engebret@us.ibm.com
+ * Steven Royer seroyer@linux.vnet.ibm.com
+ * Adam Reznechek adreznec@linux.vnet.ibm.com
+ * Bryant G. Ly <bryantly@linux.vnet.ibm.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/percpu.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/miscdevice.h>
+#include <linux/sched/signal.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/vio.h>
+
+#include "ibmvmc.h"
+
+#define IBMVMC_DRIVER_VERSION "1.0"
+
+/*
+ * Static global variables
+ */
+static DECLARE_WAIT_QUEUE_HEAD(ibmvmc_read_wait);
+
+static const char ibmvmc_driver_name[] = "ibmvmc";
+
+static struct ibmvmc_struct ibmvmc;
+static struct ibmvmc_hmc hmcs[MAX_HMCS];
+static struct crq_server_adapter ibmvmc_adapter;
+
+static int ibmvmc_max_buf_pool_size = DEFAULT_BUF_POOL_SIZE;
+static int ibmvmc_max_hmcs = DEFAULT_HMCS;
+static int ibmvmc_max_mtu = DEFAULT_MTU;
+
+static inline long h_copy_rdma(s64 length, u64 sliobn, u64 slioba,
+ u64 dliobn, u64 dlioba)
+{
+ long rc = 0;
+
+ /* Ensure all writes to source memory are visible before hcall */
+ dma_wmb();
+ pr_debug("ibmvmc: h_copy_rdma(0x%llx, 0x%llx, 0x%llx, 0x%llx, 0x%llx\n",
+ length, sliobn, slioba, dliobn, dlioba);
+ rc = plpar_hcall_norets(H_COPY_RDMA, length, sliobn, slioba,
+ dliobn, dlioba);
+ pr_debug("ibmvmc: h_copy_rdma rc = 0x%lx\n", rc);
+
+ return rc;
+}
+
+static inline void h_free_crq(uint32_t unit_address)
+{
+ long rc = 0;
+
+ do {
+ if (H_IS_LONG_BUSY(rc))
+ msleep(get_longbusy_msecs(rc));
+
+ rc = plpar_hcall_norets(H_FREE_CRQ, unit_address);
+ } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc)));
+}
+
+/**
+ * h_request_vmc: - request a hypervisor virtual management channel device
+ * @vmc_index: drc index of the vmc device created
+ *
+ * Requests the hypervisor create a new virtual management channel device,
+ * allowing this partition to send hypervisor virtualization control
+ * commands.
+ *
+ * Return:
+ * 0 - Success
+ * Non-zero - Failure
+ */
+static inline long h_request_vmc(u32 *vmc_index)
+{
+ long rc = 0;
+ unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
+
+ do {
+ if (H_IS_LONG_BUSY(rc))
+ msleep(get_longbusy_msecs(rc));
+
+ /* Call to request the VMC device from phyp */
+ rc = plpar_hcall(H_REQUEST_VMC, retbuf);
+ pr_debug("ibmvmc: %s rc = 0x%lx\n", __func__, rc);
+ *vmc_index = retbuf[0];
+ } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc)));
+
+ return rc;
+}
+
+/* routines for managing a command/response queue */
+/**
+ * ibmvmc_handle_event: - Interrupt handler for crq events
+ * @irq: number of irq to handle, not used
+ * @dev_instance: crq_server_adapter that received interrupt
+ *
+ * Disables interrupts and schedules ibmvmc_task
+ *
+ * Always returns IRQ_HANDLED
+ */
+static irqreturn_t ibmvmc_handle_event(int irq, void *dev_instance)
+{
+ struct crq_server_adapter *adapter =
+ (struct crq_server_adapter *)dev_instance;
+
+ vio_disable_interrupts(to_vio_dev(adapter->dev));
+ tasklet_schedule(&adapter->work_task);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ibmvmc_release_crq_queue - Release CRQ Queue
+ *
+ * @adapter: crq_server_adapter struct
+ *
+ * Return:
+ * 0 - Success
+ * Non-Zero - Failure
+ */
+static void ibmvmc_release_crq_queue(struct crq_server_adapter *adapter)
+{
+ struct vio_dev *vdev = to_vio_dev(adapter->dev);
+ struct crq_queue *queue = &adapter->queue;
+
+ free_irq(vdev->irq, (void *)adapter);
+ tasklet_kill(&adapter->work_task);
+
+ if (adapter->reset_task)
+ kthread_stop(adapter->reset_task);
+
+ h_free_crq(vdev->unit_address);
+ dma_unmap_single(adapter->dev,
+ queue->msg_token,
+ queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL);
+ free_page((unsigned long)queue->msgs);
+}
+
+/**
+ * ibmvmc_reset_crq_queue - Reset CRQ Queue
+ *
+ * @adapter: crq_server_adapter struct
+ *
+ * This function calls h_free_crq and then calls H_REG_CRQ and does all the
+ * bookkeeping to get us back to where we can communicate.
+ *
+ * Return:
+ * 0 - Success
+ * Non-Zero - Failure
+ */
+static int ibmvmc_reset_crq_queue(struct crq_server_adapter *adapter)
+{
+ struct vio_dev *vdev = to_vio_dev(adapter->dev);
+ struct crq_queue *queue = &adapter->queue;
+ int rc = 0;
+
+ /* Close the CRQ */
+ h_free_crq(vdev->unit_address);
+
+ /* Clean out the queue */
+ memset(queue->msgs, 0x00, PAGE_SIZE);
+ queue->cur = 0;
+
+ /* And re-open it again */
+ rc = plpar_hcall_norets(H_REG_CRQ,
+ vdev->unit_address,
+ queue->msg_token, PAGE_SIZE);
+ if (rc == 2)
+ /* Adapter is good, but other end is not ready */
+ dev_warn(adapter->dev, "Partner adapter not ready\n");
+ else if (rc != 0)
+ dev_err(adapter->dev, "couldn't register crq--rc 0x%x\n", rc);
+
+ return rc;
+}
+
+/**
+ * crq_queue_next_crq: - Returns the next entry in message queue
+ * @queue: crq_queue to use
+ *
+ * Returns pointer to next entry in queue, or NULL if there are no new
+ * entried in the CRQ.
+ */
+static struct ibmvmc_crq_msg *crq_queue_next_crq(struct crq_queue *queue)
+{
+ struct ibmvmc_crq_msg *crq;
+ unsigned long flags;
+
+ spin_lock_irqsave(&queue->lock, flags);
+ crq = &queue->msgs[queue->cur];
+ if (crq->valid & 0x80) {
+ if (++queue->cur == queue->size)
+ queue->cur = 0;
+
+ /* Ensure the read of the valid bit occurs before reading any
+ * other bits of the CRQ entry
+ */
+ dma_rmb();
+ } else {
+ crq = NULL;
+ }
+
+ spin_unlock_irqrestore(&queue->lock, flags);
+
+ return crq;
+}
+
+/**
+ * ibmvmc_send_crq - Send CRQ
+ *
+ * @adapter: crq_server_adapter struct
+ * @word1: Word1 Data field
+ * @word2: Word2 Data field
+ *
+ * Return:
+ * 0 - Success
+ * Non-Zero - Failure
+ */
+static long ibmvmc_send_crq(struct crq_server_adapter *adapter,
+ u64 word1, u64 word2)
+{
+ struct vio_dev *vdev = to_vio_dev(adapter->dev);
+ long rc;
+
+ dev_dbg(adapter->dev, "(0x%x, 0x%016llx, 0x%016llx)\n",
+ vdev->unit_address, word1, word2);
+
+ /*
+ * Ensure the command buffer is flushed to memory before handing it
+ * over to the other side to prevent it from fetching any stale data.
+ */
+ dma_wmb();
+ rc = plpar_hcall_norets(H_SEND_CRQ, vdev->unit_address, word1, word2);
+ dev_dbg(adapter->dev, "rc = 0x%lx\n", rc);
+
+ return rc;
+}
+
+/**
+ * alloc_dma_buffer - Create DMA Buffer
+ *
+ * @vdev: vio_dev struct
+ * @size: Size field
+ * @dma_handle: DMA address field
+ *
+ * Allocates memory for the command queue and maps remote memory into an
+ * ioba.
+ *
+ * Returns a pointer to the buffer
+ */
+static void *alloc_dma_buffer(struct vio_dev *vdev, size_t size,
+ dma_addr_t *dma_handle)
+{
+ /* allocate memory */
+ void *buffer = kzalloc(size, GFP_KERNEL);
+
+ if (!buffer) {
+ *dma_handle = 0;
+ return NULL;
+ }
+
+ /* DMA map */
+ *dma_handle = dma_map_single(&vdev->dev, buffer, size,
+ DMA_BIDIRECTIONAL);
+
+ if (dma_mapping_error(&vdev->dev, *dma_handle)) {
+ *dma_handle = 0;
+ kzfree(buffer);
+ return NULL;
+ }
+
+ return buffer;
+}
+
+/**
+ * free_dma_buffer - Free DMA Buffer
+ *
+ * @vdev: vio_dev struct
+ * @size: Size field
+ * @vaddr: Address field
+ * @dma_handle: DMA address field
+ *
+ * Releases memory for a command queue and unmaps mapped remote memory.
+ */
+static void free_dma_buffer(struct vio_dev *vdev, size_t size, void *vaddr,
+ dma_addr_t dma_handle)
+{
+ /* DMA unmap */
+ dma_unmap_single(&vdev->dev, dma_handle, size, DMA_BIDIRECTIONAL);
+
+ /* deallocate memory */
+ kzfree(vaddr);
+}
+
+/**
+ * ibmvmc_get_valid_hmc_buffer - Retrieve Valid HMC Buffer
+ *
+ * @hmc_index: HMC Index Field
+ *
+ * Return:
+ * Pointer to ibmvmc_buffer
+ */
+static struct ibmvmc_buffer *ibmvmc_get_valid_hmc_buffer(u8 hmc_index)
+{
+ struct ibmvmc_buffer *buffer;
+ struct ibmvmc_buffer *ret_buf = NULL;
+ unsigned long i;
+
+ if (hmc_index > ibmvmc.max_hmc_index)
+ return NULL;
+
+ buffer = hmcs[hmc_index].buffer;
+
+ for (i = 0; i < ibmvmc_max_buf_pool_size; i++) {
+ if (buffer[i].valid && buffer[i].free &&
+ buffer[i].owner == VMC_BUF_OWNER_ALPHA) {
+ buffer[i].free = 0;
+ ret_buf = &buffer[i];
+ break;
+ }
+ }
+
+ return ret_buf;
+}
+
+/**
+ * ibmvmc_get_free_hmc_buffer - Get Free HMC Buffer
+ *
+ * @adapter: crq_server_adapter struct
+ * @hmc_index: Hmc Index field
+ *
+ * Return:
+ * Pointer to ibmvmc_buffer
+ */
+static struct ibmvmc_buffer *ibmvmc_get_free_hmc_buffer(struct crq_server_adapter *adapter,
+ u8 hmc_index)
+{
+ struct ibmvmc_buffer *buffer;
+ struct ibmvmc_buffer *ret_buf = NULL;
+ unsigned long i;
+
+ if (hmc_index > ibmvmc.max_hmc_index) {
+ dev_info(adapter->dev, "get_free_hmc_buffer: invalid hmc_index=0x%x\n",
+ hmc_index);
+ return NULL;
+ }
+
+ buffer = hmcs[hmc_index].buffer;
+
+ for (i = 0; i < ibmvmc_max_buf_pool_size; i++) {
+ if (buffer[i].free &&
+ buffer[i].owner == VMC_BUF_OWNER_ALPHA) {
+ buffer[i].free = 0;
+ ret_buf = &buffer[i];
+ break;
+ }
+ }
+
+ return ret_buf;
+}
+
+/**
+ * ibmvmc_free_hmc_buffer - Free an HMC Buffer
+ *
+ * @hmc: ibmvmc_hmc struct
+ * @buffer: ibmvmc_buffer struct
+ *
+ */
+static void ibmvmc_free_hmc_buffer(struct ibmvmc_hmc *hmc,
+ struct ibmvmc_buffer *buffer)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hmc->lock, flags);
+ buffer->free = 1;
+ spin_unlock_irqrestore(&hmc->lock, flags);
+}
+
+/**
+ * ibmvmc_count_hmc_buffers - Count HMC Buffers
+ *
+ * @hmc_index: HMC Index field
+ * @valid: Valid number of buffers field
+ * @free: Free number of buffers field
+ *
+ */
+static void ibmvmc_count_hmc_buffers(u8 hmc_index, unsigned int *valid,
+ unsigned int *free)
+{
+ struct ibmvmc_buffer *buffer;
+ unsigned long i;
+ unsigned long flags;
+
+ if (hmc_index > ibmvmc.max_hmc_index)
+ return;
+
+ if (!valid || !free)
+ return;
+
+ *valid = 0; *free = 0;
+
+ buffer = hmcs[hmc_index].buffer;
+ spin_lock_irqsave(&hmcs[hmc_index].lock, flags);
+
+ for (i = 0; i < ibmvmc_max_buf_pool_size; i++) {
+ if (buffer[i].valid) {
+ *valid = *valid + 1;
+ if (buffer[i].free)
+ *free = *free + 1;
+ }
+ }
+
+ spin_unlock_irqrestore(&hmcs[hmc_index].lock, flags);
+}
+
+/**
+ * ibmvmc_get_free_hmc - Get Free HMC
+ *
+ * Return:
+ * Pointer to an available HMC Connection
+ * Null otherwise
+ */
+static struct ibmvmc_hmc *ibmvmc_get_free_hmc(void)
+{
+ unsigned long i;
+ unsigned long flags;
+
+ /*
+ * Find an available HMC connection.
+ */
+ for (i = 0; i <= ibmvmc.max_hmc_index; i++) {
+ spin_lock_irqsave(&hmcs[i].lock, flags);
+ if (hmcs[i].state == ibmhmc_state_free) {
+ hmcs[i].index = i;
+ hmcs[i].state = ibmhmc_state_initial;
+ spin_unlock_irqrestore(&hmcs[i].lock, flags);
+ return &hmcs[i];
+ }
+ spin_unlock_irqrestore(&hmcs[i].lock, flags);
+ }
+
+ return NULL;
+}
+
+/**
+ * ibmvmc_return_hmc - Return an HMC Connection
+ *
+ * @hmc: ibmvmc_hmc struct
+ * @release_readers: Number of readers connected to session
+ *
+ * This function releases the HMC connections back into the pool.
+ *
+ * Return:
+ * 0 - Success
+ * Non-zero - Failure
+ */
+static int ibmvmc_return_hmc(struct ibmvmc_hmc *hmc, bool release_readers)
+{
+ struct ibmvmc_buffer *buffer;
+ struct crq_server_adapter *adapter;
+ struct vio_dev *vdev;
+ unsigned long i;
+ unsigned long flags;
+
+ if (!hmc || !hmc->adapter)
+ return -EIO;
+
+ if (release_readers) {
+ if (hmc->file_session) {
+ struct ibmvmc_file_session *session = hmc->file_session;
+
+ session->valid = 0;
+ wake_up_interruptible(&ibmvmc_read_wait);
+ }
+ }
+
+ adapter = hmc->adapter;
+ vdev = to_vio_dev(adapter->dev);
+
+ spin_lock_irqsave(&hmc->lock, flags);
+ hmc->index = 0;
+ hmc->state = ibmhmc_state_free;
+ hmc->queue_head = 0;
+ hmc->queue_tail = 0;
+ buffer = hmc->buffer;
+ for (i = 0; i < ibmvmc_max_buf_pool_size; i++) {
+ if (buffer[i].valid) {
+ free_dma_buffer(vdev,
+ ibmvmc.max_mtu,
+ buffer[i].real_addr_local,
+ buffer[i].dma_addr_local);
+ dev_dbg(adapter->dev, "Forgot buffer id 0x%lx\n", i);
+ }
+ memset(&buffer[i], 0, sizeof(struct ibmvmc_buffer));
+
+ hmc->queue_outbound_msgs[i] = VMC_INVALID_BUFFER_ID;
+ }
+
+ spin_unlock_irqrestore(&hmc->lock, flags);
+
+ return 0;
+}
+
+/**
+ * ibmvmc_send_open - Interface Open
+ * @buffer: Pointer to ibmvmc_buffer struct
+ * @hmc: Pointer to ibmvmc_hmc struct
+ *
+ * This command is sent by the management partition as the result of a
+ * management partition device request. It causes the hypervisor to
+ * prepare a set of data buffers for the Novalink connection indicated HMC idx.
+ * A unique HMC Idx would be used if multiple management applications running
+ * concurrently were desired. Before responding to this command, the
+ * hypervisor must provide the management partition with at least one of these
+ * new buffers via the Add Buffer. This indicates whether the messages are
+ * inbound or outbound from the hypervisor.
+ *
+ * Return:
+ * 0 - Success
+ * Non-zero - Failure
+ */
+static int ibmvmc_send_open(struct ibmvmc_buffer *buffer,
+ struct ibmvmc_hmc *hmc)
+{
+ struct ibmvmc_crq_msg crq_msg;
+ struct crq_server_adapter *adapter;
+ __be64 *crq_as_u64 = (__be64 *)&crq_msg;
+ int rc = 0;
+
+ if (!hmc || !hmc->adapter)
+ return -EIO;
+
+ adapter = hmc->adapter;
+
+ dev_dbg(adapter->dev, "send_open: 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx\n",
+ (unsigned long)buffer->size, (unsigned long)adapter->liobn,
+ (unsigned long)buffer->dma_addr_local,
+ (unsigned long)adapter->riobn,
+ (unsigned long)buffer->dma_addr_remote);
+
+ rc = h_copy_rdma(buffer->size,
+ adapter->liobn,
+ buffer->dma_addr_local,
+ adapter->riobn,
+ buffer->dma_addr_remote);
+ if (rc) {
+ dev_err(adapter->dev, "Error: In send_open, h_copy_rdma rc 0x%x\n",
+ rc);
+ return -EIO;
+ }
+
+ hmc->state = ibmhmc_state_opening;
+
+ crq_msg.valid = 0x80;
+ crq_msg.type = VMC_MSG_OPEN;
+ crq_msg.status = 0;
+ crq_msg.var1.rsvd = 0;
+ crq_msg.hmc_session = hmc->session;
+ crq_msg.hmc_index = hmc->index;
+ crq_msg.var2.buffer_id = cpu_to_be16(buffer->id);
+ crq_msg.rsvd = 0;
+ crq_msg.var3.rsvd = 0;
+
+ ibmvmc_send_crq(adapter, be64_to_cpu(crq_as_u64[0]),
+ be64_to_cpu(crq_as_u64[1]));
+
+ return rc;
+}
+
+/**
+ * ibmvmc_send_close - Interface Close
+ * @hmc: Pointer to ibmvmc_hmc struct
+ *
+ * This command is sent by the management partition to terminate an Novalink to
+ * hypervisor connection. When this command is sent, the management
+ * partition has quiesced all I/O operations to all buffers associated with
+ * this Novalink connection, and has freed any storage for these buffers.
+ *
+ * Return:
+ * 0 - Success
+ * Non-zero - Failure
+ */
+static int ibmvmc_send_close(struct ibmvmc_hmc *hmc)
+{
+ struct ibmvmc_crq_msg crq_msg;
+ struct crq_server_adapter *adapter;
+ __be64 *crq_as_u64 = (__be64 *)&crq_msg;
+ int rc = 0;
+
+ if (!hmc || !hmc->adapter)
+ return -EIO;
+
+ adapter = hmc->adapter;
+
+ dev_info(adapter->dev, "CRQ send: close\n");
+
+ crq_msg.valid = 0x80;
+ crq_msg.type = VMC_MSG_CLOSE;
+ crq_msg.status = 0;
+ crq_msg.var1.rsvd = 0;
+ crq_msg.hmc_session = hmc->session;
+ crq_msg.hmc_index = hmc->index;
+ crq_msg.var2.rsvd = 0;
+ crq_msg.rsvd = 0;
+ crq_msg.var3.rsvd = 0;
+
+ ibmvmc_send_crq(adapter, be64_to_cpu(crq_as_u64[0]),
+ be64_to_cpu(crq_as_u64[1]));
+
+ return rc;
+}
+
+/**
+ * ibmvmc_send_capabilities - Send VMC Capabilities
+ *
+ * @adapter: crq_server_adapter struct
+ *
+ * The capabilities message is an administrative message sent after the CRQ
+ * initialization sequence of messages and is used to exchange VMC capabilities
+ * between the management partition and the hypervisor. The management
+ * partition must send this message and the hypervisor must respond with VMC
+ * capabilities Response message before HMC interface message can begin. Any
+ * HMC interface messages received before the exchange of capabilities has
+ * complete are dropped.
+ *
+ * Return:
+ * 0 - Success
+ */
+static int ibmvmc_send_capabilities(struct crq_server_adapter *adapter)
+{
+ struct ibmvmc_admin_crq_msg crq_msg;
+ __be64 *crq_as_u64 = (__be64 *)&crq_msg;
+
+ dev_dbg(adapter->dev, "ibmvmc: CRQ send: capabilities\n");
+ crq_msg.valid = 0x80;
+ crq_msg.type = VMC_MSG_CAP;
+ crq_msg.status = 0;
+ crq_msg.rsvd[0] = 0;
+ crq_msg.rsvd[1] = 0;
+ crq_msg.max_hmc = ibmvmc_max_hmcs;
+ crq_msg.max_mtu = cpu_to_be32(ibmvmc_max_mtu);
+ crq_msg.pool_size = cpu_to_be16(ibmvmc_max_buf_pool_size);
+ crq_msg.crq_size = cpu_to_be16(adapter->queue.size);
+ crq_msg.version = cpu_to_be16(IBMVMC_PROTOCOL_VERSION);
+
+ ibmvmc_send_crq(adapter, be64_to_cpu(crq_as_u64[0]),
+ be64_to_cpu(crq_as_u64[1]));
+
+ ibmvmc.state = ibmvmc_state_capabilities;
+
+ return 0;
+}
+
+/**
+ * ibmvmc_send_add_buffer_resp - Add Buffer Response
+ *
+ * @adapter: crq_server_adapter struct
+ * @status: Status field
+ * @hmc_session: HMC Session field
+ * @hmc_index: HMC Index field
+ * @buffer_id: Buffer Id field
+ *
+ * This command is sent by the management partition to the hypervisor in
+ * response to the Add Buffer message. The Status field indicates the result of
+ * the command.
+ *
+ * Return:
+ * 0 - Success
+ */
+static int ibmvmc_send_add_buffer_resp(struct crq_server_adapter *adapter,
+ u8 status, u8 hmc_session,
+ u8 hmc_index, u16 buffer_id)
+{
+ struct ibmvmc_crq_msg crq_msg;
+ __be64 *crq_as_u64 = (__be64 *)&crq_msg;
+
+ dev_dbg(adapter->dev, "CRQ send: add_buffer_resp\n");
+ crq_msg.valid = 0x80;
+ crq_msg.type = VMC_MSG_ADD_BUF_RESP;
+ crq_msg.status = status;
+ crq_msg.var1.rsvd = 0;
+ crq_msg.hmc_session = hmc_session;
+ crq_msg.hmc_index = hmc_index;
+ crq_msg.var2.buffer_id = cpu_to_be16(buffer_id);
+ crq_msg.rsvd = 0;
+ crq_msg.var3.rsvd = 0;
+
+ ibmvmc_send_crq(adapter, be64_to_cpu(crq_as_u64[0]),
+ be64_to_cpu(crq_as_u64[1]));
+
+ return 0;
+}
+
+/**
+ * ibmvmc_send_rem_buffer_resp - Remove Buffer Response
+ *
+ * @adapter: crq_server_adapter struct
+ * @status: Status field
+ * @hmc_session: HMC Session field
+ * @hmc_index: HMC Index field
+ * @buffer_id: Buffer Id field
+ *
+ * This command is sent by the management partition to the hypervisor in
+ * response to the Remove Buffer message. The Buffer ID field indicates
+ * which buffer the management partition selected to remove. The Status
+ * field indicates the result of the command.
+ *
+ * Return:
+ * 0 - Success
+ */
+static int ibmvmc_send_rem_buffer_resp(struct crq_server_adapter *adapter,
+ u8 status, u8 hmc_session,
+ u8 hmc_index, u16 buffer_id)
+{
+ struct ibmvmc_crq_msg crq_msg;
+ __be64 *crq_as_u64 = (__be64 *)&crq_msg;
+
+ dev_dbg(adapter->dev, "CRQ send: rem_buffer_resp\n");
+ crq_msg.valid = 0x80;
+ crq_msg.type = VMC_MSG_REM_BUF_RESP;
+ crq_msg.status = status;
+ crq_msg.var1.rsvd = 0;
+ crq_msg.hmc_session = hmc_session;
+ crq_msg.hmc_index = hmc_index;
+ crq_msg.var2.buffer_id = cpu_to_be16(buffer_id);
+ crq_msg.rsvd = 0;
+ crq_msg.var3.rsvd = 0;
+
+ ibmvmc_send_crq(adapter, be64_to_cpu(crq_as_u64[0]),
+ be64_to_cpu(crq_as_u64[1]));
+
+ return 0;
+}
+
+/**
+ * ibmvmc_send_msg - Signal Message
+ *
+ * @adapter: crq_server_adapter struct
+ * @buffer: ibmvmc_buffer struct
+ * @hmc: ibmvmc_hmc struct
+ * @msg_length: message length field
+ *
+ * This command is sent between the management partition and the hypervisor
+ * in order to signal the arrival of an HMC protocol message. The command
+ * can be sent by both the management partition and the hypervisor. It is
+ * used for all traffic between the Novalink application and the hypervisor,
+ * regardless of who initiated the communication.
+ *
+ * There is no response to this message.
+ *
+ * Return:
+ * 0 - Success
+ * Non-zero - Failure
+ */
+static int ibmvmc_send_msg(struct crq_server_adapter *adapter,
+ struct ibmvmc_buffer *buffer,
+ struct ibmvmc_hmc *hmc, int msg_len)
+{
+ struct ibmvmc_crq_msg crq_msg;
+ __be64 *crq_as_u64 = (__be64 *)&crq_msg;
+ int rc = 0;
+
+ dev_dbg(adapter->dev, "CRQ send: rdma to HV\n");
+ rc = h_copy_rdma(msg_len,
+ adapter->liobn,
+ buffer->dma_addr_local,
+ adapter->riobn,
+ buffer->dma_addr_remote);
+ if (rc) {
+ dev_err(adapter->dev, "Error in send_msg, h_copy_rdma rc 0x%x\n",
+ rc);
+ return rc;
+ }
+
+ crq_msg.valid = 0x80;
+ crq_msg.type = VMC_MSG_SIGNAL;
+ crq_msg.status = 0;
+ crq_msg.var1.rsvd = 0;
+ crq_msg.hmc_session = hmc->session;
+ crq_msg.hmc_index = hmc->index;
+ crq_msg.var2.buffer_id = cpu_to_be16(buffer->id);
+ crq_msg.var3.msg_len = cpu_to_be32(msg_len);
+ dev_dbg(adapter->dev, "CRQ send: msg to HV 0x%llx 0x%llx\n",
+ be64_to_cpu(crq_as_u64[0]), be64_to_cpu(crq_as_u64[1]));
+
+ buffer->owner = VMC_BUF_OWNER_HV;
+ ibmvmc_send_crq(adapter, be64_to_cpu(crq_as_u64[0]),
+ be64_to_cpu(crq_as_u64[1]));
+
+ return rc;
+}
+
+/**
+ * ibmvmc_open - Open Session
+ *
+ * @inode: inode struct
+ * @file: file struct
+ *
+ * Return:
+ * 0 - Success
+ */
+static int ibmvmc_open(struct inode *inode, struct file *file)
+{
+ struct ibmvmc_file_session *session;
+ int rc = 0;
+
+ pr_debug("%s: inode = 0x%lx, file = 0x%lx, state = 0x%x\n", __func__,
+ (unsigned long)inode, (unsigned long)file,
+ ibmvmc.state);
+
+ session = kzalloc(sizeof(*session), GFP_KERNEL);
+ session->file = file;
+ file->private_data = session;
+
+ return rc;
+}
+
+/**
+ * ibmvmc_close - Close Session
+ *
+ * @inode: inode struct
+ * @file: file struct
+ *
+ * Return:
+ * 0 - Success
+ * Non-zero - Failure
+ */
+static int ibmvmc_close(struct inode *inode, struct file *file)
+{
+ struct ibmvmc_file_session *session;
+ struct ibmvmc_hmc *hmc;
+ int rc = 0;
+ unsigned long flags;
+
+ pr_debug("%s: file = 0x%lx, state = 0x%x\n", __func__,
+ (unsigned long)file, ibmvmc.state);
+
+ session = file->private_data;
+ if (!session)
+ return -EIO;
+
+ hmc = session->hmc;
+ if (hmc) {
+ if (!hmc->adapter)
+ return -EIO;
+
+ if (ibmvmc.state == ibmvmc_state_failed) {
+ dev_warn(hmc->adapter->dev, "close: state_failed\n");
+ return -EIO;
+ }
+
+ spin_lock_irqsave(&hmc->lock, flags);
+ if (hmc->state >= ibmhmc_state_opening) {
+ rc = ibmvmc_send_close(hmc);
+ if (rc)
+ dev_warn(hmc->adapter->dev, "close: send_close failed.\n");
+ }
+ spin_unlock_irqrestore(&hmc->lock, flags);
+ }
+
+ kzfree(session);
+
+ return rc;
+}
+
+/**
+ * ibmvmc_read - Read
+ *
+ * @file: file struct
+ * @buf: Character buffer
+ * @nbytes: Size in bytes
+ * @ppos: Offset
+ *
+ * Return:
+ * 0 - Success
+ * Non-zero - Failure
+ */
+static ssize_t ibmvmc_read(struct file *file, char *buf, size_t nbytes,
+ loff_t *ppos)
+{
+ struct ibmvmc_file_session *session;
+ struct ibmvmc_hmc *hmc;
+ struct crq_server_adapter *adapter;
+ struct ibmvmc_buffer *buffer;
+ ssize_t n;
+ ssize_t retval = 0;
+ unsigned long flags;
+ DEFINE_WAIT(wait);
+
+ pr_debug("ibmvmc: read: file = 0x%lx, buf = 0x%lx, nbytes = 0x%lx\n",
+ (unsigned long)file, (unsigned long)buf,
+ (unsigned long)nbytes);
+
+ if (nbytes == 0)
+ return 0;
+
+ if (nbytes > ibmvmc.max_mtu) {
+ pr_warn("ibmvmc: read: nbytes invalid 0x%x\n",
+ (unsigned int)nbytes);
+ return -EINVAL;
+ }
+
+ session = file->private_data;
+ if (!session) {
+ pr_warn("ibmvmc: read: no session\n");
+ return -EIO;
+ }
+
+ hmc = session->hmc;
+ if (!hmc) {
+ pr_warn("ibmvmc: read: no hmc\n");
+ return -EIO;
+ }
+
+ adapter = hmc->adapter;
+ if (!adapter) {
+ pr_warn("ibmvmc: read: no adapter\n");
+ return -EIO;
+ }
+
+ do {
+ prepare_to_wait(&ibmvmc_read_wait, &wait, TASK_INTERRUPTIBLE);
+
+ spin_lock_irqsave(&hmc->lock, flags);
+ if (hmc->queue_tail != hmc->queue_head)
+ /* Data is available */
+ break;
+
+ spin_unlock_irqrestore(&hmc->lock, flags);
+
+ if (!session->valid) {
+ retval = -EBADFD;
+ goto out;
+ }
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ goto out;
+ }
+
+ schedule();
+
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ goto out;
+ }
+ } while (1);
+
+ buffer = &(hmc->buffer[hmc->queue_outbound_msgs[hmc->queue_tail]]);
+ hmc->queue_tail++;
+ if (hmc->queue_tail == ibmvmc_max_buf_pool_size)
+ hmc->queue_tail = 0;
+ spin_unlock_irqrestore(&hmc->lock, flags);
+
+ nbytes = min_t(size_t, nbytes, buffer->msg_len);
+ n = copy_to_user((void *)buf, buffer->real_addr_local, nbytes);
+ dev_dbg(adapter->dev, "read: copy to user nbytes = 0x%lx.\n", nbytes);
+ ibmvmc_free_hmc_buffer(hmc, buffer);
+ retval = nbytes;
+
+ if (n) {
+ dev_warn(adapter->dev, "read: copy to user failed.\n");
+ retval = -EFAULT;
+ }
+
+ out:
+ finish_wait(&ibmvmc_read_wait, &wait);
+ dev_dbg(adapter->dev, "read: out %ld\n", retval);
+ return retval;
+}
+
+/**
+ * ibmvmc_poll - Poll
+ *
+ * @file: file struct
+ * @wait: Poll Table
+ *
+ * Return:
+ * poll.h return values
+ */
+static unsigned int ibmvmc_poll(struct file *file, poll_table *wait)
+{
+ struct ibmvmc_file_session *session;
+ struct ibmvmc_hmc *hmc;
+ unsigned int mask = 0;
+
+ session = file->private_data;
+ if (!session)
+ return 0;
+
+ hmc = session->hmc;
+ if (!hmc)
+ return 0;
+
+ poll_wait(file, &ibmvmc_read_wait, wait);
+
+ if (hmc->queue_head != hmc->queue_tail)
+ mask |= POLLIN | POLLRDNORM;
+
+ return mask;
+}
+
+/**
+ * ibmvmc_write - Write
+ *
+ * @file: file struct
+ * @buf: Character buffer
+ * @count: Count field
+ * @ppos: Offset
+ *
+ * Return:
+ * 0 - Success
+ * Non-zero - Failure
+ */
+static ssize_t ibmvmc_write(struct file *file, const char *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct ibmvmc_buffer *vmc_buffer;
+ struct ibmvmc_file_session *session;
+ struct crq_server_adapter *adapter;
+ struct ibmvmc_hmc *hmc;
+ unsigned char *buf;
+ unsigned long flags;
+ size_t bytes;
+ const char *p = buffer;
+ size_t c = count;
+ int ret = 0;
+
+ session = file->private_data;
+ if (!session)
+ return -EIO;
+
+ hmc = session->hmc;
+ if (!hmc)
+ return -EIO;
+
+ spin_lock_irqsave(&hmc->lock, flags);
+ if (hmc->state == ibmhmc_state_free) {
+ /* HMC connection is not valid (possibly was reset under us). */
+ ret = -EIO;
+ goto out;
+ }
+
+ adapter = hmc->adapter;
+ if (!adapter) {
+ ret = -EIO;
+ goto out;
+ }
+
+ if (count > ibmvmc.max_mtu) {
+ dev_warn(adapter->dev, "invalid buffer size 0x%lx\n",
+ (unsigned long)count);
+ ret = -EIO;
+ goto out;
+ }
+
+ /* Waiting for the open resp message to the ioctl(1) - retry */
+ if (hmc->state == ibmhmc_state_opening) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* Make sure the ioctl() was called & the open msg sent, and that
+ * the HMC connection has not failed.
+ */
+ if (hmc->state != ibmhmc_state_ready) {
+ ret = -EIO;
+ goto out;
+ }
+
+ vmc_buffer = ibmvmc_get_valid_hmc_buffer(hmc->index);
+ if (!vmc_buffer) {
+ /* No buffer available for the msg send, or we have not yet
+ * completed the open/open_resp sequence. Retry until this is
+ * complete.
+ */
+ ret = -EBUSY;
+ goto out;
+ }
+ if (!vmc_buffer->real_addr_local) {
+ dev_err(adapter->dev, "no buffer storage assigned\n");
+ ret = -EIO;
+ goto out;
+ }
+ buf = vmc_buffer->real_addr_local;
+
+ while (c > 0) {
+ bytes = min_t(size_t, c, vmc_buffer->size);
+
+ bytes -= copy_from_user(buf, p, bytes);
+ if (!bytes) {
+ ret = -EFAULT;
+ goto out;
+ }
+ c -= bytes;
+ p += bytes;
+ }
+ if (p == buffer)
+ goto out;
+
+ file->f_path.dentry->d_inode->i_mtime = current_time(file_inode(file));
+ mark_inode_dirty(file->f_path.dentry->d_inode);
+
+ dev_dbg(adapter->dev, "write: file = 0x%lx, count = 0x%lx\n",
+ (unsigned long)file, (unsigned long)count);
+
+ ibmvmc_send_msg(adapter, vmc_buffer, hmc, count);
+ ret = p - buffer;
+ out:
+ spin_unlock_irqrestore(&hmc->lock, flags);
+ return (ssize_t)(ret);
+}
+
+/**
+ * ibmvmc_setup_hmc - Setup the HMC
+ *
+ * @session: ibmvmc_file_session struct
+ *
+ * Return:
+ * 0 - Success
+ * Non-zero - Failure
+ */
+static long ibmvmc_setup_hmc(struct ibmvmc_file_session *session)
+{
+ struct ibmvmc_hmc *hmc;
+ unsigned int valid, free, index;
+
+ if (ibmvmc.state == ibmvmc_state_failed) {
+ pr_warn("ibmvmc: Reserve HMC: state_failed\n");
+ return -EIO;
+ }
+
+ if (ibmvmc.state < ibmvmc_state_ready) {
+ pr_warn("ibmvmc: Reserve HMC: not state_ready\n");
+ return -EAGAIN;
+ }
+
+ /* Device is busy until capabilities have been exchanged and we
+ * have a generic buffer for each possible HMC connection.
+ */
+ for (index = 0; index <= ibmvmc.max_hmc_index; index++) {
+ valid = 0;
+ ibmvmc_count_hmc_buffers(index, &valid, &free);
+ if (valid == 0) {
+ pr_warn("ibmvmc: buffers not ready for index %d\n",
+ index);
+ return -ENOBUFS;
+ }
+ }
+
+ /* Get an hmc object, and transition to ibmhmc_state_initial */
+ hmc = ibmvmc_get_free_hmc();
+ if (!hmc) {
+ pr_warn("%s: free hmc not found\n", __func__);
+ return -EBUSY;
+ }
+
+ hmc->session = hmc->session + 1;
+ if (hmc->session == 0xff)
+ hmc->session = 1;
+
+ session->hmc = hmc;
+ hmc->adapter = &ibmvmc_adapter;
+ hmc->file_session = session;
+ session->valid = 1;
+
+ return 0;
+}
+
+/**
+ * ibmvmc_ioctl_sethmcid - IOCTL Set HMC ID
+ *
+ * @session: ibmvmc_file_session struct
+ * @new_hmc_id: HMC id field
+ *
+ * IOCTL command to setup the hmc id
+ *
+ * Return:
+ * 0 - Success
+ * Non-zero - Failure
+ */
+static long ibmvmc_ioctl_sethmcid(struct ibmvmc_file_session *session,
+ unsigned char __user *new_hmc_id)
+{
+ struct ibmvmc_hmc *hmc;
+ struct ibmvmc_buffer *buffer;
+ size_t bytes;
+ char print_buffer[HMC_ID_LEN + 1];
+ unsigned long flags;
+ long rc = 0;
+
+ /* Reserve HMC session */
+ hmc = session->hmc;
+ if (!hmc) {
+ rc = ibmvmc_setup_hmc(session);
+ if (rc)
+ return rc;
+
+ hmc = session->hmc;
+ if (!hmc) {
+ pr_err("ibmvmc: setup_hmc success but no hmc\n");
+ return -EIO;
+ }
+ }
+
+ if (hmc->state != ibmhmc_state_initial) {
+ pr_warn("ibmvmc: sethmcid: invalid state to send open 0x%x\n",
+ hmc->state);
+ return -EIO;
+ }
+
+ bytes = copy_from_user(hmc->hmc_id, new_hmc_id, HMC_ID_LEN);
+ if (bytes)
+ return -EFAULT;
+
+ /* Send Open Session command */
+ spin_lock_irqsave(&hmc->lock, flags);
+ buffer = ibmvmc_get_valid_hmc_buffer(hmc->index);
+ spin_unlock_irqrestore(&hmc->lock, flags);
+
+ if (!buffer || !buffer->real_addr_local) {
+ pr_warn("ibmvmc: sethmcid: no buffer available\n");
+ return -EIO;
+ }
+
+ /* Make sure buffer is NULL terminated before trying to print it */
+ memset(print_buffer, 0, HMC_ID_LEN + 1);
+ strncpy(print_buffer, hmc->hmc_id, HMC_ID_LEN);
+ pr_info("ibmvmc: sethmcid: Set HMC ID: \"%s\"\n", print_buffer);
+
+ memcpy(buffer->real_addr_local, hmc->hmc_id, HMC_ID_LEN);
+ /* RDMA over ID, send open msg, change state to ibmhmc_state_opening */
+ rc = ibmvmc_send_open(buffer, hmc);
+
+ return rc;
+}
+
+/**
+ * ibmvmc_ioctl_query - IOCTL Query
+ *
+ * @session: ibmvmc_file_session struct
+ * @ret_struct: ibmvmc_query_struct
+ *
+ * Return:
+ * 0 - Success
+ * Non-zero - Failure
+ */
+static long ibmvmc_ioctl_query(struct ibmvmc_file_session *session,
+ struct ibmvmc_query_struct __user *ret_struct)
+{
+ struct ibmvmc_query_struct query_struct;
+ size_t bytes;
+
+ memset(&query_struct, 0, sizeof(query_struct));
+ query_struct.have_vmc = (ibmvmc.state > ibmvmc_state_initial);
+ query_struct.state = ibmvmc.state;
+ query_struct.vmc_drc_index = ibmvmc.vmc_drc_index;
+
+ bytes = copy_to_user(ret_struct, &query_struct,
+ sizeof(query_struct));
+ if (bytes)
+ return -EFAULT;
+
+ return 0;
+}
+
+/**
+ * ibmvmc_ioctl_requestvmc - IOCTL Request VMC
+ *
+ * @session: ibmvmc_file_session struct
+ * @ret_vmc_index: VMC Index
+ *
+ * Return:
+ * 0 - Success
+ * Non-zero - Failure
+ */
+static long ibmvmc_ioctl_requestvmc(struct ibmvmc_file_session *session,
+ u32 __user *ret_vmc_index)
+{
+ /* TODO: (adreznec) Add locking to control multiple process access */
+ size_t bytes;
+ long rc;
+ u32 vmc_drc_index;
+
+ /* Call to request the VMC device from phyp*/
+ rc = h_request_vmc(&vmc_drc_index);
+ pr_debug("ibmvmc: requestvmc: H_REQUEST_VMC rc = 0x%lx\n", rc);
+
+ if (rc == H_SUCCESS) {
+ rc = 0;
+ } else if (rc == H_FUNCTION) {
+ pr_err("ibmvmc: requestvmc: h_request_vmc not supported\n");
+ return -EPERM;
+ } else if (rc == H_AUTHORITY) {
+ pr_err("ibmvmc: requestvmc: hypervisor denied vmc request\n");
+ return -EPERM;
+ } else if (rc == H_HARDWARE) {
+ pr_err("ibmvmc: requestvmc: hypervisor hardware fault\n");
+ return -EIO;
+ } else if (rc == H_RESOURCE) {
+ pr_err("ibmvmc: requestvmc: vmc resource unavailable\n");
+ return -ENODEV;
+ } else if (rc == H_NOT_AVAILABLE) {
+ pr_err("ibmvmc: requestvmc: system cannot be vmc managed\n");
+ return -EPERM;
+ } else if (rc == H_PARAMETER) {
+ pr_err("ibmvmc: requestvmc: invalid parameter\n");
+ return -EINVAL;
+ }
+
+ /* Success, set the vmc index in global struct */
+ ibmvmc.vmc_drc_index = vmc_drc_index;
+
+ bytes = copy_to_user(ret_vmc_index, &vmc_drc_index,
+ sizeof(*ret_vmc_index));
+ if (bytes) {
+ pr_warn("ibmvmc: requestvmc: copy to user failed.\n");
+ return -EFAULT;
+ }
+ return rc;
+}
+
+/**
+ * ibmvmc_ioctl - IOCTL
+ *
+ * @session: ibmvmc_file_session struct
+ * @cmd: cmd field
+ * @arg: Argument field
+ *
+ * Return:
+ * 0 - Success
+ * Non-zero - Failure
+ */
+static long ibmvmc_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct ibmvmc_file_session *session = file->private_data;
+
+ pr_debug("ibmvmc: ioctl file=0x%lx, cmd=0x%x, arg=0x%lx, ses=0x%lx\n",
+ (unsigned long)file, cmd, arg,
+ (unsigned long)session);
+
+ if (!session) {
+ pr_warn("ibmvmc: ioctl: no session\n");
+ return -EIO;
+ }
+
+ switch (cmd) {
+ case VMC_IOCTL_SETHMCID:
+ return ibmvmc_ioctl_sethmcid(session,
+ (unsigned char __user *)arg);
+ case VMC_IOCTL_QUERY:
+ return ibmvmc_ioctl_query(session,
+ (struct ibmvmc_query_struct __user *)arg);
+ case VMC_IOCTL_REQUESTVMC:
+ return ibmvmc_ioctl_requestvmc(session,
+ (unsigned int __user *)arg);
+ default:
+ pr_warn("ibmvmc: unknown ioctl 0x%x\n", cmd);
+ return -EINVAL;
+ }
+}
+
+static const struct file_operations ibmvmc_fops = {
+ .owner = THIS_MODULE,
+ .read = ibmvmc_read,
+ .write = ibmvmc_write,
+ .poll = ibmvmc_poll,
+ .unlocked_ioctl = ibmvmc_ioctl,
+ .open = ibmvmc_open,
+ .release = ibmvmc_close,
+};
+
+/**
+ * ibmvmc_add_buffer - Add Buffer
+ *
+ * @adapter: crq_server_adapter struct
+ * @crq: ibmvmc_crq_msg struct
+ *
+ * This message transfers a buffer from hypervisor ownership to management
+ * partition ownership. The LIOBA is obtained from the virtual TCE table
+ * associated with the hypervisor side of the VMC device, and points to a
+ * buffer of size MTU (as established in the capabilities exchange).
+ *
+ * Typical flow for ading buffers:
+ * 1. A new Novalink connection is opened by the management partition.
+ * 2. The hypervisor assigns new buffers for the traffic associated with
+ * that connection.
+ * 3. The hypervisor sends VMC Add Buffer messages to the management
+ * partition, informing it of the new buffers.
+ * 4. The hypervisor sends an HMC protocol message (to the Novalink application)
+ * notifying it of the new buffers. This informs the application that
+ * it has buffers available for sending HMC commands.
+ *
+ * Return:
+ * 0 - Success
+ * Non-zero - Failure
+ */
+static int ibmvmc_add_buffer(struct crq_server_adapter *adapter,
+ struct ibmvmc_crq_msg *crq)
+{
+ struct ibmvmc_buffer *buffer;
+ u8 hmc_index;
+ u8 hmc_session;
+ u16 buffer_id;
+ unsigned long flags;
+ int rc = 0;
+
+ if (!crq)
+ return -1;
+
+ hmc_session = crq->hmc_session;
+ hmc_index = crq->hmc_index;
+ buffer_id = be16_to_cpu(crq->var2.buffer_id);
+
+ if (hmc_index > ibmvmc.max_hmc_index) {
+ dev_err(adapter->dev, "add_buffer: invalid hmc_index = 0x%x\n",
+ hmc_index);
+ ibmvmc_send_add_buffer_resp(adapter, VMC_MSG_INVALID_HMC_INDEX,
+ hmc_session, hmc_index, buffer_id);
+ return -1;
+ }
+
+ if (buffer_id >= ibmvmc.max_buffer_pool_size) {
+ dev_err(adapter->dev, "add_buffer: invalid buffer_id = 0x%x\n",
+ buffer_id);
+ ibmvmc_send_add_buffer_resp(adapter, VMC_MSG_INVALID_BUFFER_ID,
+ hmc_session, hmc_index, buffer_id);
+ return -1;
+ }
+
+ spin_lock_irqsave(&hmcs[hmc_index].lock, flags);
+ buffer = &hmcs[hmc_index].buffer[buffer_id];
+
+ if (buffer->real_addr_local || buffer->dma_addr_local) {
+ dev_warn(adapter->dev, "add_buffer: already allocated id = 0x%lx\n",
+ (unsigned long)buffer_id);
+ spin_unlock_irqrestore(&hmcs[hmc_index].lock, flags);
+ ibmvmc_send_add_buffer_resp(adapter, VMC_MSG_INVALID_BUFFER_ID,
+ hmc_session, hmc_index, buffer_id);
+ return -1;
+ }
+
+ buffer->real_addr_local = alloc_dma_buffer(to_vio_dev(adapter->dev),
+ ibmvmc.max_mtu,
+ &buffer->dma_addr_local);
+
+ if (!buffer->real_addr_local) {
+ dev_err(adapter->dev, "add_buffer: alloc_dma_buffer failed.\n");
+ spin_unlock_irqrestore(&hmcs[hmc_index].lock, flags);
+ ibmvmc_send_add_buffer_resp(adapter, VMC_MSG_INTERFACE_FAILURE,
+ hmc_session, hmc_index, buffer_id);
+ return -1;
+ }
+
+ buffer->dma_addr_remote = be32_to_cpu(crq->var3.lioba);
+ buffer->size = ibmvmc.max_mtu;
+ buffer->owner = crq->var1.owner;
+ buffer->free = 1;
+ /* Must ensure valid==1 is observable only after all other fields are */
+ dma_wmb();
+ buffer->valid = 1;
+ buffer->id = buffer_id;
+
+ dev_dbg(adapter->dev, "add_buffer: successfully added a buffer:\n");
+ dev_dbg(adapter->dev, " index: %d, session: %d, buffer: 0x%x, owner: %d\n",
+ hmc_index, hmc_session, buffer_id, buffer->owner);
+ dev_dbg(adapter->dev, " local: 0x%x, remote: 0x%x\n",
+ (u32)buffer->dma_addr_local,
+ (u32)buffer->dma_addr_remote);
+ spin_unlock_irqrestore(&hmcs[hmc_index].lock, flags);
+
+ ibmvmc_send_add_buffer_resp(adapter, VMC_MSG_SUCCESS, hmc_session,
+ hmc_index, buffer_id);
+
+ return rc;
+}
+
+/**
+ * ibmvmc_rem_buffer - Remove Buffer
+ *
+ * @adapter: crq_server_adapter struct
+ * @crq: ibmvmc_crq_msg struct
+ *
+ * This message requests an HMC buffer to be transferred from management
+ * partition ownership to hypervisor ownership. The management partition may
+ * not be able to satisfy the request at a particular point in time if all its
+ * buffers are in use. The management partition requires a depth of at least
+ * one inbound buffer to allow Novalink commands to flow to the hypervisor. It
+ * is, therefore, an interface error for the hypervisor to attempt to remove the
+ * management partition's last buffer.
+ *
+ * The hypervisor is expected to manage buffer usage with the Novalink
+ * application directly and inform the management partition when buffers may be
+ * removed. The typical flow for removing buffers:
+ *
+ * 1. The Novalink application no longer needs a communication path to a
+ * particular hypervisor function. That function is closed.
+ * 2. The hypervisor and the Novalink application quiesce all traffic to that
+ * function. The hypervisor requests a reduction in buffer pool size.
+ * 3. The Novalink application acknowledges the reduction in buffer pool size.
+ * 4. The hypervisor sends a Remove Buffer message to the management partition,
+ * informing it of the reduction in buffers.
+ * 5. The management partition verifies it can remove the buffer. This is
+ * possible if buffers have been quiesced.
+ *
+ * Return:
+ * 0 - Success
+ * Non-zero - Failure
+ */
+/*
+ * The hypervisor requested that we pick an unused buffer, and return it.
+ * Before sending the buffer back, we free any storage associated with the
+ * buffer.
+ */
+static int ibmvmc_rem_buffer(struct crq_server_adapter *adapter,
+ struct ibmvmc_crq_msg *crq)
+{
+ struct ibmvmc_buffer *buffer;
+ u8 hmc_index;
+ u8 hmc_session;
+ u16 buffer_id = 0;
+ unsigned long flags;
+ int rc = 0;
+
+ if (!crq)
+ return -1;
+
+ hmc_session = crq->hmc_session;
+ hmc_index = crq->hmc_index;
+
+ if (hmc_index > ibmvmc.max_hmc_index) {
+ dev_warn(adapter->dev, "rem_buffer: invalid hmc_index = 0x%x\n",
+ hmc_index);
+ ibmvmc_send_rem_buffer_resp(adapter, VMC_MSG_INVALID_HMC_INDEX,
+ hmc_session, hmc_index, buffer_id);
+ return -1;
+ }
+
+ spin_lock_irqsave(&hmcs[hmc_index].lock, flags);
+ buffer = ibmvmc_get_free_hmc_buffer(adapter, hmc_index);
+ if (!buffer) {
+ dev_info(adapter->dev, "rem_buffer: no buffer to remove\n");
+ spin_unlock_irqrestore(&hmcs[hmc_index].lock, flags);
+ ibmvmc_send_rem_buffer_resp(adapter, VMC_MSG_NO_BUFFER,
+ hmc_session, hmc_index,
+ VMC_INVALID_BUFFER_ID);
+ return -1;
+ }
+
+ buffer_id = buffer->id;
+
+ if (buffer->valid)
+ free_dma_buffer(to_vio_dev(adapter->dev),
+ ibmvmc.max_mtu,
+ buffer->real_addr_local,
+ buffer->dma_addr_local);
+
+ memset(buffer, 0, sizeof(struct ibmvmc_buffer));
+ spin_unlock_irqrestore(&hmcs[hmc_index].lock, flags);
+
+ dev_dbg(adapter->dev, "rem_buffer: removed buffer 0x%x.\n", buffer_id);
+ ibmvmc_send_rem_buffer_resp(adapter, VMC_MSG_SUCCESS, hmc_session,
+ hmc_index, buffer_id);
+
+ return rc;
+}
+
+static int ibmvmc_recv_msg(struct crq_server_adapter *adapter,
+ struct ibmvmc_crq_msg *crq)
+{
+ struct ibmvmc_buffer *buffer;
+ struct ibmvmc_hmc *hmc;
+ unsigned long msg_len;
+ u8 hmc_index;
+ u8 hmc_session;
+ u16 buffer_id;
+ unsigned long flags;
+ int rc = 0;
+
+ if (!crq)
+ return -1;
+
+ /* Hypervisor writes CRQs directly into our memory in big endian */
+ dev_dbg(adapter->dev, "Recv_msg: msg from HV 0x%016llx 0x%016llx\n",
+ be64_to_cpu(*((unsigned long *)crq)),
+ be64_to_cpu(*(((unsigned long *)crq) + 1)));
+
+ hmc_session = crq->hmc_session;
+ hmc_index = crq->hmc_index;
+ buffer_id = be16_to_cpu(crq->var2.buffer_id);
+ msg_len = be32_to_cpu(crq->var3.msg_len);
+
+ if (hmc_index > ibmvmc.max_hmc_index) {
+ dev_err(adapter->dev, "Recv_msg: invalid hmc_index = 0x%x\n",
+ hmc_index);
+ ibmvmc_send_add_buffer_resp(adapter, VMC_MSG_INVALID_HMC_INDEX,
+ hmc_session, hmc_index, buffer_id);
+ return -1;
+ }
+
+ if (buffer_id >= ibmvmc.max_buffer_pool_size) {
+ dev_err(adapter->dev, "Recv_msg: invalid buffer_id = 0x%x\n",
+ buffer_id);
+ ibmvmc_send_add_buffer_resp(adapter, VMC_MSG_INVALID_BUFFER_ID,
+ hmc_session, hmc_index, buffer_id);
+ return -1;
+ }
+
+ hmc = &hmcs[hmc_index];
+ spin_lock_irqsave(&hmc->lock, flags);
+
+ if (hmc->state == ibmhmc_state_free) {
+ dev_err(adapter->dev, "Recv_msg: invalid hmc state = 0x%x\n",
+ hmc->state);
+ /* HMC connection is not valid (possibly was reset under us). */
+ spin_unlock_irqrestore(&hmc->lock, flags);
+ return -1;
+ }
+
+ buffer = &hmc->buffer[buffer_id];
+
+ if (buffer->valid == 0 || buffer->owner == VMC_BUF_OWNER_ALPHA) {
+ dev_err(adapter->dev, "Recv_msg: not valid, or not HV. 0x%x 0x%x\n",
+ buffer->valid, buffer->owner);
+ spin_unlock_irqrestore(&hmc->lock, flags);
+ return -1;
+ }
+
+ /* RDMA the data into the partition. */
+ rc = h_copy_rdma(msg_len,
+ adapter->riobn,
+ buffer->dma_addr_remote,
+ adapter->liobn,
+ buffer->dma_addr_local);
+
+ dev_dbg(adapter->dev, "Recv_msg: msg_len = 0x%x, buffer_id = 0x%x, queue_head = 0x%x, hmc_idx = 0x%x\n",
+ (unsigned int)msg_len, (unsigned int)buffer_id,
+ (unsigned int)hmc->queue_head, (unsigned int)hmc_index);
+ buffer->msg_len = msg_len;
+ buffer->free = 0;
+ buffer->owner = VMC_BUF_OWNER_ALPHA;
+
+ if (rc) {
+ dev_err(adapter->dev, "Failure in recv_msg: h_copy_rdma = 0x%x\n",
+ rc);
+ spin_unlock_irqrestore(&hmc->lock, flags);
+ return -1;
+ }
+
+ /* Must be locked because read operates on the same data */
+ hmc->queue_outbound_msgs[hmc->queue_head] = buffer_id;
+ hmc->queue_head++;
+ if (hmc->queue_head == ibmvmc_max_buf_pool_size)
+ hmc->queue_head = 0;
+
+ if (hmc->queue_head == hmc->queue_tail)
+ dev_err(adapter->dev, "outbound buffer queue wrapped.\n");
+
+ spin_unlock_irqrestore(&hmc->lock, flags);
+
+ wake_up_interruptible(&ibmvmc_read_wait);
+
+ return 0;
+}
+
+/**
+ * ibmvmc_process_capabilities - Process Capabilities
+ *
+ * @adapter: crq_server_adapter struct
+ * @crqp: ibmvmc_crq_msg struct
+ *
+ */
+static void ibmvmc_process_capabilities(struct crq_server_adapter *adapter,
+ struct ibmvmc_crq_msg *crqp)
+{
+ struct ibmvmc_admin_crq_msg *crq = (struct ibmvmc_admin_crq_msg *)crqp;
+
+ if ((be16_to_cpu(crq->version) >> 8) !=
+ (IBMVMC_PROTOCOL_VERSION >> 8)) {
+ dev_err(adapter->dev, "init failed, incompatible versions 0x%x 0x%x\n",
+ be16_to_cpu(crq->version),
+ IBMVMC_PROTOCOL_VERSION);
+ ibmvmc.state = ibmvmc_state_failed;
+ return;
+ }
+
+ ibmvmc.max_mtu = min_t(u32, ibmvmc_max_mtu, be32_to_cpu(crq->max_mtu));
+ ibmvmc.max_buffer_pool_size = min_t(u16, ibmvmc_max_buf_pool_size,
+ be16_to_cpu(crq->pool_size));
+ ibmvmc.max_hmc_index = min_t(u8, ibmvmc_max_hmcs, crq->max_hmc) - 1;
+ ibmvmc.state = ibmvmc_state_ready;
+
+ dev_info(adapter->dev, "Capabilities: mtu=0x%x, pool_size=0x%x, max_hmc=0x%x\n",
+ ibmvmc.max_mtu, ibmvmc.max_buffer_pool_size,
+ ibmvmc.max_hmc_index);
+}
+
+/**
+ * ibmvmc_validate_hmc_session - Validate HMC Session
+ *
+ * @adapter: crq_server_adapter struct
+ * @crq: ibmvmc_crq_msg struct
+ *
+ * Return:
+ * 0 - Success
+ * Non-zero - Failure
+ */
+static int ibmvmc_validate_hmc_session(struct crq_server_adapter *adapter,
+ struct ibmvmc_crq_msg *crq)
+{
+ unsigned char hmc_index;
+
+ hmc_index = crq->hmc_index;
+
+ if (crq->hmc_session == 0)
+ return 0;
+
+ if (hmc_index > ibmvmc.max_hmc_index)
+ return -1;
+
+ if (hmcs[hmc_index].session != crq->hmc_session) {
+ dev_warn(adapter->dev, "Drop, bad session: expected 0x%x, recv 0x%x\n",
+ hmcs[hmc_index].session, crq->hmc_session);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * ibmvmc_reset - Reset
+ *
+ * @adapter: crq_server_adapter struct
+ * @xport_event: export_event field
+ *
+ * Closes all HMC sessions and conditionally schedules a CRQ reset.
+ * @xport_event: If true, the partner closed their CRQ; we don't need to reset.
+ * If false, we need to schedule a CRQ reset.
+ */
+static void ibmvmc_reset(struct crq_server_adapter *adapter, bool xport_event)
+{
+ int i;
+
+ if (ibmvmc.state != ibmvmc_state_sched_reset) {
+ dev_info(adapter->dev, "*** Reset to initial state.\n");
+ for (i = 0; i < ibmvmc_max_hmcs; i++)
+ ibmvmc_return_hmc(&hmcs[i], xport_event);
+
+ if (xport_event) {
+ /* CRQ was closed by the partner. We don't need to do
+ * anything except set ourself to the correct state to
+ * handle init msgs.
+ */
+ ibmvmc.state = ibmvmc_state_crqinit;
+ } else {
+ /* The partner did not close their CRQ - instead, we're
+ * closing the CRQ on our end. Need to schedule this
+ * for process context, because CRQ reset may require a
+ * sleep.
+ *
+ * Setting ibmvmc.state here immediately prevents
+ * ibmvmc_open from completing until the reset
+ * completes in process context.
+ */
+ ibmvmc.state = ibmvmc_state_sched_reset;
+ dev_dbg(adapter->dev, "Device reset scheduled");
+ wake_up_interruptible(&adapter->reset_wait_queue);
+ }
+ }
+}
+
+/**
+ * ibmvmc_reset_task - Reset Task
+ *
+ * @data: Data field
+ *
+ * Performs a CRQ reset of the VMC device in process context.
+ * NOTE: This function should not be called directly, use ibmvmc_reset.
+ */
+static int ibmvmc_reset_task(void *data)
+{
+ struct crq_server_adapter *adapter = data;
+ int rc;
+
+ set_user_nice(current, -20);
+
+ while (!kthread_should_stop()) {
+ wait_event_interruptible(adapter->reset_wait_queue,
+ (ibmvmc.state == ibmvmc_state_sched_reset) ||
+ kthread_should_stop());
+
+ if (kthread_should_stop())
+ break;
+
+ dev_dbg(adapter->dev, "CRQ resetting in process context");
+ tasklet_disable(&adapter->work_task);
+
+ rc = ibmvmc_reset_crq_queue(adapter);
+
+ if (rc != H_SUCCESS && rc != H_RESOURCE) {
+ dev_err(adapter->dev, "Error initializing CRQ. rc = 0x%x\n",
+ rc);
+ ibmvmc.state = ibmvmc_state_failed;
+ } else {
+ ibmvmc.state = ibmvmc_state_crqinit;
+
+ if (ibmvmc_send_crq(adapter, 0xC001000000000000LL, 0)
+ != 0 && rc != H_RESOURCE)
+ dev_warn(adapter->dev, "Failed to send initialize CRQ message\n");
+ }
+
+ vio_enable_interrupts(to_vio_dev(adapter->dev));
+ tasklet_enable(&adapter->work_task);
+ }
+
+ return 0;
+}
+
+/**
+ * ibmvmc_process_open_resp - Process Open Response
+ *
+ * @crq: ibmvmc_crq_msg struct
+ * @adapter: crq_server_adapter struct
+ *
+ * This command is sent by the hypervisor in response to the Interface
+ * Open message. When this message is received, the indicated buffer is
+ * again available for management partition use.
+ */
+static void ibmvmc_process_open_resp(struct ibmvmc_crq_msg *crq,
+ struct crq_server_adapter *adapter)
+{
+ unsigned char hmc_index;
+ unsigned short buffer_id;
+
+ hmc_index = crq->hmc_index;
+ if (hmc_index > ibmvmc.max_hmc_index) {
+ /* Why would PHYP give an index > max negotiated? */
+ ibmvmc_reset(adapter, false);
+ return;
+ }
+
+ if (crq->status) {
+ dev_warn(adapter->dev, "open_resp: failed - status 0x%x\n",
+ crq->status);
+ ibmvmc_return_hmc(&hmcs[hmc_index], false);
+ return;
+ }
+
+ if (hmcs[hmc_index].state == ibmhmc_state_opening) {
+ buffer_id = be16_to_cpu(crq->var2.buffer_id);
+ if (buffer_id >= ibmvmc.max_buffer_pool_size) {
+ dev_err(adapter->dev, "open_resp: invalid buffer_id = 0x%x\n",
+ buffer_id);
+ hmcs[hmc_index].state = ibmhmc_state_failed;
+ } else {
+ ibmvmc_free_hmc_buffer(&hmcs[hmc_index],
+ &hmcs[hmc_index].buffer[buffer_id]);
+ hmcs[hmc_index].state = ibmhmc_state_ready;
+ dev_dbg(adapter->dev, "open_resp: set hmc state = ready\n");
+ }
+ } else {
+ dev_warn(adapter->dev, "open_resp: invalid hmc state (0x%x)\n",
+ hmcs[hmc_index].state);
+ }
+}
+
+/**
+ * ibmvmc_process_close_resp - Process Close Response
+ *
+ * @crq: ibmvmc_crq_msg struct
+ * @adapter: crq_server_adapter struct
+ *
+ * This command is sent by the hypervisor in response to the Novalink Interface
+ * Close message.
+ *
+ * If the close fails, simply reset the entire driver as the state of the VMC
+ * must be in tough shape.
+ */
+static void ibmvmc_process_close_resp(struct ibmvmc_crq_msg *crq,
+ struct crq_server_adapter *adapter)
+{
+ unsigned char hmc_index;
+
+ hmc_index = crq->hmc_index;
+ if (hmc_index > ibmvmc.max_hmc_index) {
+ ibmvmc_reset(adapter, false);
+ return;
+ }
+
+ if (crq->status) {
+ dev_warn(adapter->dev, "close_resp: failed - status 0x%x\n",
+ crq->status);
+ ibmvmc_reset(adapter, false);
+ return;
+ }
+
+ ibmvmc_return_hmc(&hmcs[hmc_index], false);
+}
+
+/**
+ * ibmvmc_crq_process - Process CRQ
+ *
+ * @adapter: crq_server_adapter struct
+ * @crq: ibmvmc_crq_msg struct
+ *
+ * Process the CRQ message based upon the type of message received.
+ *
+ */
+static void ibmvmc_crq_process(struct crq_server_adapter *adapter,
+ struct ibmvmc_crq_msg *crq)
+{
+ switch (crq->type) {
+ case VMC_MSG_CAP_RESP:
+ dev_dbg(adapter->dev, "CRQ recv: capabilities resp (0x%x)\n",
+ crq->type);
+ if (ibmvmc.state == ibmvmc_state_capabilities)
+ ibmvmc_process_capabilities(adapter, crq);
+ else
+ dev_warn(adapter->dev, "caps msg invalid in state 0x%x\n",
+ ibmvmc.state);
+ break;
+ case VMC_MSG_OPEN_RESP:
+ dev_dbg(adapter->dev, "CRQ recv: open resp (0x%x)\n",
+ crq->type);
+ if (ibmvmc_validate_hmc_session(adapter, crq) == 0)
+ ibmvmc_process_open_resp(crq, adapter);
+ break;
+ case VMC_MSG_ADD_BUF:
+ dev_dbg(adapter->dev, "CRQ recv: add buf (0x%x)\n",
+ crq->type);
+ if (ibmvmc_validate_hmc_session(adapter, crq) == 0)
+ ibmvmc_add_buffer(adapter, crq);
+ break;
+ case VMC_MSG_REM_BUF:
+ dev_dbg(adapter->dev, "CRQ recv: rem buf (0x%x)\n",
+ crq->type);
+ if (ibmvmc_validate_hmc_session(adapter, crq) == 0)
+ ibmvmc_rem_buffer(adapter, crq);
+ break;
+ case VMC_MSG_SIGNAL:
+ dev_dbg(adapter->dev, "CRQ recv: signal msg (0x%x)\n",
+ crq->type);
+ if (ibmvmc_validate_hmc_session(adapter, crq) == 0)
+ ibmvmc_recv_msg(adapter, crq);
+ break;
+ case VMC_MSG_CLOSE_RESP:
+ dev_dbg(adapter->dev, "CRQ recv: close resp (0x%x)\n",
+ crq->type);
+ if (ibmvmc_validate_hmc_session(adapter, crq) == 0)
+ ibmvmc_process_close_resp(crq, adapter);
+ break;
+ case VMC_MSG_CAP:
+ case VMC_MSG_OPEN:
+ case VMC_MSG_CLOSE:
+ case VMC_MSG_ADD_BUF_RESP:
+ case VMC_MSG_REM_BUF_RESP:
+ dev_warn(adapter->dev, "CRQ recv: unexpected msg (0x%x)\n",
+ crq->type);
+ break;
+ default:
+ dev_warn(adapter->dev, "CRQ recv: unknown msg (0x%x)\n",
+ crq->type);
+ break;
+ }
+}
+
+/**
+ * ibmvmc_handle_crq_init - Handle CRQ Init
+ *
+ * @crq: ibmvmc_crq_msg struct
+ * @adapter: crq_server_adapter struct
+ *
+ * Handle the type of crq initialization based on whether
+ * it is a message or a response.
+ *
+ */
+static void ibmvmc_handle_crq_init(struct ibmvmc_crq_msg *crq,
+ struct crq_server_adapter *adapter)
+{
+ switch (crq->type) {
+ case 0x01: /* Initialization message */
+ dev_dbg(adapter->dev, "CRQ recv: CRQ init msg - state 0x%x\n",
+ ibmvmc.state);
+ if (ibmvmc.state == ibmvmc_state_crqinit) {
+ /* Send back a response */
+ if (ibmvmc_send_crq(adapter, 0xC002000000000000,
+ 0) == 0)
+ ibmvmc_send_capabilities(adapter);
+ else
+ dev_err(adapter->dev, " Unable to send init rsp\n");
+ } else {
+ dev_err(adapter->dev, "Invalid state 0x%x mtu = 0x%x\n",
+ ibmvmc.state, ibmvmc.max_mtu);
+ }
+
+ break;
+ case 0x02: /* Initialization response */
+ dev_dbg(adapter->dev, "CRQ recv: initialization resp msg - state 0x%x\n",
+ ibmvmc.state);
+ if (ibmvmc.state == ibmvmc_state_crqinit)
+ ibmvmc_send_capabilities(adapter);
+ break;
+ default:
+ dev_warn(adapter->dev, "Unknown crq message type 0x%lx\n",
+ (unsigned long)crq->type);
+ }
+}
+
+/**
+ * ibmvmc_handle_crq - Handle CRQ
+ *
+ * @crq: ibmvmc_crq_msg struct
+ * @adapter: crq_server_adapter struct
+ *
+ * Read the command elements from the command queue and execute the
+ * requests based upon the type of crq message.
+ *
+ */
+static void ibmvmc_handle_crq(struct ibmvmc_crq_msg *crq,
+ struct crq_server_adapter *adapter)
+{
+ switch (crq->valid) {
+ case 0xC0: /* initialization */
+ ibmvmc_handle_crq_init(crq, adapter);
+ break;
+ case 0xFF: /* Hypervisor telling us the connection is closed */
+ dev_warn(adapter->dev, "CRQ recv: virtual adapter failed - resetting.\n");
+ ibmvmc_reset(adapter, true);
+ break;
+ case 0x80: /* real payload */
+ ibmvmc_crq_process(adapter, crq);
+ break;
+ default:
+ dev_warn(adapter->dev, "CRQ recv: unknown msg 0x%02x.\n",
+ crq->valid);
+ break;
+ }
+}
+
+static void ibmvmc_task(unsigned long data)
+{
+ struct crq_server_adapter *adapter =
+ (struct crq_server_adapter *)data;
+ struct vio_dev *vdev = to_vio_dev(adapter->dev);
+ struct ibmvmc_crq_msg *crq;
+ int done = 0;
+
+ while (!done) {
+ /* Pull all the valid messages off the CRQ */
+ while ((crq = crq_queue_next_crq(&adapter->queue)) != NULL) {
+ ibmvmc_handle_crq(crq, adapter);
+ crq->valid = 0x00;
+ /* CRQ reset was requested, stop processing CRQs.
+ * Interrupts will be re-enabled by the reset task.
+ */
+ if (ibmvmc.state == ibmvmc_state_sched_reset)
+ return;
+ }
+
+ vio_enable_interrupts(vdev);
+ crq = crq_queue_next_crq(&adapter->queue);
+ if (crq) {
+ vio_disable_interrupts(vdev);
+ ibmvmc_handle_crq(crq, adapter);
+ crq->valid = 0x00;
+ /* CRQ reset was requested, stop processing CRQs.
+ * Interrupts will be re-enabled by the reset task.
+ */
+ if (ibmvmc.state == ibmvmc_state_sched_reset)
+ return;
+ } else {
+ done = 1;
+ }
+ }
+}
+
+/**
+ * ibmvmc_init_crq_queue - Init CRQ Queue
+ *
+ * @adapter: crq_server_adapter struct
+ *
+ * Return:
+ * 0 - Success
+ * Non-zero - Failure
+ */
+static int ibmvmc_init_crq_queue(struct crq_server_adapter *adapter)
+{
+ struct vio_dev *vdev = to_vio_dev(adapter->dev);
+ struct crq_queue *queue = &adapter->queue;
+ int rc;
+ int retrc;
+
+ queue->msgs = (struct ibmvmc_crq_msg *)get_zeroed_page(GFP_KERNEL);
+
+ if (!queue->msgs)
+ goto malloc_failed;
+
+ queue->size = PAGE_SIZE / sizeof(*queue->msgs);
+
+ queue->msg_token = dma_map_single(adapter->dev, queue->msgs,
+ queue->size * sizeof(*queue->msgs),
+ DMA_BIDIRECTIONAL);
+
+ if (dma_mapping_error(adapter->dev, queue->msg_token))
+ goto map_failed;
+
+ retrc = plpar_hcall_norets(H_REG_CRQ,
+ vdev->unit_address,
+ queue->msg_token, PAGE_SIZE);
+ retrc = rc;
+
+ if (rc == H_RESOURCE)
+ rc = ibmvmc_reset_crq_queue(adapter);
+
+ if (rc == 2) {
+ dev_warn(adapter->dev, "Partner adapter not ready\n");
+ retrc = 0;
+ } else if (rc != 0) {
+ dev_err(adapter->dev, "Error %d opening adapter\n", rc);
+ goto reg_crq_failed;
+ }
+
+ queue->cur = 0;
+ spin_lock_init(&queue->lock);
+
+ tasklet_init(&adapter->work_task, ibmvmc_task, (unsigned long)adapter);
+
+ if (request_irq(vdev->irq,
+ ibmvmc_handle_event,
+ 0, "ibmvmc", (void *)adapter) != 0) {
+ dev_err(adapter->dev, "couldn't register irq 0x%x\n",
+ vdev->irq);
+ goto req_irq_failed;
+ }
+
+ rc = vio_enable_interrupts(vdev);
+ if (rc != 0) {
+ dev_err(adapter->dev, "Error %d enabling interrupts!!!\n", rc);
+ goto req_irq_failed;
+ }
+
+ return retrc;
+
+req_irq_failed:
+ /* Cannot have any work since we either never got our IRQ registered,
+ * or never got interrupts enabled
+ */
+ tasklet_kill(&adapter->work_task);
+ h_free_crq(vdev->unit_address);
+reg_crq_failed:
+ dma_unmap_single(adapter->dev,
+ queue->msg_token,
+ queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL);
+map_failed:
+ free_page((unsigned long)queue->msgs);
+malloc_failed:
+ return -ENOMEM;
+}
+
+/* Fill in the liobn and riobn fields on the adapter */
+static int read_dma_window(struct vio_dev *vdev,
+ struct crq_server_adapter *adapter)
+{
+ const __be32 *dma_window;
+ const __be32 *prop;
+
+ /* TODO Using of_parse_dma_window would be better, but it doesn't give
+ * a way to read multiple windows without already knowing the size of
+ * a window or the number of windows
+ */
+ dma_window =
+ (const __be32 *)vio_get_attribute(vdev, "ibm,my-dma-window",
+ NULL);
+ if (!dma_window) {
+ dev_warn(adapter->dev, "Couldn't find ibm,my-dma-window property\n");
+ return -1;
+ }
+
+ adapter->liobn = be32_to_cpu(*dma_window);
+ dma_window++;
+
+ prop = (const __be32 *)vio_get_attribute(vdev, "ibm,#dma-address-cells",
+ NULL);
+ if (!prop) {
+ dev_warn(adapter->dev, "Couldn't find ibm,#dma-address-cells property\n");
+ dma_window++;
+ } else {
+ dma_window += be32_to_cpu(*prop);
+ }
+
+ prop = (const __be32 *)vio_get_attribute(vdev, "ibm,#dma-size-cells",
+ NULL);
+ if (!prop) {
+ dev_warn(adapter->dev, "Couldn't find ibm,#dma-size-cells property\n");
+ dma_window++;
+ } else {
+ dma_window += be32_to_cpu(*prop);
+ }
+
+ /* dma_window should point to the second window now */
+ adapter->riobn = be32_to_cpu(*dma_window);
+
+ return 0;
+}
+
+static int ibmvmc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
+{
+ struct crq_server_adapter *adapter = &ibmvmc_adapter;
+ int rc;
+
+ dev_set_drvdata(&vdev->dev, NULL);
+ memset(adapter, 0, sizeof(*adapter));
+ adapter->dev = &vdev->dev;
+
+ dev_info(adapter->dev, "Probe for UA 0x%x\n", vdev->unit_address);
+
+ rc = read_dma_window(vdev, adapter);
+ if (rc != 0) {
+ ibmvmc.state = ibmvmc_state_failed;
+ return -1;
+ }
+
+ dev_dbg(adapter->dev, "Probe: liobn 0x%x, riobn 0x%x\n",
+ adapter->liobn, adapter->riobn);
+
+ init_waitqueue_head(&adapter->reset_wait_queue);
+ adapter->reset_task = kthread_run(ibmvmc_reset_task, adapter, "ibmvmc");
+ if (IS_ERR(adapter->reset_task)) {
+ dev_err(adapter->dev, "Failed to start reset thread\n");
+ ibmvmc.state = ibmvmc_state_failed;
+ rc = PTR_ERR(adapter->reset_task);
+ adapter->reset_task = NULL;
+ return rc;
+ }
+
+ rc = ibmvmc_init_crq_queue(adapter);
+ if (rc != 0 && rc != H_RESOURCE) {
+ dev_err(adapter->dev, "Error initializing CRQ. rc = 0x%x\n",
+ rc);
+ ibmvmc.state = ibmvmc_state_failed;
+ goto crq_failed;
+ }
+
+ ibmvmc.state = ibmvmc_state_crqinit;
+
+ /* Try to send an initialization message. Note that this is allowed
+ * to fail if the other end is not acive. In that case we just wait
+ * for the other side to initialize.
+ */
+ if (ibmvmc_send_crq(adapter, 0xC001000000000000LL, 0) != 0 &&
+ rc != H_RESOURCE)
+ dev_warn(adapter->dev, "Failed to send initialize CRQ message\n");
+
+ dev_set_drvdata(&vdev->dev, adapter);
+
+ return 0;
+
+crq_failed:
+ kthread_stop(adapter->reset_task);
+ adapter->reset_task = NULL;
+ return -EPERM;
+}
+
+static int ibmvmc_remove(struct vio_dev *vdev)
+{
+ struct crq_server_adapter *adapter = dev_get_drvdata(&vdev->dev);
+
+ dev_info(adapter->dev, "Entering remove for UA 0x%x\n",
+ vdev->unit_address);
+ ibmvmc_release_crq_queue(adapter);
+
+ return 0;
+}
+
+static struct vio_device_id ibmvmc_device_table[] = {
+ { "ibm,vmc", "IBM,vmc" },
+ { "", "" }
+};
+MODULE_DEVICE_TABLE(vio, ibmvmc_device_table);
+
+static struct vio_driver ibmvmc_driver = {
+ .name = ibmvmc_driver_name,
+ .id_table = ibmvmc_device_table,
+ .probe = ibmvmc_probe,
+ .remove = ibmvmc_remove,
+};
+
+static void __init ibmvmc_scrub_module_parms(void)
+{
+ if (ibmvmc_max_mtu > MAX_MTU) {
+ pr_warn("ibmvmc: Max MTU reduced to %d\n", MAX_MTU);
+ ibmvmc_max_mtu = MAX_MTU;
+ } else if (ibmvmc_max_mtu < MIN_MTU) {
+ pr_warn("ibmvmc: Max MTU increased to %d\n", MIN_MTU);
+ ibmvmc_max_mtu = MIN_MTU;
+ }
+
+ if (ibmvmc_max_buf_pool_size > MAX_BUF_POOL_SIZE) {
+ pr_warn("ibmvmc: Max buffer pool size reduced to %d\n",
+ MAX_BUF_POOL_SIZE);
+ ibmvmc_max_buf_pool_size = MAX_BUF_POOL_SIZE;
+ } else if (ibmvmc_max_buf_pool_size < MIN_BUF_POOL_SIZE) {
+ pr_warn("ibmvmc: Max buffer pool size increased to %d\n",
+ MIN_BUF_POOL_SIZE);
+ ibmvmc_max_buf_pool_size = MIN_BUF_POOL_SIZE;
+ }
+
+ if (ibmvmc_max_hmcs > MAX_HMCS) {
+ pr_warn("ibmvmc: Max HMCs reduced to %d\n", MAX_HMCS);
+ ibmvmc_max_hmcs = MAX_HMCS;
+ } else if (ibmvmc_max_hmcs < MIN_HMCS) {
+ pr_warn("ibmvmc: Max HMCs increased to %d\n", MIN_HMCS);
+ ibmvmc_max_hmcs = MIN_HMCS;
+ }
+}
+
+static struct miscdevice ibmvmc_miscdev = {
+ .name = ibmvmc_driver_name,
+ .minor = MISC_DYNAMIC_MINOR,
+ .fops = &ibmvmc_fops,
+};
+
+static int __init ibmvmc_module_init(void)
+{
+ int rc, i, j;
+
+ ibmvmc.state = ibmvmc_state_initial;
+ pr_info("ibmvmc: version %s\n", IBMVMC_DRIVER_VERSION);
+
+ rc = misc_register(&ibmvmc_miscdev);
+ if (rc) {
+ pr_err("ibmvmc: misc registration failed\n");
+ goto misc_register_failed;
+ }
+ pr_info("ibmvmc: node %d:%d\n", MISC_MAJOR,
+ ibmvmc_miscdev.minor);
+
+ /* Initialize data structures */
+ memset(hmcs, 0, sizeof(struct ibmvmc_hmc) * MAX_HMCS);
+ for (i = 0; i < MAX_HMCS; i++) {
+ spin_lock_init(&hmcs[i].lock);
+ hmcs[i].state = ibmhmc_state_free;
+ for (j = 0; j < MAX_BUF_POOL_SIZE; j++)
+ hmcs[i].queue_outbound_msgs[j] = VMC_INVALID_BUFFER_ID;
+ }
+
+ /* Sanity check module parms */
+ ibmvmc_scrub_module_parms();
+
+ /*
+ * Initialize some reasonable values. Might be negotiated smaller
+ * values during the capabilities exchange.
+ */
+ ibmvmc.max_mtu = ibmvmc_max_mtu;
+ ibmvmc.max_buffer_pool_size = ibmvmc_max_buf_pool_size;
+ ibmvmc.max_hmc_index = ibmvmc_max_hmcs - 1;
+
+ rc = vio_register_driver(&ibmvmc_driver);
+
+ if (rc) {
+ pr_err("ibmvmc: rc %d from vio_register_driver\n", rc);
+ goto vio_reg_failed;
+ }
+
+ return 0;
+
+vio_reg_failed:
+ misc_deregister(&ibmvmc_miscdev);
+misc_register_failed:
+ return rc;
+}
+
+static void __exit ibmvmc_module_exit(void)
+{
+ pr_info("ibmvmc: module exit\n");
+ vio_unregister_driver(&ibmvmc_driver);
+ misc_deregister(&ibmvmc_miscdev);
+}
+
+module_init(ibmvmc_module_init);
+module_exit(ibmvmc_module_exit);
+
+module_param_named(buf_pool_size, ibmvmc_max_buf_pool_size,
+ int, 0644);
+MODULE_PARM_DESC(buf_pool_size, "Buffer pool size");
+module_param_named(max_hmcs, ibmvmc_max_hmcs, int, 0644);
+MODULE_PARM_DESC(max_hmcs, "Max HMCs");
+module_param_named(max_mtu, ibmvmc_max_mtu, int, 0644);
+MODULE_PARM_DESC(max_mtu, "Max MTU");
+
+MODULE_AUTHOR("Steven Royer <seroyer@linux.vnet.ibm.com>");
+MODULE_DESCRIPTION("IBM VMC");
+MODULE_VERSION(IBMVMC_DRIVER_VERSION);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/ibmvmc.h b/drivers/misc/ibmvmc.h
new file mode 100644
index 0000000..e140ada
--- /dev/null
+++ b/drivers/misc/ibmvmc.h
@@ -0,0 +1,209 @@
+/* SPDX-License-Identifier: GPL-2.0+
+ *
+ * linux/drivers/misc/ibmvmc.h
+ *
+ * IBM Power Systems Virtual Management Channel Support.
+ *
+ * Copyright (c) 2004, 2018 IBM Corp.
+ * Dave Engebretsen engebret@us.ibm.com
+ * Steven Royer seroyer@linux.vnet.ibm.com
+ * Adam Reznechek adreznec@linux.vnet.ibm.com
+ * Bryant G. Ly <bryantly@linux.vnet.ibm.com>
+ */
+#ifndef IBMVMC_H
+#define IBMVMC_H
+
+#include <linux/types.h>
+#include <linux/cdev.h>
+
+#include <asm/vio.h>
+
+#define IBMVMC_PROTOCOL_VERSION 0x0101
+
+#define MIN_BUF_POOL_SIZE 16
+#define MIN_HMCS 1
+#define MIN_MTU 4096
+#define MAX_BUF_POOL_SIZE 64
+#define MAX_HMCS 2
+#define MAX_MTU (4 * 4096)
+#define DEFAULT_BUF_POOL_SIZE 32
+#define DEFAULT_HMCS 1
+#define DEFAULT_MTU 4096
+#define HMC_ID_LEN 32
+
+#define VMC_INVALID_BUFFER_ID 0xFFFF
+
+/* ioctl numbers */
+#define VMC_BASE 0xCC
+#define VMC_IOCTL_SETHMCID _IOW(VMC_BASE, 0x00, unsigned char *)
+#define VMC_IOCTL_QUERY _IOR(VMC_BASE, 0x01, struct ibmvmc_query_struct)
+#define VMC_IOCTL_REQUESTVMC _IOR(VMC_BASE, 0x02, u32)
+
+#define VMC_MSG_CAP 0x01
+#define VMC_MSG_CAP_RESP 0x81
+#define VMC_MSG_OPEN 0x02
+#define VMC_MSG_OPEN_RESP 0x82
+#define VMC_MSG_CLOSE 0x03
+#define VMC_MSG_CLOSE_RESP 0x83
+#define VMC_MSG_ADD_BUF 0x04
+#define VMC_MSG_ADD_BUF_RESP 0x84
+#define VMC_MSG_REM_BUF 0x05
+#define VMC_MSG_REM_BUF_RESP 0x85
+#define VMC_MSG_SIGNAL 0x06
+
+#define VMC_MSG_SUCCESS 0
+#define VMC_MSG_INVALID_HMC_INDEX 1
+#define VMC_MSG_INVALID_BUFFER_ID 2
+#define VMC_MSG_CLOSED_HMC 3
+#define VMC_MSG_INTERFACE_FAILURE 4
+#define VMC_MSG_NO_BUFFER 5
+
+#define VMC_BUF_OWNER_ALPHA 0
+#define VMC_BUF_OWNER_HV 1
+
+enum ibmvmc_states {
+ ibmvmc_state_sched_reset = -1,
+ ibmvmc_state_initial = 0,
+ ibmvmc_state_crqinit = 1,
+ ibmvmc_state_capabilities = 2,
+ ibmvmc_state_ready = 3,
+ ibmvmc_state_failed = 4,
+};
+
+enum ibmhmc_states {
+ /* HMC connection not established */
+ ibmhmc_state_free = 0,
+
+ /* HMC connection established (open called) */
+ ibmhmc_state_initial = 1,
+
+ /* open msg sent to HV, due to ioctl(1) call */
+ ibmhmc_state_opening = 2,
+
+ /* HMC connection ready, open resp msg from HV */
+ ibmhmc_state_ready = 3,
+
+ /* HMC connection failure */
+ ibmhmc_state_failed = 4,
+};
+
+struct ibmvmc_buffer {
+ u8 valid; /* 1 when DMA storage allocated to buffer */
+ u8 free; /* 1 when buffer available for the Alpha Partition */
+ u8 owner;
+ u16 id;
+ u32 size;
+ u32 msg_len;
+ dma_addr_t dma_addr_local;
+ dma_addr_t dma_addr_remote;
+ void *real_addr_local;
+};
+
+struct ibmvmc_admin_crq_msg {
+ u8 valid; /* RPA Defined */
+ u8 type; /* ibmvmc msg type */
+ u8 status; /* Response msg status. Zero is success and on failure,
+ * either 1 - General Failure, or 2 - Invalid Version is
+ * returned.
+ */
+ u8 rsvd[2];
+ u8 max_hmc; /* Max # of independent HMC connections supported */
+ __be16 pool_size; /* Maximum number of buffers supported per HMC
+ * connection
+ */
+ __be32 max_mtu; /* Maximum message size supported (bytes) */
+ __be16 crq_size; /* # of entries available in the CRQ for the
+ * source partition. The target partition must
+ * limit the number of outstanding messages to
+ * one half or less.
+ */
+ __be16 version; /* Indicates the code level of the management partition
+ * or the hypervisor with the high-order byte
+ * indicating a major version and the low-order byte
+ * indicating a minor version.
+ */
+};
+
+struct ibmvmc_crq_msg {
+ u8 valid; /* RPA Defined */
+ u8 type; /* ibmvmc msg type */
+ u8 status; /* Response msg status */
+ union {
+ u8 rsvd; /* Reserved */
+ u8 owner;
+ } var1;
+ u8 hmc_session; /* Session Identifier for the current VMC connection */
+ u8 hmc_index; /* A unique HMC Idx would be used if multiple management
+ * applications running concurrently were desired
+ */
+ union {
+ __be16 rsvd;
+ __be16 buffer_id;
+ } var2;
+ __be32 rsvd;
+ union {
+ __be32 rsvd;
+ __be32 lioba;
+ __be32 msg_len;
+ } var3;
+};
+
+/* an RPA command/response transport queue */
+struct crq_queue {
+ struct ibmvmc_crq_msg *msgs;
+ int size, cur;
+ dma_addr_t msg_token;
+ spinlock_t lock;
+};
+
+/* VMC server adapter settings */
+struct crq_server_adapter {
+ struct device *dev;
+ struct crq_queue queue;
+ u32 liobn;
+ u32 riobn;
+ struct tasklet_struct work_task;
+ wait_queue_head_t reset_wait_queue;
+ struct task_struct *reset_task;
+};
+
+/* Driver wide settings */
+struct ibmvmc_struct {
+ u32 state;
+ u32 max_mtu;
+ u32 max_buffer_pool_size;
+ u32 max_hmc_index;
+ struct crq_server_adapter *adapter;
+ struct cdev cdev;
+ u32 vmc_drc_index;
+};
+
+struct ibmvmc_file_session;
+
+/* Connection specific settings */
+struct ibmvmc_hmc {
+ u8 session;
+ u8 index;
+ u32 state;
+ struct crq_server_adapter *adapter;
+ spinlock_t lock;
+ unsigned char hmc_id[HMC_ID_LEN];
+ struct ibmvmc_buffer buffer[MAX_BUF_POOL_SIZE];
+ unsigned short queue_outbound_msgs[MAX_BUF_POOL_SIZE];
+ int queue_head, queue_tail;
+ struct ibmvmc_file_session *file_session;
+};
+
+struct ibmvmc_file_session {
+ struct file *file;
+ struct ibmvmc_hmc *hmc;
+ bool valid;
+};
+
+struct ibmvmc_query_struct {
+ int have_vmc;
+ int state;
+ int vmc_drc_index;
+};
+
+#endif /* __IBMVMC_H */
--
2.7.2
^ permalink raw reply related
* [PATCH v1 0/1] misc: IBM Virtual Management Channel Driver
From: Bryant G. Ly @ 2018-04-23 14:46 UTC (permalink / raw)
To: benh, mpe, gregkh, arnd
Cc: corbet, seroyer, mrochs, adreznec, fbarrat, davem, linus.walleij,
akpm, rdunlap, mikey, pombredanne, tlfalcon, msuchanek, linux-doc,
linuxppc-dev, Bryant G. Ly
Steven Royer had previously attempted to upstream this
driver two years ago, but never got the chance to address
the concerns from Greg Kroah-Hartman.
The thread with the initial upstream is:
https://lkml.org/lkml/2016/2/16/918
I have addressed the following:
- Documentation
- Use of dev_dbg instead of pr_dbg
- Change to misc class
- Fixed memory barrier usages
- Addressed styling, checkpatch, renaming of functions
- General fixes to the driver to make it more inline with
existing upstream drivers.
Bryant G. Ly (1):
misc: IBM Virtual Management Channel Driver
Documentation/ioctl/ioctl-number.txt | 1 +
Documentation/misc-devices/ibmvmc.txt | 161 +++
MAINTAINERS | 6 +
arch/powerpc/include/asm/hvcall.h | 1 +
drivers/misc/Kconfig | 9 +
drivers/misc/Makefile | 1 +
drivers/misc/ibmvmc.c | 2413 +++++++++++++++++++++++++++++++++
drivers/misc/ibmvmc.h | 215 +++
8 files changed, 2807 insertions(+)
create mode 100644 Documentation/misc-devices/ibmvmc.txt
create mode 100644 drivers/misc/ibmvmc.c
create mode 100644 drivers/misc/ibmvmc.h
--
2.7.2
^ permalink raw reply
* Re: [PATCH] powerpc/mce: Fix a bug where mce loops on memory UE.
From: Mahesh Jagannath Salgaonkar @ 2018-04-23 13:01 UTC (permalink / raw)
To: Balbir Singh; +Cc: linuxppc-dev
In-Reply-To: <CAKTCnzm=OAXmUp0g7YrR9wFoFwB3TS_LnQiwZPR6QgeKk6ExYQ@mail.gmail.com>
On 04/23/2018 04:44 PM, Balbir Singh wrote:
> On Mon, Apr 23, 2018 at 8:33 PM, Mahesh Jagannath Salgaonkar
> <mahesh@linux.vnet.ibm.com> wrote:
>> On 04/23/2018 12:21 PM, Balbir Singh wrote:
>>> On Mon, Apr 23, 2018 at 2:59 PM, Mahesh J Salgaonkar
>>> <mahesh@linux.vnet.ibm.com> wrote:
>>>> From: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
>>>>
>>>> The current code extracts the physical address for UE errors and then
>>>> hooks it up into memory failure infrastructure. On successful extraction
>>>> of physical address it wrongly sets "handled = 1" which means this UE error
>>>> has been recovered. Since MCE handler gets return value as handled = 1, it
>>>> assumes that error has been recovered and goes back to same NIP. This causes
>>>> MCE interrupt again and again in a loop leading to hard lockup.
>>>>
>>>> Also, initialize phys_addr to ULONG_MAX so that we don't end up queuing
>>>> undesired page to hwpoison.
>>>>
>>>> Without this patch we see:
>>>> [ 1476.541984] Severe Machine check interrupt [Recovered]
>>>> [ 1476.541985] NIP: [000000001002588c] PID: 7109 Comm: find
>>>> [ 1476.541986] Initiator: CPU
>>>> [ 1476.541987] Error type: UE [Load/Store]
>>>> [ 1476.541988] Effective address: 00007fffd2755940
>>>> [ 1476.541989] Physical address: 000020181a080000
>>>> [...]
>>>> [ 1476.542003] Severe Machine check interrupt [Recovered]
>>>> [ 1476.542004] NIP: [000000001002588c] PID: 7109 Comm: find
>>>> [ 1476.542005] Initiator: CPU
>>>> [ 1476.542006] Error type: UE [Load/Store]
>>>> [ 1476.542006] Effective address: 00007fffd2755940
>>>> [ 1476.542007] Physical address: 000020181a080000
>>>> [ 1476.542010] Severe Machine check interrupt [Recovered]
>>>> [ 1476.542012] NIP: [000000001002588c] PID: 7109 Comm: find
>>>> [ 1476.542013] Initiator: CPU
>>>> [ 1476.542014] Error type: UE [Load/Store]
>>>> [ 1476.542015] Effective address: 00007fffd2755940
>>>> [ 1476.542016] Physical address: 000020181a080000
>>>> [ 1476.542448] Memory failure: 0x20181a08: recovery action for dirty LRU page: Recovered
>>>> [ 1476.542452] Memory failure: 0x20181a08: already hardware poisoned
>>>> [ 1476.542453] Memory failure: 0x20181a08: already hardware poisoned
>>>> [ 1476.542454] Memory failure: 0x20181a08: already hardware poisoned
>>>> [ 1476.542455] Memory failure: 0x20181a08: already hardware poisoned
>>>> [ 1476.542456] Memory failure: 0x20181a08: already hardware poisoned
>>>> [ 1476.542457] Memory failure: 0x20181a08: already hardware poisoned
>>>> [...]
>>>> [ 1490.972174] Watchdog CPU:38 Hard LOCKUP
>>>>
>>>> After this patch we see:
>>>>
>>>> [ 325.384336] Severe Machine check interrupt [Not recovered]
>>>
>>> How did you test for this?
>>
>> By injecting cache SUE using L2 FIR register (0x1001080c).
>>
>>> If the error was recovered, shouldn't the
>>> process have gotten
>>> a SIGBUS and we should have prevented further access as a part of the handling
>>> (memory_failure()). Do we just need a MF_MUST_KILL in the flags?
>>
>> We hook it up to memory_failure() through a work queue and by the time
>> work queue kicks in, the application continues to restart and hit same
>> NIP again and again. Every MCE again hooks the same address to memory
>> failure work queue and throws multiple recovered MCE messages for same
>> address. Once the memory_failure() hwpoisons the page, application gets
>> SIGBUS and then we are fine.
>>
>
> That seems quite broken and not recovered is very confusing. So effectively
> we can never recover from a MCE UE.
By not setting handle = 1, the recovery code will fall through
machine_check_exception()->opal_machine_check() and then SIGBUS is sent
to this process to recover OR head to panic path for kernel UE. We have
already hooked up the physical address to memory_failure() which will
later hwpoison the page whenever work queue kicks in. This patch makes
sure this happens.
> I think we need a notion of delayed
> recovery then? Where we do recover, but mark is as recovered with delays?
Yeah, may be we can set disposition for userspace mce event as recovery
in progress/delayed and then print the mce event again from work queue
by looking at return value from memory_failure(). What do you think ?
> We might want to revisit our recovery process and see if the recovery requires
> to turn the MMU on, but that is for later, I suppose.
>
>> But in case of UE in kernel space, if early machine_check handler
>> "machine_check_early()" returns as recovered then
>> machine_check_handle_early() queues up the MCE event and continues from
>> NIP assuming it is safe causing a MCE loop. So, for UE in kernel we end
>> up in hard lockup.
>>
>
> Yeah for the kernel, we need to definitely cause a panic for now, I've got other
> patches for things we need to do for pmem that would allow potential recovery.
>
> Balbir Singh
>
^ permalink raw reply
* Re: [PATCH] powerpc/mce: Fix a bug where mce loops on memory UE.
From: Nicholas Piggin @ 2018-04-23 13:01 UTC (permalink / raw)
To: Balbir Singh; +Cc: Mahesh Jagannath Salgaonkar, linuxppc-dev
In-Reply-To: <CAKTCnzm=OAXmUp0g7YrR9wFoFwB3TS_LnQiwZPR6QgeKk6ExYQ@mail.gmail.com>
On Mon, 23 Apr 2018 21:14:12 +1000
Balbir Singh <bsingharora@gmail.com> wrote:
> On Mon, Apr 23, 2018 at 8:33 PM, Mahesh Jagannath Salgaonkar
> <mahesh@linux.vnet.ibm.com> wrote:
> > On 04/23/2018 12:21 PM, Balbir Singh wrote:
> >> On Mon, Apr 23, 2018 at 2:59 PM, Mahesh J Salgaonkar
> >> <mahesh@linux.vnet.ibm.com> wrote:
> >>> From: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
> >>>
> >>> The current code extracts the physical address for UE errors and then
> >>> hooks it up into memory failure infrastructure. On successful extraction
> >>> of physical address it wrongly sets "handled = 1" which means this UE error
> >>> has been recovered. Since MCE handler gets return value as handled = 1, it
> >>> assumes that error has been recovered and goes back to same NIP. This causes
> >>> MCE interrupt again and again in a loop leading to hard lockup.
> >>>
> >>> Also, initialize phys_addr to ULONG_MAX so that we don't end up queuing
> >>> undesired page to hwpoison.
> >>>
> >>> Without this patch we see:
> >>> [ 1476.541984] Severe Machine check interrupt [Recovered]
> >>> [ 1476.541985] NIP: [000000001002588c] PID: 7109 Comm: find
> >>> [ 1476.541986] Initiator: CPU
> >>> [ 1476.541987] Error type: UE [Load/Store]
> >>> [ 1476.541988] Effective address: 00007fffd2755940
> >>> [ 1476.541989] Physical address: 000020181a080000
> >>> [...]
> >>> [ 1476.542003] Severe Machine check interrupt [Recovered]
> >>> [ 1476.542004] NIP: [000000001002588c] PID: 7109 Comm: find
> >>> [ 1476.542005] Initiator: CPU
> >>> [ 1476.542006] Error type: UE [Load/Store]
> >>> [ 1476.542006] Effective address: 00007fffd2755940
> >>> [ 1476.542007] Physical address: 000020181a080000
> >>> [ 1476.542010] Severe Machine check interrupt [Recovered]
> >>> [ 1476.542012] NIP: [000000001002588c] PID: 7109 Comm: find
> >>> [ 1476.542013] Initiator: CPU
> >>> [ 1476.542014] Error type: UE [Load/Store]
> >>> [ 1476.542015] Effective address: 00007fffd2755940
> >>> [ 1476.542016] Physical address: 000020181a080000
> >>> [ 1476.542448] Memory failure: 0x20181a08: recovery action for dirty LRU page: Recovered
> >>> [ 1476.542452] Memory failure: 0x20181a08: already hardware poisoned
> >>> [ 1476.542453] Memory failure: 0x20181a08: already hardware poisoned
> >>> [ 1476.542454] Memory failure: 0x20181a08: already hardware poisoned
> >>> [ 1476.542455] Memory failure: 0x20181a08: already hardware poisoned
> >>> [ 1476.542456] Memory failure: 0x20181a08: already hardware poisoned
> >>> [ 1476.542457] Memory failure: 0x20181a08: already hardware poisoned
> >>> [...]
> >>> [ 1490.972174] Watchdog CPU:38 Hard LOCKUP
> >>>
> >>> After this patch we see:
> >>>
> >>> [ 325.384336] Severe Machine check interrupt [Not recovered]
> >>
> >> How did you test for this?
> >
> > By injecting cache SUE using L2 FIR register (0x1001080c).
> >
> >> If the error was recovered, shouldn't the
> >> process have gotten
> >> a SIGBUS and we should have prevented further access as a part of the handling
> >> (memory_failure()). Do we just need a MF_MUST_KILL in the flags?
> >
> > We hook it up to memory_failure() through a work queue and by the time
> > work queue kicks in, the application continues to restart and hit same
> > NIP again and again. Every MCE again hooks the same address to memory
> > failure work queue and throws multiple recovered MCE messages for same
> > address. Once the memory_failure() hwpoisons the page, application gets
> > SIGBUS and then we are fine.
> >
>
> That seems quite broken and not recovered is very confusing. So effectively
> we can never recover from a MCE UE. I think we need a notion of delayed
> recovery then? Where we do recover, but mark is as recovered with delays?
> We might want to revisit our recovery process and see if the recovery requires
> to turn the MMU on, but that is for later, I suppose.
The notion of being handled in the machine check return value is not
whether the failing resource is later de-allocated or fixed, but if
*this* particular exception was able to be corrected / processing
resume as normal without further action.
The MCE UE is not recovered just by finding its address here, so I think
Mahesh's patch is right.
You can still recover it with further action later.
>
> > But in case of UE in kernel space, if early machine_check handler
> > "machine_check_early()" returns as recovered then
> > machine_check_handle_early() queues up the MCE event and continues from
> > NIP assuming it is safe causing a MCE loop. So, for UE in kernel we end
> > up in hard lockup.
> >
>
> Yeah for the kernel, we need to definitely cause a panic for now, I've got other
> patches for things we need to do for pmem that would allow potential recovery.
>
> Balbir Singh
^ permalink raw reply
* Re: [PATCH v4 4/7] powerpc/fadump: Reservationless firmware assisted dump
From: Hari Bathini @ 2018-04-23 12:53 UTC (permalink / raw)
To: Mahesh J Salgaonkar, linuxppc-dev
Cc: Ananth Narayan, kernelfans, Aneesh Kumar K.V, Nathan Fontenot,
Anshuman Khandual, Srikar Dronamraju
In-Reply-To: <152420068315.31037.10792452404355231147.stgit@jupiter.in.ibm.com>
On Friday 20 April 2018 10:34 AM, Mahesh J Salgaonkar wrote:
> From: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
>
> One of the primary issues with Firmware Assisted Dump (fadump) on Power
> is that it needs a large amount of memory to be reserved. On large
> systems with TeraBytes of memory, this reservation can be quite
> significant.
>
> In some cases, fadump fails if the memory reserved is insufficient, or
> if the reserved memory was DLPAR hot-removed.
>
> In the normal case, post reboot, the preserved memory is filtered to
> extract only relevant areas of interest using the makedumpfile tool.
> While the tool provides flexibility to determine what needs to be part
> of the dump and what memory to filter out, all supported distributions
> default this to "Capture only kernel data and nothing else".
>
> We take advantage of this default and the Linux kernel's Contiguous
> Memory Allocator (CMA) to fundamentally change the memory reservation
> model for fadump.
>
> Instead of setting aside a significant chunk of memory nobody can use,
> this patch uses CMA instead, to reserve a significant chunk of memory
> that the kernel is prevented from using (due to MIGRATE_CMA), but
> applications are free to use it. With this fadump will still be able
> to capture all of the kernel memory and most of the user space memory
> except the user pages that were present in CMA region.
>
> Essentially, on a P9 LPAR with 2 cores, 8GB RAM and current upstream:
> [root@zzxx-yy10 ~]# free -m
> total used free shared buff/cache available
> Mem: 7557 193 6822 12 541 6725
> Swap: 4095 0 4095
>
> With this patch:
> [root@zzxx-yy10 ~]# free -m
> total used free shared buff/cache available
> Mem: 8133 194 7464 12 475 7338
> Swap: 4095 0 4095
>
> Changes made here are completely transparent to how fadump has
> traditionally worked.
>
> Thanks to Aneesh Kumar and Anshuman Khandual for helping us understand
> CMA and its usage.
>
> TODO:
> - Handle case where CMA reservation spans nodes.
>
> Signed-off-by: Ananth N Mavinakayanahalli <ananth@linux.vnet.ibm.com>
> Signed-off-by: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
> ---
> arch/powerpc/kernel/fadump.c | 120 ++++++++++++++++++++++++++++++++++++------
> 1 file changed, 103 insertions(+), 17 deletions(-)
>
> diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c
> index 16b3e8c5cae0..7f76924ab190 100644
> --- a/arch/powerpc/kernel/fadump.c
> +++ b/arch/powerpc/kernel/fadump.c
> @@ -34,6 +34,7 @@
> #include <linux/crash_dump.h>
> #include <linux/kobject.h>
> #include <linux/sysfs.h>
> +#include <linux/cma.h>
>
> #include <asm/debugfs.h>
> #include <asm/page.h>
> @@ -45,11 +46,57 @@
> static struct fw_dump fw_dump;
> static struct fadump_mem_struct fdm;
> static const struct fadump_mem_struct *fdm_active;
> +static struct cma *fadump_cma;
>
> static DEFINE_MUTEX(fadump_mutex);
> struct fad_crash_memory_ranges crash_memory_ranges[INIT_CRASHMEM_RANGES];
> int crash_mem_ranges;
>
> +/*
> + * fadump_cma_reserve() - reserve area for fadump memory reservation
> + *
> + * This function reserves memory from early allocator. It should be
> + * called by arch specific code once the memblock allocator
> + * has been activated.
> + */
> +int __init fadump_cma_reserve(void)
> +{
> + unsigned long long base, size;
> + int rc;
> +
> + if (!fw_dump.fadump_enabled)
> + return 0;
> +
> + base = fw_dump.reserve_dump_area_start;
> + size = fw_dump.reserve_dump_area_size;
Mahesh, How about moving sections around instead:
Old:
1. cpu state data region
2. hpte region
3. real memory region
New:
2. cpu state data region
3. hpte region
1. real memory region
and using only boot memory size for cma reserve. The other regions,
crashinfo header
& elfcorehdrs can still use memblock_reserve.
This achieves two things. One, ensures we don't waste memory in alignment
as cma uses hugepage(16MB)/maxorder as default alignment (we need to
ensure boot
memory size is aligned by hugepage(16MB)/maxorder though). Two, we don't
have to
move around meta data from end to start (patch 1/7)
To differentiate the old and new section order, we can overload crash
info magic
(FADUMPINF -> FADUMPIV2), I guess. That differentiation may be needed for
re-registering after dump capture..
> + pr_debug("Original reserve area base %ld, size %ld\n",
> + (unsigned long)base >> 20,
> + (unsigned long)size >> 20);
> + if (!size)
> + return 0;
> +
> + rc = cma_declare_contiguous(base, size, 0, 0, 0, false,
> + "fadump_cma", &fadump_cma);
Compilation fails when CONFIG_CMA is not set. A fallback when CONFIG_CMA
is not set or dependency enforced for FA_DUMP config option seems to be
missing..
Also, considering we already deduce the base by looking for holes in
fadump code, we could
have a 'fixed' ('true' for 6th parameter) cma region? Again, we have to
ensure CMA alignment
for boot memory size in fadump_calculate_reserve_size() for doing all
this seamlessly..
> + if (rc) {
> + printk(KERN_ERR "fadump: Failed to reserve cma area for "
> + "firmware-assisted dump, %d\n", rc);
> + fw_dump.reserve_dump_area_size = 0;
> + return 0;
> + }
> + /*
> + * So we now have cma area reserved for fadump. base may be different
> + * from what we requested.
> + */
> + fw_dump.reserve_dump_area_start = cma_get_base(fadump_cma);
> + fw_dump.reserve_dump_area_size = cma_get_size(fadump_cma);
> + printk("Reserved %ldMB cma area at %ldMB for firmware-assisted dump "
> + "(System RAM: %ldMB)\n",
> + cma_get_size(fadump_cma) >> 20,
> + (unsigned long)cma_get_base(fadump_cma) >> 20,
> + (unsigned long)(memblock_phys_mem_size() >> 20));
> + return 1;
> +}
> +
> /* Scan the Firmware Assisted dump configuration details. */
> int __init early_init_dt_scan_fw_dump(unsigned long node,
> const char *uname, int depth, void *data)
> @@ -496,8 +543,9 @@ int __init fadump_reserve_mem(void)
> pr_info("Number of kernel Dump sections: %d\n",
> be16_to_cpu(fdm_active->header.dump_num_sections));
> fw_dump.fadumphdr_addr = get_fadump_metadata_base(fdm_active);
> - pr_debug("fadumphdr_addr = %p\n",
> - (void *) fw_dump.fadumphdr_addr);
> + pr_debug("fadumphdr_addr = %pa\n", &fw_dump.fadumphdr_addr);
> + fw_dump.reserve_dump_area_start = base;
> + fw_dump.reserve_dump_area_size = size;
> } else {
> size = get_fadump_area_size();
>
> @@ -514,21 +562,10 @@ int __init fadump_reserve_mem(void)
> !memblock_is_region_reserved(base, size))
> break;
> }
> - if ((base > (memory_boundary - size)) ||
> - memblock_reserve(base, size)) {
> - pr_err("Failed to reserve memory\n");
> - return 0;
> - }
> -
> - pr_info("Reserved %ldMB of memory at %ldMB for firmware-"
> - "assisted dump (System RAM: %ldMB)\n",
> - (unsigned long)(size >> 20),
> - (unsigned long)(base >> 20),
> - (unsigned long)(memblock_phys_mem_size() >> 20));
> + fw_dump.reserve_dump_area_start = base;
> + fw_dump.reserve_dump_area_size = size;
> + return fadump_cma_reserve();
> }
> -
> - fw_dump.reserve_dump_area_start = base;
> - fw_dump.reserve_dump_area_size = size;
> return 1;
> }
>
> @@ -1191,6 +1228,39 @@ static unsigned long init_fadump_header(unsigned long addr)
> return addr;
> }
>
> +static unsigned long allocate_metadata_area(void)
> +{
> + int nr_pages;
> + unsigned long size;
> + struct page *page = NULL;
> +
> + /*
> + * Check if fadump cma region is activated.
> + * fadump_cma->count == 0 means cma activation has failed. This means
> + * that the fadump reserved memory now will not be visible/available
> + * for user applications to use. It will be as good as old fadump
> + * behaviour of blocking this memory chunk from production system
> + * use. CMA activation failure does not mean that fadump will not
> + * work. Will continue to setup fadump.
> + */
> + if (!fadump_cma || !cma_get_size(fadump_cma)) {
> + pr_warn("fadump cma region activation failed.\n");
> + return 0;
> + }
> +
> + size = get_fadump_metadata_size();
> + nr_pages = ALIGN(size, PAGE_SIZE) >> PAGE_SHIFT;
> + pr_info("Fadump metadata size = %ld (nr_pages = %d)\n", size, nr_pages);
> +
> + page = cma_alloc(fadump_cma, nr_pages, 0, GFP_KERNEL);
> + if (page) {
> + pr_debug("Allocated fadump metadata area at %ldMB (cma)\n",
> + (unsigned long)page_to_phys(page) >> 20);
> + return page_to_phys(page);
> + }
> + return 0;
> +}
> +
We shouldn't be needing this function with the above mentioned change..
Thanks
Hari
^ permalink raw reply
* Re: [PATCH] powerpc/mce: Fix a bug where mce loops on memory UE.
From: Balbir Singh @ 2018-04-23 11:14 UTC (permalink / raw)
To: Mahesh Jagannath Salgaonkar; +Cc: linuxppc-dev
In-Reply-To: <cb831682-8cc6-4cd7-9778-52e5f4ad5af0@linux.vnet.ibm.com>
On Mon, Apr 23, 2018 at 8:33 PM, Mahesh Jagannath Salgaonkar
<mahesh@linux.vnet.ibm.com> wrote:
> On 04/23/2018 12:21 PM, Balbir Singh wrote:
>> On Mon, Apr 23, 2018 at 2:59 PM, Mahesh J Salgaonkar
>> <mahesh@linux.vnet.ibm.com> wrote:
>>> From: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
>>>
>>> The current code extracts the physical address for UE errors and then
>>> hooks it up into memory failure infrastructure. On successful extraction
>>> of physical address it wrongly sets "handled = 1" which means this UE error
>>> has been recovered. Since MCE handler gets return value as handled = 1, it
>>> assumes that error has been recovered and goes back to same NIP. This causes
>>> MCE interrupt again and again in a loop leading to hard lockup.
>>>
>>> Also, initialize phys_addr to ULONG_MAX so that we don't end up queuing
>>> undesired page to hwpoison.
>>>
>>> Without this patch we see:
>>> [ 1476.541984] Severe Machine check interrupt [Recovered]
>>> [ 1476.541985] NIP: [000000001002588c] PID: 7109 Comm: find
>>> [ 1476.541986] Initiator: CPU
>>> [ 1476.541987] Error type: UE [Load/Store]
>>> [ 1476.541988] Effective address: 00007fffd2755940
>>> [ 1476.541989] Physical address: 000020181a080000
>>> [...]
>>> [ 1476.542003] Severe Machine check interrupt [Recovered]
>>> [ 1476.542004] NIP: [000000001002588c] PID: 7109 Comm: find
>>> [ 1476.542005] Initiator: CPU
>>> [ 1476.542006] Error type: UE [Load/Store]
>>> [ 1476.542006] Effective address: 00007fffd2755940
>>> [ 1476.542007] Physical address: 000020181a080000
>>> [ 1476.542010] Severe Machine check interrupt [Recovered]
>>> [ 1476.542012] NIP: [000000001002588c] PID: 7109 Comm: find
>>> [ 1476.542013] Initiator: CPU
>>> [ 1476.542014] Error type: UE [Load/Store]
>>> [ 1476.542015] Effective address: 00007fffd2755940
>>> [ 1476.542016] Physical address: 000020181a080000
>>> [ 1476.542448] Memory failure: 0x20181a08: recovery action for dirty LRU page: Recovered
>>> [ 1476.542452] Memory failure: 0x20181a08: already hardware poisoned
>>> [ 1476.542453] Memory failure: 0x20181a08: already hardware poisoned
>>> [ 1476.542454] Memory failure: 0x20181a08: already hardware poisoned
>>> [ 1476.542455] Memory failure: 0x20181a08: already hardware poisoned
>>> [ 1476.542456] Memory failure: 0x20181a08: already hardware poisoned
>>> [ 1476.542457] Memory failure: 0x20181a08: already hardware poisoned
>>> [...]
>>> [ 1490.972174] Watchdog CPU:38 Hard LOCKUP
>>>
>>> After this patch we see:
>>>
>>> [ 325.384336] Severe Machine check interrupt [Not recovered]
>>
>> How did you test for this?
>
> By injecting cache SUE using L2 FIR register (0x1001080c).
>
>> If the error was recovered, shouldn't the
>> process have gotten
>> a SIGBUS and we should have prevented further access as a part of the handling
>> (memory_failure()). Do we just need a MF_MUST_KILL in the flags?
>
> We hook it up to memory_failure() through a work queue and by the time
> work queue kicks in, the application continues to restart and hit same
> NIP again and again. Every MCE again hooks the same address to memory
> failure work queue and throws multiple recovered MCE messages for same
> address. Once the memory_failure() hwpoisons the page, application gets
> SIGBUS and then we are fine.
>
That seems quite broken and not recovered is very confusing. So effectively
we can never recover from a MCE UE. I think we need a notion of delayed
recovery then? Where we do recover, but mark is as recovered with delays?
We might want to revisit our recovery process and see if the recovery requires
to turn the MMU on, but that is for later, I suppose.
> But in case of UE in kernel space, if early machine_check handler
> "machine_check_early()" returns as recovered then
> machine_check_handle_early() queues up the MCE event and continues from
> NIP assuming it is safe causing a MCE loop. So, for UE in kernel we end
> up in hard lockup.
>
Yeah for the kernel, we need to definitely cause a panic for now, I've got other
patches for things we need to do for pmem that would allow potential recovery.
Balbir Singh
^ permalink raw reply
* Re: [PATCH 4/4] powerpc: Allow LD_DEAD_CODE_DATA_ELIMINATION to be selected
From: Mathieu Malaterre @ 2018-04-23 11:01 UTC (permalink / raw)
To: christophe leroy
Cc: Nicholas Piggin, linux-arch, linux-kbuild, linuxppc-dev,
Nicolas Pitre
In-Reply-To: <738aa4d6-9313-f484-0510-e965f2881869@c-s.fr>
Christophe,
On Sat, Apr 21, 2018 at 9:16 AM, christophe leroy
<christophe.leroy@c-s.fr> wrote:
>
>
> Le 20/04/2018 =C3=A0 22:08, Mathieu Malaterre a =C3=A9crit :
>>
>> On Fri, Apr 20, 2018 at 12:41 PM, Nicholas Piggin <npiggin@gmail.com>
>> wrote:
>>>
>>> On Fri, 20 Apr 2018 12:00:49 +0200
>>> Mathieu Malaterre <malat@debian.org> wrote:
>>>
>>>> On Fri, Apr 20, 2018 at 9:34 AM, Nicholas Piggin <npiggin@gmail.com>
>>>> wrote:
>>>>>
>>>>> This requires further changes to linker script to KEEP some tables
>>>>> and wildcard compiler generated sections into the right place. This
>>>>> includes pp32 modifications from Christophe Leroy.
>>>>>
>>>>> When compiling powernv_defconfig with this option:
>>>>>
>>>>> text data bss dec filename
>>>>> 11827621 4810490 1341080 17979191 vmlinux
>>>>> 11752437 4598858 1338776 17690071 vmlinux.dcde
>>>>>
>>>>> Resulting kernel is almost 400kB smaller (and still boots).
>>>>>
>>>>> [ppc32 numbers here]
>>>>
>>>>
>>>> ^^^
>>>>
>>>> Do you want somebody else to provide those numbers ?
>>>
>>>
>>> If you have a booting kernel, yes some more numbers would be good.
>>
>>
>> I've used /boot/config-4.15.0-2-powerpc from my current debian
>> package. Rebuild master with and without option, boot ok, load/unload
>> module ok.
>>
>> $ size nick/vmlinux.with*
>> text data bss dec hex filename
>> 7386425 2364370 1425432 11176227 aa8923 nick/vmlinux.with
>> 7461457 2475122 1428064 11364643 ad6923 nick/vmlinux.without
>>
>> This is not clear why with option the size of kernel is slightly bigger:
>
>
> The file contains also debug symbols, which might differ.
> Only the LOAD part of the file is interesting, that's the part you get wh=
en
> doing ppc-linux-objcopy vmlinux vmlinux.bin -O binary
>
> You can see it with readelf -l vmlinux
Here is what I see:
$ diff -u <(readelf -l nick/vmlinux.with) <(readelf -l nick/vmlinux.without=
)
--- /proc/self/fd/11 2018-04-23 12:59:52.413101612 +0200
+++ /proc/self/fd/12 2018-04-23 12:59:52.417101567 +0200
@@ -5,9 +5,9 @@
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
- LOAD 0x010000 0xc0000000 0x00000000 0x950f34 0xaad018 RWE 0x10=
000
- NOTE 0x867640 0xc0857640 0x00857640 0x00024 0x00024 0x4
- NOTE 0x867664 0xc0857664 0x00857664 0x0000c 0x0000c 0x1
+ LOAD 0x010000 0xc0000000 0x00000000 0x97ef34 0xadba68 RWE 0x10=
000
+ NOTE 0x87f640 0xc086f640 0x0086f640 0x00024 0x00024 0x4
+ NOTE 0x87f664 0xc086f664 0x0086f664 0x0000c 0x0000c 0x1
Section to Segment mapping:
Segment Sections...
> Christophe
>
>>
>> $ du -sk nick/vmlinux.with*
>> 124488 nick/vmlinux.with
>> 124004 nick/vmlinux.without
>>
>>
>>> Thanks,
>>> Nick
>
>
> ---
> L'absence de virus dans ce courrier =C3=A9lectronique a =C3=A9t=C3=A9 v=
=C3=A9rifi=C3=A9e par le
> logiciel antivirus Avast.
> https://www.avast.com/antivirus
>
^ permalink raw reply
* Re: [PATCH] powerpc/mce: Fix a bug where mce loops on memory UE.
From: Mahesh Jagannath Salgaonkar @ 2018-04-23 10:33 UTC (permalink / raw)
To: Balbir Singh; +Cc: linuxppc-dev
In-Reply-To: <CAKTCnznTiS2kQThYrCALGGKb8DfAu-diYzrCbOzwO2vd7Cfw9Q@mail.gmail.com>
On 04/23/2018 12:21 PM, Balbir Singh wrote:
> On Mon, Apr 23, 2018 at 2:59 PM, Mahesh J Salgaonkar
> <mahesh@linux.vnet.ibm.com> wrote:
>> From: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
>>
>> The current code extracts the physical address for UE errors and then
>> hooks it up into memory failure infrastructure. On successful extraction
>> of physical address it wrongly sets "handled = 1" which means this UE error
>> has been recovered. Since MCE handler gets return value as handled = 1, it
>> assumes that error has been recovered and goes back to same NIP. This causes
>> MCE interrupt again and again in a loop leading to hard lockup.
>>
>> Also, initialize phys_addr to ULONG_MAX so that we don't end up queuing
>> undesired page to hwpoison.
>>
>> Without this patch we see:
>> [ 1476.541984] Severe Machine check interrupt [Recovered]
>> [ 1476.541985] NIP: [000000001002588c] PID: 7109 Comm: find
>> [ 1476.541986] Initiator: CPU
>> [ 1476.541987] Error type: UE [Load/Store]
>> [ 1476.541988] Effective address: 00007fffd2755940
>> [ 1476.541989] Physical address: 000020181a080000
>> [...]
>> [ 1476.542003] Severe Machine check interrupt [Recovered]
>> [ 1476.542004] NIP: [000000001002588c] PID: 7109 Comm: find
>> [ 1476.542005] Initiator: CPU
>> [ 1476.542006] Error type: UE [Load/Store]
>> [ 1476.542006] Effective address: 00007fffd2755940
>> [ 1476.542007] Physical address: 000020181a080000
>> [ 1476.542010] Severe Machine check interrupt [Recovered]
>> [ 1476.542012] NIP: [000000001002588c] PID: 7109 Comm: find
>> [ 1476.542013] Initiator: CPU
>> [ 1476.542014] Error type: UE [Load/Store]
>> [ 1476.542015] Effective address: 00007fffd2755940
>> [ 1476.542016] Physical address: 000020181a080000
>> [ 1476.542448] Memory failure: 0x20181a08: recovery action for dirty LRU page: Recovered
>> [ 1476.542452] Memory failure: 0x20181a08: already hardware poisoned
>> [ 1476.542453] Memory failure: 0x20181a08: already hardware poisoned
>> [ 1476.542454] Memory failure: 0x20181a08: already hardware poisoned
>> [ 1476.542455] Memory failure: 0x20181a08: already hardware poisoned
>> [ 1476.542456] Memory failure: 0x20181a08: already hardware poisoned
>> [ 1476.542457] Memory failure: 0x20181a08: already hardware poisoned
>> [...]
>> [ 1490.972174] Watchdog CPU:38 Hard LOCKUP
>>
>> After this patch we see:
>>
>> [ 325.384336] Severe Machine check interrupt [Not recovered]
>
> How did you test for this?
By injecting cache SUE using L2 FIR register (0x1001080c).
> If the error was recovered, shouldn't the
> process have gotten
> a SIGBUS and we should have prevented further access as a part of the handling
> (memory_failure()). Do we just need a MF_MUST_KILL in the flags?
We hook it up to memory_failure() through a work queue and by the time
work queue kicks in, the application continues to restart and hit same
NIP again and again. Every MCE again hooks the same address to memory
failure work queue and throws multiple recovered MCE messages for same
address. Once the memory_failure() hwpoisons the page, application gets
SIGBUS and then we are fine.
But in case of UE in kernel space, if early machine_check handler
"machine_check_early()" returns as recovered then
machine_check_handle_early() queues up the MCE event and continues from
NIP assuming it is safe causing a MCE loop. So, for UE in kernel we end
up in hard lockup.
> Why shouldn't we treat it as handled if we isolate the page?
Yes we should, but I think not until the page is actually hwpoisioned OR
until we send SIGBUS to process.
>
> Thanks,
> Balbir Singh.
>
^ permalink raw reply
* Re: [PATCH RFC 1/1] KVM: PPC: Book3S HV: pack VCORE IDs to access full VCPU ID space
From: Cédric Le Goater @ 2018-04-23 9:06 UTC (permalink / raw)
To: David Gibson, Sam Bobroff; +Cc: linuxppc-dev, kvm, kvm-ppc, paulus
In-Reply-To: <20180416040942.GB20551@umbus.fritz.box>
On 04/16/2018 06:09 AM, David Gibson wrote:
> On Thu, Apr 12, 2018 at 05:02:06PM +1000, Sam Bobroff wrote:
>> It is not currently possible to create the full number of possible
>> VCPUs (KVM_MAX_VCPUS) on Power9 with KVM-HV when the guest uses less
>> threads per core than it's core stride (or "VSMT mode"). This is
>> because the VCORE ID and XIVE offsets to grow beyond KVM_MAX_VCPUS
>> even though the VCPU ID is less than KVM_MAX_VCPU_ID.
>>
>> To address this, "pack" the VCORE ID and XIVE offsets by using
>> knowledge of the way the VCPU IDs will be used when there are less
>> guest threads per core than the core stride. The primary thread of
>> each core will always be used first. Then, if the guest uses more than
>> one thread per core, these secondary threads will sequentially follow
>> the primary in each core.
>>
>> So, the only way an ID above KVM_MAX_VCPUS can be seen, is if the
>> VCPUs are being spaced apart, so at least half of each core is empty
>> and IDs between KVM_MAX_VCPUS and (KVM_MAX_VCPUS * 2) can be mapped
>> into the second half of each core (4..7, in an 8-thread core).
>>
>> Similarly, if IDs above KVM_MAX_VCPUS * 2 are seen, at least 3/4 of
>> each core is being left empty, and we can map down into the second and
>> third quarters of each core (2, 3 and 5, 6 in an 8-thread core).
>>
>> Lastly, if IDs above KVM_MAX_VCPUS * 4 are seen, only the primary
>> threads are being used and 7/8 of the core is empty, allowing use of
>> the 1, 3, 5 and 7 thread slots.
>>
>> (Strides less than 8 are handled similarly.)
>>
>> This allows the VCORE ID or offset to be calculated quickly from the
>> VCPU ID or XIVE server numbers, without access to the VCPU structure.
>>
>> Signed-off-by: Sam Bobroff <sam.bobroff@au1.ibm.com>
>> ---
>> Hello everyone,
>>
>> I've tested this on P8 and P9, in lots of combinations of host and guest
>> threading modes and it has been fine but it does feel like a "tricky"
>> approach, so I still feel somewhat wary about it.
Have you done any migration ?
>> I've posted it as an RFC because I have not tested it with guest native-XIVE,
>> and I suspect that it will take some work to support it.
The KVM XIVE device will be different for XIVE exploitation mode, same structures
though. I will send a patchset shortly.
>> arch/powerpc/include/asm/kvm_book3s.h | 19 +++++++++++++++++++
>> arch/powerpc/kvm/book3s_hv.c | 14 ++++++++++----
>> arch/powerpc/kvm/book3s_xive.c | 9 +++++++--
>> 3 files changed, 36 insertions(+), 6 deletions(-)
>>
>> diff --git a/arch/powerpc/include/asm/kvm_book3s.h b/arch/powerpc/include/asm/kvm_book3s.h
>> index 376ae803b69c..1295056d564a 100644
>> --- a/arch/powerpc/include/asm/kvm_book3s.h
>> +++ b/arch/powerpc/include/asm/kvm_book3s.h
>> @@ -368,4 +368,23 @@ extern int kvmppc_h_logical_ci_store(struct kvm_vcpu *vcpu);
>> #define SPLIT_HACK_MASK 0xff000000
>> #define SPLIT_HACK_OFFS 0xfb000000
>>
>> +/* Pack a VCPU ID from the [0..KVM_MAX_VCPU_ID) space down to the
>> + * [0..KVM_MAX_VCPUS) space, while using knowledge of the guest's core stride
>> + * (but not it's actual threading mode, which is not available) to avoid
>> + * collisions.
>> + */
>> +static inline u32 kvmppc_pack_vcpu_id(struct kvm *kvm, u32 id)
>> +{
>> + const int block_offsets[MAX_SMT_THREADS] = {0, 4, 2, 6, 1, 5, 3, 7};
>
> I'd suggest 1,3,5,7 at the end rather than 1,5,3,7 - accomplishes
> roughly the same thing, but I think makes the pattern more obvious.
>
>> + int stride = kvm->arch.emul_smt_mode > 1 ?
>> + kvm->arch.emul_smt_mode : kvm->arch.smt_mode;
>
> AFAICT from BUG_ON()s etc. at the callsites, kvm->arch.smt_mode must
> always be 1 when this is called, so the conditional here doesn't seem
> useful.
>
>> + int block = (id / KVM_MAX_VCPUS) * (MAX_SMT_THREADS / stride);
>> + u32 packed_id;
>> +
>> + BUG_ON(block >= MAX_SMT_THREADS);
>> + packed_id = (id % KVM_MAX_VCPUS) + block_offsets[block];
>> + BUG_ON(packed_id >= KVM_MAX_VCPUS);
>> + return packed_id;
>> +}
>
> It took me a while to wrap my head around the packing function, but I
> think I got there in the end. It's pretty clever.
>
> One thing bothers me, though. This certainly packs things under
> KVM_MAX_VCPUS, but not necessarily under the actual number of vcpus.
> e.g. KVM_MAC_VCPUS==16, 8 vcpus total, stride 8, 2 vthreads/vcore (as
> qemu sees it), gives both unpacked IDs (0, 1, 8, 9, 16, 17, 24, 25)
> and packed ids of (0, 1, 8, 9, 4, 5, 12, 13) - leaving 2, 3, 6, 7
> etc. unused.
>
> So again, the question is what exactly are these remapped IDs useful
> for. If we're indexing into a bare array of structures of size
> KVM_MAX_VCPUS then we're *already* wasting a bunch of space by having
> more entries than vcpus. If we're indexing into something sparser,
> then why is the remapping worthwhile?
>
>
>
>> +
>> #endif /* __ASM_KVM_BOOK3S_H__ */
>> diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
>> index 9cb9448163c4..49165cc90051 100644
>> --- a/arch/powerpc/kvm/book3s_hv.c
>> +++ b/arch/powerpc/kvm/book3s_hv.c
>> @@ -1762,7 +1762,7 @@ static int threads_per_vcore(struct kvm *kvm)
>> return threads_per_subcore;
>> }
>>
>> -static struct kvmppc_vcore *kvmppc_vcore_create(struct kvm *kvm, int core)
>> +static struct kvmppc_vcore *kvmppc_vcore_create(struct kvm *kvm, int id)
>> {
>> struct kvmppc_vcore *vcore;
>>
>> @@ -1776,7 +1776,7 @@ static struct kvmppc_vcore *kvmppc_vcore_create(struct kvm *kvm, int core)
>> init_swait_queue_head(&vcore->wq);
>> vcore->preempt_tb = TB_NIL;
>> vcore->lpcr = kvm->arch.lpcr;
>> - vcore->first_vcpuid = core * kvm->arch.smt_mode;
>> + vcore->first_vcpuid = id;
>> vcore->kvm = kvm;
>> INIT_LIST_HEAD(&vcore->preempt_list);
>>
>> @@ -1992,12 +1992,18 @@ static struct kvm_vcpu *kvmppc_core_vcpu_create_hv(struct kvm *kvm,
>> mutex_lock(&kvm->lock);
>> vcore = NULL;
>> err = -EINVAL;
>> - core = id / kvm->arch.smt_mode;
>> + if (cpu_has_feature(CPU_FTR_ARCH_300)) {
>> + BUG_ON(kvm->arch.smt_mode != 1);
>> + core = kvmppc_pack_vcpu_id(kvm, id);
>> + } else {
>> + core = id / kvm->arch.smt_mode;
>> + }
>> if (core < KVM_MAX_VCORES) {
>> vcore = kvm->arch.vcores[core];
>> + BUG_ON(cpu_has_feature(CPU_FTR_ARCH_300) && vcore);
>> if (!vcore) {
>> err = -ENOMEM;
>> - vcore = kvmppc_vcore_create(kvm, core);
>> + vcore = kvmppc_vcore_create(kvm, id & ~(kvm->arch.smt_mode - 1));
>> kvm->arch.vcores[core] = vcore;
>> kvm->arch.online_vcores++;
>> }
>> diff --git a/arch/powerpc/kvm/book3s_xive.c b/arch/powerpc/kvm/book3s_xive.c
>> index f9818d7d3381..681dfe12a5f3 100644
>> --- a/arch/powerpc/kvm/book3s_xive.c
>> +++ b/arch/powerpc/kvm/book3s_xive.c
>> @@ -317,6 +317,11 @@ static int xive_select_target(struct kvm *kvm, u32 *server, u8 prio)
>> return -EBUSY;
>> }
>>
>> +static u32 xive_vp(struct kvmppc_xive *xive, u32 server)
>> +{
>> + return xive->vp_base + kvmppc_pack_vcpu_id(xive->kvm, server);
>> +}
>> +
>
> I'm finding the XIVE indexing really baffling. There are a bunch of
> other places where the code uses (xive->vp_base + NUMBER) directly.
This links the QEMU vCPU server NUMBER to a XIVE virtual processor number
in OPAL. So we need to check that all used NUMBERs are, first, consistent
and then, in the correct range.
> If those are host side references, I guess they don't need updates for
> this.
> But if that's the case, then how does indexing into the same array
> with both host and guest server numbers make sense?
yes. VPs are allocated with KVM_MAX_VCPUS :
xive->vp_base = xive_native_alloc_vp_block(KVM_MAX_VCPUS);
but
#define KVM_MAX_VCPU_ID (threads_per_subcore * KVM_MAX_VCORES)
WE would need to change the allocation of the VPs I guess.
>> static u8 xive_lock_and_mask(struct kvmppc_xive *xive,
>> struct kvmppc_xive_src_block *sb,
>> struct kvmppc_xive_irq_state *state)
>> @@ -1084,7 +1089,7 @@ int kvmppc_xive_connect_vcpu(struct kvm_device *dev,
>> pr_devel("Duplicate !\n");
>> return -EEXIST;
>> }
>> - if (cpu >= KVM_MAX_VCPUS) {
>> + if (cpu >= KVM_MAX_VCPU_ID) {>>
>> pr_devel("Out of bounds !\n");
>> return -EINVAL;
>> }
>> @@ -1098,7 +1103,7 @@ int kvmppc_xive_connect_vcpu(struct kvm_device *dev,
>> xc->xive = xive;
>> xc->vcpu = vcpu;
>> xc->server_num = cpu;
>> - xc->vp_id = xive->vp_base + cpu;
>> + xc->vp_id = xive_vp(xive, cpu);
>> xc->mfrr = 0xff;
>> xc->valid = true;
>>
>
^ permalink raw reply
* Re: [PATCH] powerpc/mce: Fix a bug where mce loops on memory UE.
From: Balbir Singh @ 2018-04-23 9:23 UTC (permalink / raw)
To: Mahesh J Salgaonkar; +Cc: linuxppc-dev
In-Reply-To: <CAKTCnznTiS2kQThYrCALGGKb8DfAu-diYzrCbOzwO2vd7Cfw9Q@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 2917 bytes --]
On Mon, Apr 23, 2018 at 4:51 PM, Balbir Singh <bsingharora@gmail.com> wrote:
> On Mon, Apr 23, 2018 at 2:59 PM, Mahesh J Salgaonkar
> <mahesh@linux.vnet.ibm.com> wrote:
>> From: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
>>
>> The current code extracts the physical address for UE errors and then
>> hooks it up into memory failure infrastructure. On successful extraction
>> of physical address it wrongly sets "handled = 1" which means this UE error
>> has been recovered. Since MCE handler gets return value as handled = 1, it
>> assumes that error has been recovered and goes back to same NIP. This causes
>> MCE interrupt again and again in a loop leading to hard lockup.
>>
>> Also, initialize phys_addr to ULONG_MAX so that we don't end up queuing
>> undesired page to hwpoison.
>>
>> Without this patch we see:
>> [ 1476.541984] Severe Machine check interrupt [Recovered]
>> [ 1476.541985] NIP: [000000001002588c] PID: 7109 Comm: find
>> [ 1476.541986] Initiator: CPU
>> [ 1476.541987] Error type: UE [Load/Store]
>> [ 1476.541988] Effective address: 00007fffd2755940
>> [ 1476.541989] Physical address: 000020181a080000
>> [...]
>> [ 1476.542003] Severe Machine check interrupt [Recovered]
>> [ 1476.542004] NIP: [000000001002588c] PID: 7109 Comm: find
>> [ 1476.542005] Initiator: CPU
>> [ 1476.542006] Error type: UE [Load/Store]
>> [ 1476.542006] Effective address: 00007fffd2755940
>> [ 1476.542007] Physical address: 000020181a080000
>> [ 1476.542010] Severe Machine check interrupt [Recovered]
>> [ 1476.542012] NIP: [000000001002588c] PID: 7109 Comm: find
>> [ 1476.542013] Initiator: CPU
>> [ 1476.542014] Error type: UE [Load/Store]
>> [ 1476.542015] Effective address: 00007fffd2755940
>> [ 1476.542016] Physical address: 000020181a080000
>> [ 1476.542448] Memory failure: 0x20181a08: recovery action for dirty LRU page: Recovered
>> [ 1476.542452] Memory failure: 0x20181a08: already hardware poisoned
>> [ 1476.542453] Memory failure: 0x20181a08: already hardware poisoned
>> [ 1476.542454] Memory failure: 0x20181a08: already hardware poisoned
>> [ 1476.542455] Memory failure: 0x20181a08: already hardware poisoned
>> [ 1476.542456] Memory failure: 0x20181a08: already hardware poisoned
>> [ 1476.542457] Memory failure: 0x20181a08: already hardware poisoned
>> [...]
>> [ 1490.972174] Watchdog CPU:38 Hard LOCKUP
>>
>> After this patch we see:
>>
>> [ 325.384336] Severe Machine check interrupt [Not recovered]
>
> How did you test for this? If the error was recovered, shouldn't the
> process have gotten
> a SIGBUS and we should have prevented further access as a part of the handling
> (memory_failure()). Do we just need a MF_MUST_KILL in the flags?
>
> Why shouldn't we treat it as handled if we isolate the page?
Not yet signed-off-by patch attached for testing, Mahesh, please check/confirm
Thanks,
Balbir
>
> Thanks,
> Balbir Singh.
[-- Attachment #2: 0001-powerpc-mce-force-a-KILL-on-user-page-MCE.patch --]
[-- Type: text/x-patch, Size: 977 bytes --]
From b297f2ad8473eea7755bcc239e7de21227438065 Mon Sep 17 00:00:00 2001
From: Balbir Singh <bsingharora@gmail.com>
Date: Mon, 23 Apr 2018 19:21:27 +1000
Subject: [PATCH] powerpc/mce: force a KILL on user page MCE
Experimental fix for MCE error, force a kill
because that's our current recovery mechanism
Not tested/compiled yet
Signed-off-by: Balbir Singh <bsingharora@gmail.com>
---
arch/powerpc/kernel/mce.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/powerpc/kernel/mce.c b/arch/powerpc/kernel/mce.c
index efdd16a79075..a8441f75861e 100644
--- a/arch/powerpc/kernel/mce.c
+++ b/arch/powerpc/kernel/mce.c
@@ -273,7 +273,7 @@ static void machine_process_ue_event(struct work_struct *work)
pfn = evt->u.ue_error.physical_address >>
PAGE_SHIFT;
- memory_failure(pfn, 0);
+ memory_failure(pfn, MF_MUST_KILL);
} else
pr_warn("Failed to identify bad address from "
"where the uncorrectable error (UE) "
--
2.17.0
^ permalink raw reply related
* [PATCH 5/5] powerpc: remove unused to_tm() helper
From: Arnd Bergmann @ 2018-04-23 8:36 UTC (permalink / raw)
To: Michael Ellerman
Cc: Paul Mackerras, Benjamin Herrenschmidt, linuxppc-dev,
linux-kernel, y2038, arnd
In-Reply-To: <20180423083642.2608886-1-arnd@arndb.de>
to_tm() is now completely unused, the only reference being in the
_dump_time() helper that is also unused. This removes both, leaving
the rest of the powerpc RTC code y2038 safe to as far as the hardware
supports.
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
arch/powerpc/include/asm/time.h | 2 --
arch/powerpc/kernel/time.c | 50 ---------------------------------------
arch/powerpc/platforms/ps3/time.c | 24 -------------------
3 files changed, 76 deletions(-)
diff --git a/arch/powerpc/include/asm/time.h b/arch/powerpc/include/asm/time.h
index db546c034905..79bc9c3e4325 100644
--- a/arch/powerpc/include/asm/time.h
+++ b/arch/powerpc/include/asm/time.h
@@ -26,8 +26,6 @@ extern unsigned long tb_ticks_per_usec;
extern unsigned long tb_ticks_per_sec;
extern struct clock_event_device decrementer_clockevent;
-struct rtc_time;
-extern void to_tm(int tim, struct rtc_time * tm);
extern void tick_broadcast_ipi_handler(void);
extern void generic_calibrate_decr(void);
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
index f9b0baa3fa2b..79bdeea85ab4 100644
--- a/arch/powerpc/kernel/time.c
+++ b/arch/powerpc/kernel/time.c
@@ -1138,56 +1138,6 @@ void __init time_init(void)
#endif
}
-
-#define FEBRUARY 2
-#define STARTOFTIME 1970
-#define SECDAY 86400L
-#define SECYR (SECDAY * 365)
-#define leapyear(year) ((year) % 4 == 0 && \
- ((year) % 100 != 0 || (year) % 400 == 0))
-#define days_in_year(a) (leapyear(a) ? 366 : 365)
-#define days_in_month(a) (month_days[(a) - 1])
-
-static int month_days[12] = {
- 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
-};
-
-void to_tm(int tim, struct rtc_time * tm)
-{
- register int i;
- register long hms, day;
-
- day = tim / SECDAY;
- hms = tim % SECDAY;
-
- /* Hours, minutes, seconds are easy */
- tm->tm_hour = hms / 3600;
- tm->tm_min = (hms % 3600) / 60;
- tm->tm_sec = (hms % 3600) % 60;
-
- /* Number of years in days */
- for (i = STARTOFTIME; day >= days_in_year(i); i++)
- day -= days_in_year(i);
- tm->tm_year = i;
-
- /* Number of months in days left */
- if (leapyear(tm->tm_year))
- days_in_month(FEBRUARY) = 29;
- for (i = 1; day >= days_in_month(i); i++)
- day -= days_in_month(i);
- days_in_month(FEBRUARY) = 28;
- tm->tm_mon = i;
-
- /* Days are what is left over (+1) from all that. */
- tm->tm_mday = day + 1;
-
- /*
- * No-one uses the day of the week.
- */
- tm->tm_wday = -1;
-}
-EXPORT_SYMBOL(to_tm);
-
/*
* Divide a 128-bit dividend by a 32-bit divisor, leaving a 128 bit
* result.
diff --git a/arch/powerpc/platforms/ps3/time.c b/arch/powerpc/platforms/ps3/time.c
index 9dac125c997e..08ca76e23d09 100644
--- a/arch/powerpc/platforms/ps3/time.c
+++ b/arch/powerpc/platforms/ps3/time.c
@@ -28,30 +28,6 @@
#include "platform.h"
-#define dump_tm(_a) _dump_tm(_a, __func__, __LINE__)
-static void _dump_tm(const struct rtc_time *tm, const char* func, int line)
-{
- pr_debug("%s:%d tm_sec %d\n", func, line, tm->tm_sec);
- pr_debug("%s:%d tm_min %d\n", func, line, tm->tm_min);
- pr_debug("%s:%d tm_hour %d\n", func, line, tm->tm_hour);
- pr_debug("%s:%d tm_mday %d\n", func, line, tm->tm_mday);
- pr_debug("%s:%d tm_mon %d\n", func, line, tm->tm_mon);
- pr_debug("%s:%d tm_year %d\n", func, line, tm->tm_year);
- pr_debug("%s:%d tm_wday %d\n", func, line, tm->tm_wday);
-}
-
-#define dump_time(_a) _dump_time(_a, __func__, __LINE__)
-static void __maybe_unused _dump_time(int time, const char *func,
- int line)
-{
- struct rtc_time tm;
-
- to_tm(time, &tm);
-
- pr_debug("%s:%d time %d\n", func, line, time);
- _dump_tm(&tm, func, line);
-}
-
void __init ps3_calibrate_decr(void)
{
int result;
--
2.9.0
^ permalink raw reply related
* [PATCH 1/5] powerpc: always enable RTC_LIB
From: Arnd Bergmann @ 2018-04-23 8:36 UTC (permalink / raw)
To: Michael Ellerman
Cc: Paul Mackerras, Benjamin Herrenschmidt, linuxppc-dev,
linux-kernel, y2038, arnd
In order to use the rtc_tm_to_time64() and rtc_time64_to_tm()
helper functions in later patches, we have to ensure that
CONFIG_RTC_LIB is always built-in.
Note that this symbol only controls a couple of helper functions,
not the actual RTC subsystem, which remains optional and is
enabled with CONFIG_RTC_CLASS.
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
arch/powerpc/Kconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index c32a181a7cbb..de2cda320fdd 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -232,6 +232,7 @@ config PPC
select OF_RESERVED_MEM
select OLD_SIGACTION if PPC32
select OLD_SIGSUSPEND
+ select RTC_LIB
select SPARSE_IRQ
select SYSCTL_EXCEPTION_TRACE
select VIRT_TO_BUS if !PPC64
--
2.9.0
^ permalink raw reply related
* [PATCH 3/5] powerpc: use time64_t in read_persistent_clock
From: Arnd Bergmann @ 2018-04-23 8:36 UTC (permalink / raw)
To: Michael Ellerman
Cc: Paul Mackerras, Benjamin Herrenschmidt, linuxppc-dev,
linux-kernel, y2038, arnd
In-Reply-To: <20180423083642.2608886-1-arnd@arndb.de>
Looking through the remaining users of the deprecated mktime()
function, I found the powerpc rtc handlers, which use it in
place of rtc_tm_to_time64().
To clean this up, I'm changing over the read_persistent_clock()
function to the read_persistent_clock64() variant, and change
all the platform specific handlers along with it.
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
v2: fix a typo: rtc_tm_to_time66 -> rtc_tm_to_time64
---
arch/powerpc/include/asm/machdep.h | 2 +-
arch/powerpc/include/asm/opal.h | 2 +-
arch/powerpc/include/asm/rtas.h | 2 +-
arch/powerpc/kernel/rtas-rtc.c | 4 ++--
arch/powerpc/kernel/time.c | 7 +++----
arch/powerpc/platforms/8xx/m8xx_setup.c | 4 +---
arch/powerpc/platforms/maple/maple.h | 2 +-
arch/powerpc/platforms/maple/time.c | 5 ++---
arch/powerpc/platforms/pasemi/pasemi.h | 2 +-
arch/powerpc/platforms/pasemi/time.c | 4 ++--
arch/powerpc/platforms/powermac/pmac.h | 2 +-
arch/powerpc/platforms/powermac/time.c | 31 +++++++++++--------------------
arch/powerpc/platforms/powernv/opal-rtc.c | 5 ++---
arch/powerpc/platforms/ps3/platform.h | 2 +-
arch/powerpc/platforms/ps3/time.c | 2 +-
15 files changed, 31 insertions(+), 45 deletions(-)
diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h
index ffe7c71e1132..a47de82fb8e2 100644
--- a/arch/powerpc/include/asm/machdep.h
+++ b/arch/powerpc/include/asm/machdep.h
@@ -83,7 +83,7 @@ struct machdep_calls {
int (*set_rtc_time)(struct rtc_time *);
void (*get_rtc_time)(struct rtc_time *);
- unsigned long (*get_boot_time)(void);
+ time64_t (*get_boot_time)(void);
unsigned char (*rtc_read_val)(int addr);
void (*rtc_write_val)(int addr, unsigned char val);
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index 03e1a920491e..fc211bd98e0f 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -325,7 +325,7 @@ extern int opal_async_wait_response_interruptible(uint64_t token,
extern int opal_get_sensor_data(u32 sensor_hndl, u32 *sensor_data);
struct rtc_time;
-extern unsigned long opal_get_boot_time(void);
+extern time64_t opal_get_boot_time(void);
extern void opal_nvram_init(void);
extern void opal_flash_update_init(void);
extern void opal_flash_update_print_message(void);
diff --git a/arch/powerpc/include/asm/rtas.h b/arch/powerpc/include/asm/rtas.h
index ec9dd79398ee..71e393c46a49 100644
--- a/arch/powerpc/include/asm/rtas.h
+++ b/arch/powerpc/include/asm/rtas.h
@@ -361,7 +361,7 @@ extern int rtas_offline_cpus_mask(cpumask_var_t cpus);
extern int rtas_ibm_suspend_me(u64 handle);
struct rtc_time;
-extern unsigned long rtas_get_boot_time(void);
+extern time64_t rtas_get_boot_time(void);
extern void rtas_get_rtc_time(struct rtc_time *rtc_time);
extern int rtas_set_rtc_time(struct rtc_time *rtc_time);
diff --git a/arch/powerpc/kernel/rtas-rtc.c b/arch/powerpc/kernel/rtas-rtc.c
index 49600985c7ef..a28239b8b0c0 100644
--- a/arch/powerpc/kernel/rtas-rtc.c
+++ b/arch/powerpc/kernel/rtas-rtc.c
@@ -13,7 +13,7 @@
#define MAX_RTC_WAIT 5000 /* 5 sec */
#define RTAS_CLOCK_BUSY (-2)
-unsigned long __init rtas_get_boot_time(void)
+time64_t __init rtas_get_boot_time(void)
{
int ret[8];
int error;
@@ -38,7 +38,7 @@ unsigned long __init rtas_get_boot_time(void)
return 0;
}
- return mktime(ret[0], ret[1], ret[2], ret[3], ret[4], ret[5]);
+ return mktime64(ret[0], ret[1], ret[2], ret[3], ret[4], ret[5]);
}
/* NOTE: get_rtc_time will get an error if executed in interrupt context
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
index 360e71d455cc..afb27962b396 100644
--- a/arch/powerpc/kernel/time.c
+++ b/arch/powerpc/kernel/time.c
@@ -795,7 +795,7 @@ int update_persistent_clock(struct timespec now)
return ppc_md.set_rtc_time(&tm);
}
-static void __read_persistent_clock(struct timespec *ts)
+static void __read_persistent_clock(struct timespec64 *ts)
{
struct rtc_time tm;
static int first = 1;
@@ -819,11 +819,10 @@ static void __read_persistent_clock(struct timespec *ts)
}
ppc_md.get_rtc_time(&tm);
- ts->tv_sec = mktime(tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
- tm.tm_hour, tm.tm_min, tm.tm_sec);
+ ts->tv_sec = rtc_tm_to_time64(&tm);
}
-void read_persistent_clock(struct timespec *ts)
+void read_persistent_clock64(struct timespec64 *ts)
{
__read_persistent_clock(ts);
diff --git a/arch/powerpc/platforms/8xx/m8xx_setup.c b/arch/powerpc/platforms/8xx/m8xx_setup.c
index 2188d691a40f..d76daa90647b 100644
--- a/arch/powerpc/platforms/8xx/m8xx_setup.c
+++ b/arch/powerpc/platforms/8xx/m8xx_setup.c
@@ -192,9 +192,7 @@ void mpc8xx_get_rtc_time(struct rtc_time *tm)
/* Get time from the RTC. */
data = in_be32(&sys_tmr->sit_rtc);
- to_tm(data, tm);
- tm->tm_year -= 1900;
- tm->tm_mon -= 1;
+ rtc_time64_to_tm(data, tm);
immr_unmap(sys_tmr);
return;
}
diff --git a/arch/powerpc/platforms/maple/maple.h b/arch/powerpc/platforms/maple/maple.h
index d10f4af3a42e..4f358b55c341 100644
--- a/arch/powerpc/platforms/maple/maple.h
+++ b/arch/powerpc/platforms/maple/maple.h
@@ -6,7 +6,7 @@
*/
extern int maple_set_rtc_time(struct rtc_time *tm);
extern void maple_get_rtc_time(struct rtc_time *tm);
-extern unsigned long maple_get_boot_time(void);
+extern time64_t maple_get_boot_time(void);
extern void maple_calibrate_decr(void);
extern void maple_pci_init(void);
extern void maple_pci_irq_fixup(struct pci_dev *dev);
diff --git a/arch/powerpc/platforms/maple/time.c b/arch/powerpc/platforms/maple/time.c
index cfddc87f81bf..becf2ebf7df5 100644
--- a/arch/powerpc/platforms/maple/time.c
+++ b/arch/powerpc/platforms/maple/time.c
@@ -137,7 +137,7 @@ static struct resource rtc_iores = {
.flags = IORESOURCE_IO | IORESOURCE_BUSY,
};
-unsigned long __init maple_get_boot_time(void)
+time64_t __init maple_get_boot_time(void)
{
struct rtc_time tm;
struct device_node *rtcs;
@@ -170,7 +170,6 @@ unsigned long __init maple_get_boot_time(void)
request_resource(&ioport_resource, &rtc_iores);
maple_get_rtc_time(&tm);
- return mktime(tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
- tm.tm_hour, tm.tm_min, tm.tm_sec);
+ return rtc_tm_to_time64(&tm);
}
diff --git a/arch/powerpc/platforms/pasemi/pasemi.h b/arch/powerpc/platforms/pasemi/pasemi.h
index 329d2a619254..70b56048ed1b 100644
--- a/arch/powerpc/platforms/pasemi/pasemi.h
+++ b/arch/powerpc/platforms/pasemi/pasemi.h
@@ -2,7 +2,7 @@
#ifndef _PASEMI_PASEMI_H
#define _PASEMI_PASEMI_H
-extern unsigned long pas_get_boot_time(void);
+extern time64_t pas_get_boot_time(void);
extern void pas_pci_init(void);
extern void pas_pci_irq_fixup(struct pci_dev *dev);
extern void pas_pci_dma_dev_setup(struct pci_dev *dev);
diff --git a/arch/powerpc/platforms/pasemi/time.c b/arch/powerpc/platforms/pasemi/time.c
index fa54351ac268..ea815254ee7b 100644
--- a/arch/powerpc/platforms/pasemi/time.c
+++ b/arch/powerpc/platforms/pasemi/time.c
@@ -21,8 +21,8 @@
#include <asm/time.h>
-unsigned long __init pas_get_boot_time(void)
+time64_t __init pas_get_boot_time(void)
{
/* Let's just return a fake date right now */
- return mktime(2006, 1, 1, 12, 0, 0);
+ return mktime64(2006, 1, 1, 12, 0, 0);
}
diff --git a/arch/powerpc/platforms/powermac/pmac.h b/arch/powerpc/platforms/powermac/pmac.h
index 6f15b8804e9b..16a52afdb76e 100644
--- a/arch/powerpc/platforms/powermac/pmac.h
+++ b/arch/powerpc/platforms/powermac/pmac.h
@@ -15,7 +15,7 @@ struct rtc_time;
extern int pmac_newworld;
extern long pmac_time_init(void);
-extern unsigned long pmac_get_boot_time(void);
+extern time64_t pmac_get_boot_time(void);
extern void pmac_get_rtc_time(struct rtc_time *);
extern int pmac_set_rtc_time(struct rtc_time *);
extern void pmac_read_rtc_time(void);
diff --git a/arch/powerpc/platforms/powermac/time.c b/arch/powerpc/platforms/powermac/time.c
index 274af6fa388e..d5d1c452038e 100644
--- a/arch/powerpc/platforms/powermac/time.c
+++ b/arch/powerpc/platforms/powermac/time.c
@@ -84,15 +84,6 @@ long __init pmac_time_init(void)
return delta;
}
-#if defined(CONFIG_ADB_CUDA) || defined(CONFIG_ADB_PMU)
-static void to_rtc_time(unsigned long now, struct rtc_time *tm)
-{
- to_tm(now, tm);
- tm->tm_year -= 1900;
- tm->tm_mon -= 1;
-}
-#endif
-
#if defined(CONFIG_ADB_CUDA) || defined(CONFIG_ADB_PMU) || \
defined(CONFIG_PMAC_SMU)
static unsigned long from_rtc_time(struct rtc_time *tm)
@@ -103,10 +94,10 @@ static unsigned long from_rtc_time(struct rtc_time *tm)
#endif
#ifdef CONFIG_ADB_CUDA
-static unsigned long cuda_get_time(void)
+static time64_t cuda_get_time(void)
{
struct adb_request req;
- unsigned int now;
+ time64_t now;
if (cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_GET_TIME) < 0)
return 0;
@@ -117,10 +108,10 @@ static unsigned long cuda_get_time(void)
req.reply_len);
now = (req.reply[3] << 24) + (req.reply[4] << 16)
+ (req.reply[5] << 8) + req.reply[6];
- return ((unsigned long)now) - RTC_OFFSET;
+ return now - RTC_OFFSET;
}
-#define cuda_get_rtc_time(tm) to_rtc_time(cuda_get_time(), (tm))
+#define cuda_get_rtc_time(tm) rtc_time64_to_tm(cuda_get_time(), (tm))
static int cuda_set_rtc_time(struct rtc_time *tm)
{
@@ -147,10 +138,10 @@ static int cuda_set_rtc_time(struct rtc_time *tm)
#endif
#ifdef CONFIG_ADB_PMU
-static unsigned long pmu_get_time(void)
+static time64_t pmu_get_time(void)
{
struct adb_request req;
- unsigned int now;
+ time64_t now;
if (pmu_request(&req, NULL, 1, PMU_READ_RTC) < 0)
return 0;
@@ -160,10 +151,10 @@ static unsigned long pmu_get_time(void)
req.reply_len);
now = (req.reply[0] << 24) + (req.reply[1] << 16)
+ (req.reply[2] << 8) + req.reply[3];
- return ((unsigned long)now) - RTC_OFFSET;
+ return now - RTC_OFFSET;
}
-#define pmu_get_rtc_time(tm) to_rtc_time(pmu_get_time(), (tm))
+#define pmu_get_rtc_time(tm) rtc_time64_to_tm(pmu_get_time(), (tm))
static int pmu_set_rtc_time(struct rtc_time *tm)
{
@@ -188,13 +179,13 @@ static int pmu_set_rtc_time(struct rtc_time *tm)
#endif
#ifdef CONFIG_PMAC_SMU
-static unsigned long smu_get_time(void)
+static time64_t smu_get_time(void)
{
struct rtc_time tm;
if (smu_get_rtc_time(&tm, 1))
return 0;
- return from_rtc_time(&tm);
+ return rtc_tm_to_time64(&tm);
}
#else
@@ -204,7 +195,7 @@ static unsigned long smu_get_time(void)
#endif
/* Can't be __init, it's called when suspending and resuming */
-unsigned long pmac_get_boot_time(void)
+time64_t pmac_get_boot_time(void)
{
/* Get the time from the RTC, used only at boot time */
switch (sys_ctrler) {
diff --git a/arch/powerpc/platforms/powernv/opal-rtc.c b/arch/powerpc/platforms/powernv/opal-rtc.c
index f8868864f373..f515c4a76900 100644
--- a/arch/powerpc/platforms/powernv/opal-rtc.c
+++ b/arch/powerpc/platforms/powernv/opal-rtc.c
@@ -34,7 +34,7 @@ static void opal_to_tm(u32 y_m_d, u64 h_m_s_ms, struct rtc_time *tm)
tm->tm_wday = -1;
}
-unsigned long __init opal_get_boot_time(void)
+time64_t __init opal_get_boot_time(void)
{
struct rtc_time tm;
u32 y_m_d;
@@ -59,8 +59,7 @@ unsigned long __init opal_get_boot_time(void)
y_m_d = be32_to_cpu(__y_m_d);
h_m_s_ms = be64_to_cpu(__h_m_s_ms);
opal_to_tm(y_m_d, h_m_s_ms, &tm);
- return mktime(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
- tm.tm_hour, tm.tm_min, tm.tm_sec);
+ return rtc_tm_to_time64(&tm);
}
static __init int opal_time_init(void)
diff --git a/arch/powerpc/platforms/ps3/platform.h b/arch/powerpc/platforms/ps3/platform.h
index 1809cfc562ee..9bc68f913466 100644
--- a/arch/powerpc/platforms/ps3/platform.h
+++ b/arch/powerpc/platforms/ps3/platform.h
@@ -57,7 +57,7 @@ static inline void ps3_smp_cleanup_cpu(int cpu) { }
/* time */
void __init ps3_calibrate_decr(void);
-unsigned long __init ps3_get_boot_time(void);
+time64_t __init ps3_get_boot_time(void);
void ps3_get_rtc_time(struct rtc_time *time);
int ps3_set_rtc_time(struct rtc_time *time);
diff --git a/arch/powerpc/platforms/ps3/time.c b/arch/powerpc/platforms/ps3/time.c
index 11b45b58c81b..9dac125c997e 100644
--- a/arch/powerpc/platforms/ps3/time.c
+++ b/arch/powerpc/platforms/ps3/time.c
@@ -76,7 +76,7 @@ static u64 read_rtc(void)
return rtc_val;
}
-unsigned long __init ps3_get_boot_time(void)
+time64_t __init ps3_get_boot_time(void)
{
return read_rtc() + ps3_os_area_get_rtc_diff();
}
--
2.9.0
^ permalink raw reply related
* [PATCH 2/5] powerpc: rtas: clean up time handling
From: Arnd Bergmann @ 2018-04-23 8:36 UTC (permalink / raw)
To: Michael Ellerman
Cc: Paul Mackerras, Benjamin Herrenschmidt, linuxppc-dev,
linux-kernel, y2038, arnd
In-Reply-To: <20180423083642.2608886-1-arnd@arndb.de>
The to_tm() helper function operates on a signed integer for the time,
so it will suffer from overflow in 2038, even on 64-bit kernels.
Rather than fix that function, this replaces its use in the rtas
procfs implementation with the standard rtc_time64_to_tm() helper
that is very similar but is not affected by the overflow.
In order to actually support long times, the parser function gets
changed to 64-bit user input and output as well. Note that the tm_mon
and tm_year representation is slightly different, so we have to manually
add an offset here.
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
v2: fix up rtc_time64_to_tm() calling conventions
---
arch/powerpc/kernel/rtas-proc.c | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/arch/powerpc/kernel/rtas-proc.c b/arch/powerpc/kernel/rtas-proc.c
index fb070d8cad07..815027476600 100644
--- a/arch/powerpc/kernel/rtas-proc.c
+++ b/arch/powerpc/kernel/rtas-proc.c
@@ -280,7 +280,7 @@ static int __init proc_rtas_init(void)
__initcall(proc_rtas_init);
-static int parse_number(const char __user *p, size_t count, unsigned long *val)
+static int parse_number(const char __user *p, size_t count, u64 *val)
{
char buf[40];
char *end;
@@ -293,7 +293,7 @@ static int parse_number(const char __user *p, size_t count, unsigned long *val)
buf[count] = 0;
- *val = simple_strtoul(buf, &end, 10);
+ *val = simple_strtoull(buf, &end, 10);
if (*end && *end != '\n')
return -EINVAL;
@@ -307,17 +307,17 @@ static ssize_t ppc_rtas_poweron_write(struct file *file,
const char __user *buf, size_t count, loff_t *ppos)
{
struct rtc_time tm;
- unsigned long nowtime;
+ time64_t nowtime;
int error = parse_number(buf, count, &nowtime);
if (error)
return error;
power_on_time = nowtime; /* save the time */
- to_tm(nowtime, &tm);
+ rtc_time64_to_tm(nowtime, &tm);
error = rtas_call(rtas_token("set-time-for-power-on"), 7, 1, NULL,
- tm.tm_year, tm.tm_mon, tm.tm_mday,
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, 0 /* nano */);
if (error)
printk(KERN_WARNING "error: setting poweron time returned: %s\n",
@@ -373,14 +373,14 @@ static ssize_t ppc_rtas_clock_write(struct file *file,
const char __user *buf, size_t count, loff_t *ppos)
{
struct rtc_time tm;
- unsigned long nowtime;
+ time64_t nowtime;
int error = parse_number(buf, count, &nowtime);
if (error)
return error;
- to_tm(nowtime, &tm);
+ rtc_time64_to_tm(nowtime, &tm);
error = rtas_call(rtas_token("set-time-of-day"), 7, 1, NULL,
- tm.tm_year, tm.tm_mon, tm.tm_mday,
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, 0);
if (error)
printk(KERN_WARNING "error: setting the clock returned: %s\n",
@@ -401,8 +401,8 @@ static int ppc_rtas_clock_show(struct seq_file *m, void *v)
unsigned int year, mon, day, hour, min, sec;
year = ret[0]; mon = ret[1]; day = ret[2];
hour = ret[3]; min = ret[4]; sec = ret[5];
- seq_printf(m, "%lu\n",
- mktime(year, mon, day, hour, min, sec));
+ seq_printf(m, "%lld\n",
+ mktime64(year, mon, day, hour, min, sec));
}
return 0;
}
@@ -731,7 +731,7 @@ static void get_location_code(struct seq_file *m, struct individual_sensor *s,
static ssize_t ppc_rtas_tone_freq_write(struct file *file,
const char __user *buf, size_t count, loff_t *ppos)
{
- unsigned long freq;
+ u64 freq;
int error = parse_number(buf, count, &freq);
if (error)
return error;
@@ -756,7 +756,7 @@ static int ppc_rtas_tone_freq_show(struct seq_file *m, void *v)
static ssize_t ppc_rtas_tone_volume_write(struct file *file,
const char __user *buf, size_t count, loff_t *ppos)
{
- unsigned long volume;
+ u64 volume;
int error = parse_number(buf, count, &volume);
if (error)
return error;
--
2.9.0
^ permalink raw reply related
* [PATCH 4/5] powerpc: use time64_t in update_persistent_clock
From: Arnd Bergmann @ 2018-04-23 8:36 UTC (permalink / raw)
To: Michael Ellerman
Cc: Paul Mackerras, Benjamin Herrenschmidt, linuxppc-dev,
linux-kernel, y2038, arnd
In-Reply-To: <20180423083642.2608886-1-arnd@arndb.de>
update_persistent_clock() is deprecated because it suffers from overflow
in 2038 on 32-bit architectures. This changes powerpc to use the
update_persistent_clock64() replacement, and to pass down 64-bit
timestamps consistently.
This is now simpler, as we no longer have to worry about the offset
numbers in tm_year and tm_mon that are different between the Linux
conventions and RTAS.
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
arch/powerpc/kernel/time.c | 6 ++----
arch/powerpc/platforms/8xx/m8xx_setup.c | 7 +++----
arch/powerpc/platforms/powermac/time.c | 17 ++++-------------
3 files changed, 9 insertions(+), 21 deletions(-)
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
index afb27962b396..f9b0baa3fa2b 100644
--- a/arch/powerpc/kernel/time.c
+++ b/arch/powerpc/kernel/time.c
@@ -781,16 +781,14 @@ void __init generic_calibrate_decr(void)
}
}
-int update_persistent_clock(struct timespec now)
+int update_persistent_clock64(struct timespec64 now)
{
struct rtc_time tm;
if (!ppc_md.set_rtc_time)
return -ENODEV;
- to_tm(now.tv_sec + 1 + timezone_offset, &tm);
- tm.tm_year -= 1900;
- tm.tm_mon -= 1;
+ rtc_time64_to_tm(now.tv_sec + 1 + timezone_offset, &tm);
return ppc_md.set_rtc_time(&tm);
}
diff --git a/arch/powerpc/platforms/8xx/m8xx_setup.c b/arch/powerpc/platforms/8xx/m8xx_setup.c
index d76daa90647b..027c42d8966c 100644
--- a/arch/powerpc/platforms/8xx/m8xx_setup.c
+++ b/arch/powerpc/platforms/8xx/m8xx_setup.c
@@ -169,15 +169,14 @@ int mpc8xx_set_rtc_time(struct rtc_time *tm)
{
sitk8xx_t __iomem *sys_tmr1;
sit8xx_t __iomem *sys_tmr2;
- int time;
+ time64_t time;
sys_tmr1 = immr_map(im_sitk);
sys_tmr2 = immr_map(im_sit);
- time = mktime(tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec);
+ time = rtc_tm_to_time64(tm);
out_be32(&sys_tmr1->sitk_rtck, KAPWR_KEY);
- out_be32(&sys_tmr2->sit_rtc, time);
+ out_be32(&sys_tmr2->sit_rtc, (u32)time);
out_be32(&sys_tmr1->sitk_rtck, ~KAPWR_KEY);
immr_unmap(sys_tmr2);
diff --git a/arch/powerpc/platforms/powermac/time.c b/arch/powerpc/platforms/powermac/time.c
index d5d1c452038e..7c968e46736f 100644
--- a/arch/powerpc/platforms/powermac/time.c
+++ b/arch/powerpc/platforms/powermac/time.c
@@ -84,15 +84,6 @@ long __init pmac_time_init(void)
return delta;
}
-#if defined(CONFIG_ADB_CUDA) || defined(CONFIG_ADB_PMU) || \
- defined(CONFIG_PMAC_SMU)
-static unsigned long from_rtc_time(struct rtc_time *tm)
-{
- return mktime(tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec);
-}
-#endif
-
#ifdef CONFIG_ADB_CUDA
static time64_t cuda_get_time(void)
{
@@ -115,10 +106,10 @@ static time64_t cuda_get_time(void)
static int cuda_set_rtc_time(struct rtc_time *tm)
{
- unsigned int nowtime;
+ time64_t nowtime;
struct adb_request req;
- nowtime = from_rtc_time(tm) + RTC_OFFSET;
+ nowtime = rtc_tm_to_time64(tm) + RTC_OFFSET;
if (cuda_request(&req, NULL, 6, CUDA_PACKET, CUDA_SET_TIME,
nowtime >> 24, nowtime >> 16, nowtime >> 8,
nowtime) < 0)
@@ -158,10 +149,10 @@ static time64_t pmu_get_time(void)
static int pmu_set_rtc_time(struct rtc_time *tm)
{
- unsigned int nowtime;
+ time64_t nowtime;
struct adb_request req;
- nowtime = from_rtc_time(tm) + RTC_OFFSET;
+ nowtime = rtc_tm_to_time64(tm) + RTC_OFFSET;
if (pmu_request(&req, NULL, 5, PMU_SET_RTC, nowtime >> 24,
nowtime >> 16, nowtime >> 8, nowtime) < 0)
return -ENXIO;
--
2.9.0
^ permalink raw reply related
* Re: [PATCH 2/5] powerpc: rtas: clean up time handling
From: Arnd Bergmann @ 2018-04-23 8:32 UTC (permalink / raw)
To: Michael Ellerman
Cc: Paul Mackerras, Benjamin Herrenschmidt, linuxppc-dev,
Arnd Bergmann, Linux Kernel Mailing List
In-Reply-To: <20180423081114.1813726-2-arnd@arndb.de>
On Mon, Apr 23, 2018 at 10:10 AM, Arnd Bergmann <arnd@arndb.de> wrote:
> @@ -307,17 +307,17 @@ static ssize_t ppc_rtas_poweron_write(struct file *file,
> const char __user *buf, size_t count, loff_t *ppos)
> {
> struct rtc_time tm;
> - unsigned long nowtime;
> + time64_t nowtime;
> int error = parse_number(buf, count, &nowtime);
> if (error)
> return error;
>
> power_on_time = nowtime; /* save the time */
>
> - to_tm(nowtime, &tm);
> + rtc_time64_to_tm(nowtime, 0, &tm);
>
Here was the other obvious typo: I had originally changed this to
'time64_to_tm()', which has slightly different calling conventions to
'rtc_time64_to_tm()', and messed it up when I changed it back
after it turned out I needed to select RTC_LIB anyway.
v2 coming.
Arnd
^ permalink raw reply
* Re: [PATCH 3/5] powerpc: use time64_t in read_persistent_clock
From: Arnd Bergmann @ 2018-04-23 8:28 UTC (permalink / raw)
To: Michael Ellerman
Cc: Paul Mackerras, Benjamin Herrenschmidt, linuxppc-dev,
Arnd Bergmann, Vitaly Bordug, Geoff Levand,
Linux Kernel Mailing List
In-Reply-To: <20180423081114.1813726-3-arnd@arndb.de>
On Mon, Apr 23, 2018 at 10:10 AM, Arnd Bergmann <arnd@arndb.de> wrote:
> @@ -170,7 +170,6 @@ unsigned long __init maple_get_boot_time(void)
> request_resource(&ioport_resource, &rtc_iores);
>
> maple_get_rtc_time(&tm);
> - return mktime(tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
> - tm.tm_hour, tm.tm_min, tm.tm_sec);
> + return rtc_tm_to_time66(&tm);
> }
No, it's not rtc_tm_to_time66 ;(
I did some build testing over the weekend to make sure I find all the stupid
bugs, but then sent out the version without the two fixes. Please ignore all
five patches, will follow up with v2 that fixes this one and another equally
trivial bug.
Arnd
^ permalink raw reply
* [PATCH 2/5] powerpc: rtas: clean up time handling
From: Arnd Bergmann @ 2018-04-23 8:10 UTC (permalink / raw)
To: Michael Ellerman
Cc: Paul Mackerras, Benjamin Herrenschmidt, linuxppc-dev,
Arnd Bergmann, linux-kernel
In-Reply-To: <20180423081114.1813726-1-arnd@arndb.de>
The to_tm() helper function operates on a signed integer for the time,
so it will suffer from overflow in 2038, even on 64-bit kernels.
Rather than fix that function, this replaces its use in the rtas
procfs implementation with the standard rtc_time64_to_tm() helper
that is very similar but is not affected by the overflow.
In order to actually support long times, the parser function gets
changed to 64-bit user input and output as well. Note that the tm_mon
and tm_year representation is slightly different, so we have to manually
add an offset here.
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
arch/powerpc/kernel/rtas-proc.c | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/arch/powerpc/kernel/rtas-proc.c b/arch/powerpc/kernel/rtas-proc.c
index fb070d8cad07..80864b787745 100644
--- a/arch/powerpc/kernel/rtas-proc.c
+++ b/arch/powerpc/kernel/rtas-proc.c
@@ -280,7 +280,7 @@ static int __init proc_rtas_init(void)
__initcall(proc_rtas_init);
-static int parse_number(const char __user *p, size_t count, unsigned long *val)
+static int parse_number(const char __user *p, size_t count, u64 *val)
{
char buf[40];
char *end;
@@ -293,7 +293,7 @@ static int parse_number(const char __user *p, size_t count, unsigned long *val)
buf[count] = 0;
- *val = simple_strtoul(buf, &end, 10);
+ *val = simple_strtoull(buf, &end, 10);
if (*end && *end != '\n')
return -EINVAL;
@@ -307,17 +307,17 @@ static ssize_t ppc_rtas_poweron_write(struct file *file,
const char __user *buf, size_t count, loff_t *ppos)
{
struct rtc_time tm;
- unsigned long nowtime;
+ time64_t nowtime;
int error = parse_number(buf, count, &nowtime);
if (error)
return error;
power_on_time = nowtime; /* save the time */
- to_tm(nowtime, &tm);
+ rtc_time64_to_tm(nowtime, 0, &tm);
error = rtas_call(rtas_token("set-time-for-power-on"), 7, 1, NULL,
- tm.tm_year, tm.tm_mon, tm.tm_mday,
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, 0 /* nano */);
if (error)
printk(KERN_WARNING "error: setting poweron time returned: %s\n",
@@ -373,14 +373,14 @@ static ssize_t ppc_rtas_clock_write(struct file *file,
const char __user *buf, size_t count, loff_t *ppos)
{
struct rtc_time tm;
- unsigned long nowtime;
+ time64_t nowtime;
int error = parse_number(buf, count, &nowtime);
if (error)
return error;
- to_tm(nowtime, &tm);
+ rtc_time64_to_tm(nowtime, 0, &tm);
error = rtas_call(rtas_token("set-time-of-day"), 7, 1, NULL,
- tm.tm_year, tm.tm_mon, tm.tm_mday,
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, 0);
if (error)
printk(KERN_WARNING "error: setting the clock returned: %s\n",
@@ -401,8 +401,8 @@ static int ppc_rtas_clock_show(struct seq_file *m, void *v)
unsigned int year, mon, day, hour, min, sec;
year = ret[0]; mon = ret[1]; day = ret[2];
hour = ret[3]; min = ret[4]; sec = ret[5];
- seq_printf(m, "%lu\n",
- mktime(year, mon, day, hour, min, sec));
+ seq_printf(m, "%lld\n",
+ mktime64(year, mon, day, hour, min, sec));
}
return 0;
}
@@ -731,7 +731,7 @@ static void get_location_code(struct seq_file *m, struct individual_sensor *s,
static ssize_t ppc_rtas_tone_freq_write(struct file *file,
const char __user *buf, size_t count, loff_t *ppos)
{
- unsigned long freq;
+ u64 freq;
int error = parse_number(buf, count, &freq);
if (error)
return error;
@@ -756,7 +756,7 @@ static int ppc_rtas_tone_freq_show(struct seq_file *m, void *v)
static ssize_t ppc_rtas_tone_volume_write(struct file *file,
const char __user *buf, size_t count, loff_t *ppos)
{
- unsigned long volume;
+ u64 volume;
int error = parse_number(buf, count, &volume);
if (error)
return error;
--
2.9.0
^ permalink raw reply related
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