LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3] ocxl: control via sysfs whether the FPGA is reloaded on a link reset
From: Frederic Barrat @ 2020-03-18 10:02 UTC (permalink / raw)
  To: linuxppc-dev, fbarrat, clombard, felix, ajd, alastair

From: Philippe Bergheaud <felix@linux.ibm.com>

Some opencapi FPGA images allow to control if the FPGA should be reloaded
on the next adapter reset. If it is supported, the image specifies it
through a Vendor Specific DVSEC in the config space of function 0.

Signed-off-by: Philippe Bergheaud <felix@linux.ibm.com>
---
Changelog:
v2:
  - refine ResetReload debug message
  - do not call get_function_0() if pci_dev is for function 0
v3:
  - avoid get_function_0() in ocxl_config_set_reset_reload also
       
 Documentation/ABI/testing/sysfs-class-ocxl | 10 ++++
 drivers/misc/ocxl/config.c                 | 68 +++++++++++++++++++++-
 drivers/misc/ocxl/ocxl_internal.h          |  6 ++
 drivers/misc/ocxl/sysfs.c                  | 35 +++++++++++
 include/misc/ocxl-config.h                 |  1 +
 5 files changed, 119 insertions(+), 1 deletion(-)

diff --git a/Documentation/ABI/testing/sysfs-class-ocxl b/Documentation/ABI/testing/sysfs-class-ocxl
index b5b1fa197592..b9ea671d5805 100644
--- a/Documentation/ABI/testing/sysfs-class-ocxl
+++ b/Documentation/ABI/testing/sysfs-class-ocxl
@@ -33,3 +33,13 @@ Date:		January 2018
 Contact:	linuxppc-dev@lists.ozlabs.org
 Description:	read/write
 		Give access the global mmio area for the AFU
+
+What:		/sys/class/ocxl/<afu name>/reload_on_reset
+Date:		February 2020
+Contact:	linuxppc-dev@lists.ozlabs.org
+Description:	read/write
+		Control whether the FPGA is reloaded on a link reset
+			0	Do not reload FPGA image from flash
+			1	Reload FPGA image from flash
+			unavailable
+				The device does not support this capability
diff --git a/drivers/misc/ocxl/config.c b/drivers/misc/ocxl/config.c
index c8e19bfb5ef9..b364b6ceb996 100644
--- a/drivers/misc/ocxl/config.c
+++ b/drivers/misc/ocxl/config.c
@@ -71,6 +71,20 @@ static int find_dvsec_afu_ctrl(struct pci_dev *dev, u8 afu_idx)
 	return 0;
 }
 
+/**
+ * get_function_0() - Find a related PCI device (function 0)
+ * @device: PCI device to match
+ *
+ * Returns a pointer to the related device, or null if not found
+ */
+static struct pci_dev *get_function_0(struct pci_dev *dev)
+{
+	unsigned int devfn = PCI_DEVFN(PCI_SLOT(dev->devfn), 0);
+
+	return pci_get_domain_bus_and_slot(pci_domain_nr(dev->bus),
+					   dev->bus->number, devfn);
+}
+
 static void read_pasid(struct pci_dev *dev, struct ocxl_fn_config *fn)
 {
 	u16 val;
@@ -159,7 +173,7 @@ static int read_dvsec_afu_info(struct pci_dev *dev, struct ocxl_fn_config *fn)
 static int read_dvsec_vendor(struct pci_dev *dev)
 {
 	int pos;
-	u32 cfg, tlx, dlx;
+	u32 cfg, tlx, dlx, reset_reload;
 
 	/*
 	 * vendor specific DVSEC is optional
@@ -183,6 +197,58 @@ static int read_dvsec_vendor(struct pci_dev *dev)
 	dev_dbg(&dev->dev, "  CFG version = 0x%x\n", cfg);
 	dev_dbg(&dev->dev, "  TLX version = 0x%x\n", tlx);
 	dev_dbg(&dev->dev, "  DLX version = 0x%x\n", dlx);
+
+	if (ocxl_config_get_reset_reload(dev, &reset_reload) != 0)
+		dev_dbg(&dev->dev, "  ResetReload is not available\n");
+	else
+		dev_dbg(&dev->dev, "  ResetReload = 0x%x\n", reset_reload);
+	return 0;
+}
+
+int ocxl_config_get_reset_reload(struct pci_dev *dev, int *val)
+{
+	int reset_reload = -1;
+	int pos = 0;
+	struct pci_dev *dev0 = dev;
+
+	if (PCI_FUNC(dev->devfn) != 0)
+		dev0 = get_function_0(dev);
+
+	if (dev0)
+		pos = find_dvsec(dev0, OCXL_DVSEC_VENDOR_ID);
+
+	if (pos)
+		pci_read_config_dword(dev0,
+				      pos + OCXL_DVSEC_VENDOR_RESET_RELOAD,
+				      &reset_reload);
+	if (reset_reload == -1)
+		return reset_reload;
+
+	*val = reset_reload & BIT(0);
+	return 0;
+}
+
+int ocxl_config_set_reset_reload(struct pci_dev *dev, int val)
+{
+	int reset_reload = -1;
+	int pos = 0;
+	struct pci_dev *dev0 = dev;
+
+	if (PCI_FUNC(dev->devfn) != 0)
+		dev0 = get_function_0(dev);
+
+	if (dev0)
+		pos = find_dvsec(dev0, OCXL_DVSEC_VENDOR_ID);
+
+	if (pos)
+		pci_read_config_dword(dev0,
+				      pos + OCXL_DVSEC_VENDOR_RESET_RELOAD,
+				      &reset_reload);
+	if (reset_reload == -1)
+		return reset_reload;
+
+	val &= BIT(0);
+	pci_write_config_dword(dev0, pos + OCXL_DVSEC_VENDOR_RESET_RELOAD, val);
 	return 0;
 }
 
diff --git a/drivers/misc/ocxl/ocxl_internal.h b/drivers/misc/ocxl/ocxl_internal.h
index 345bf843a38e..af9a84aeee6f 100644
--- a/drivers/misc/ocxl/ocxl_internal.h
+++ b/drivers/misc/ocxl/ocxl_internal.h
@@ -112,6 +112,12 @@ void ocxl_actag_afu_free(struct ocxl_fn *fn, u32 start, u32 size);
  */
 int ocxl_config_get_pasid_info(struct pci_dev *dev, int *count);
 
+/*
+ * Control whether the FPGA is reloaded on a link reset
+ */
+int ocxl_config_get_reset_reload(struct pci_dev *dev, int *val);
+int ocxl_config_set_reset_reload(struct pci_dev *dev, int val);
+
 /*
  * Check if an AFU index is valid for the given function.
  *
diff --git a/drivers/misc/ocxl/sysfs.c b/drivers/misc/ocxl/sysfs.c
index 58f1ba264206..8f69f7311343 100644
--- a/drivers/misc/ocxl/sysfs.c
+++ b/drivers/misc/ocxl/sysfs.c
@@ -51,11 +51,46 @@ static ssize_t contexts_show(struct device *device,
 			afu->pasid_count, afu->pasid_max);
 }
 
+static ssize_t reload_on_reset_show(struct device *device,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct ocxl_afu *afu = to_afu(device);
+	struct ocxl_fn *fn = afu->fn;
+	struct pci_dev *pci_dev = to_pci_dev(fn->dev.parent);
+	int val;
+
+	if (ocxl_config_get_reset_reload(pci_dev, &val))
+		return scnprintf(buf, PAGE_SIZE, "unavailable\n");
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", val);
+}
+
+static ssize_t reload_on_reset_store(struct device *device,
+		struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	struct ocxl_afu *afu = to_afu(device);
+	struct ocxl_fn *fn = afu->fn;
+	struct pci_dev *pci_dev = to_pci_dev(fn->dev.parent);
+	int rc, val;
+
+	rc = sscanf(buf, "%i", &val);
+	if ((rc != 1) || !(val == 1 || val == 0))
+		return -EINVAL;
+
+	if (ocxl_config_set_reset_reload(pci_dev, val))
+		return -ENODEV;
+
+	return count;
+}
+
 static struct device_attribute afu_attrs[] = {
 	__ATTR_RO(global_mmio_size),
 	__ATTR_RO(pp_mmio_size),
 	__ATTR_RO(afu_version),
 	__ATTR_RO(contexts),
+	__ATTR_RW(reload_on_reset),
 };
 
 static ssize_t global_mmio_read(struct file *filp, struct kobject *kobj,
diff --git a/include/misc/ocxl-config.h b/include/misc/ocxl-config.h
index 3526fa996a22..ccfd3b463517 100644
--- a/include/misc/ocxl-config.h
+++ b/include/misc/ocxl-config.h
@@ -41,5 +41,6 @@
 #define   OCXL_DVSEC_VENDOR_CFG_VERS            0x0C
 #define   OCXL_DVSEC_VENDOR_TLX_VERS            0x10
 #define   OCXL_DVSEC_VENDOR_DLX_VERS            0x20
+#define   OCXL_DVSEC_VENDOR_RESET_RELOAD        0x38
 
 #endif /* _OCXL_CONFIG_H_ */
-- 
2.25.1


^ permalink raw reply related

* Re: [PATCH 10/15] powerpc/watchpoint: Use loop for thread_struct->ptrace_bps
From: Ravi Bangoria @ 2020-03-18  9:43 UTC (permalink / raw)
  To: Christophe Leroy
  Cc: apopple, mikey, Ravi Bangoria, peterz, oleg, npiggin,
	linux-kernel, paulus, jolsa, fweisbec, naveen.n.rao, linuxppc-dev,
	mingo
In-Reply-To: <0eeeac90-b5e3-722b-2d2c-bb95c81d851a@c-s.fr>

>> @@ -1628,6 +1628,9 @@ int copy_thread_tls(unsigned long clone_flags, unsigned long usp,
>>       void (*f)(void);
>>       unsigned long sp = (unsigned long)task_stack_page(p) + THREAD_SIZE;
>>       struct thread_info *ti = task_thread_info(p);
>> +#ifdef CONFIG_HAVE_HW_BREAKPOINT
>> +    int i;
>> +#endif
> 
> Could we avoid all those #ifdefs ?
> 
> I think if we make p->thread.ptrace_bps[] exist all the time, with a size of 0 when CONFIG_HAVE_HW_BREAKPOINT is not set, then we can drop a lot of #ifdefs.

Hmm.. what you are saying seems possible. But IMO it should be done as
independent series. Will work on it.

> 
>>       klp_init_thread_info(p);
>> @@ -1687,7 +1690,8 @@ int copy_thread_tls(unsigned long clone_flags, unsigned long usp,
>>       p->thread.ksp_limit = (unsigned long)end_of_stack(p);
>>   #endif
>>   #ifdef CONFIG_HAVE_HW_BREAKPOINT
>> -    p->thread.ptrace_bps[0] = NULL;
>> +    for (i = 0; i < nr_wp_slots(); i++)
>> +        p->thread.ptrace_bps[i] = NULL;
>>   #endif
>>       p->thread.fp_save_area = NULL;
>> diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
>> index f6d7955fc61e..e2651f86d56f 100644
>> --- a/arch/powerpc/kernel/ptrace.c
>> +++ b/arch/powerpc/kernel/ptrace.c
> 
> You'll have to rebase all this on the series https://patchwork.ozlabs.org/project/linuxppc-dev/list/?series=161356 which is about to go into powerpc-next

Sure. Thanks for heads up.

> 
>> @@ -2829,6 +2829,19 @@ static int set_dac_range(struct task_struct *child,
>>   }
>>   #endif /* CONFIG_PPC_ADV_DEBUG_DAC_RANGE */
>> +#ifdef CONFIG_HAVE_HW_BREAKPOINT
>> +static int empty_ptrace_bp(struct thread_struct *thread)
>> +{
>> +    int i;
>> +
>> +    for (i = 0; i < nr_wp_slots(); i++) {
>> +        if (!thread->ptrace_bps[i])
>> +            return i;
>> +    }
>> +    return -1;
>> +}
>> +#endif
> 
> What does this function do exactly ? I seems to do more than what its name suggests.

It finds an empty breakpoint in ptrace_bps[]. But yeah, function name is
misleading. I'll rename it to find_empty_ptrace_bp().

...

>> @@ -2979,10 +2993,10 @@ static long ppc_del_hwdebug(struct task_struct *child, long data)
>>           return -EINVAL;
>>   #ifdef CONFIG_HAVE_HW_BREAKPOINT
>> -    bp = thread->ptrace_bps[0];
>> +    bp = thread->ptrace_bps[data - 1];
> 
> Is data checked somewhere to ensure it is not out of boundaries ? Or are we sure it is always within ?

Yes. it's checked. See patch #9:

   @@ -2955,7 +2975,7 @@ static long ppc_del_hwdebug(struct task_struct *child, long data)
    	}
    	return rc;
    #else
   -	if (data != 1)
   +	if (data < 1 || data > nr_wp_slots())
    		return -EINVAL;
    
    #ifdef CONFIG_HAVE_HW_BREAKPOINT

Thanks,
Ravi


^ permalink raw reply

* Re: [PATCH 09/15] powerpc/watchpoint: Convert thread_struct->hw_brk to an array
From: Ravi Bangoria @ 2020-03-18  9:22 UTC (permalink / raw)
  To: Christophe Leroy
  Cc: apopple, mikey, Ravi Bangoria, peterz, oleg, npiggin,
	linux-kernel, paulus, jolsa, fweisbec, naveen.n.rao, linuxppc-dev,
	mingo
In-Reply-To: <a1794506-ada3-7b1e-2fa7-bcebf6ec9d68@c-s.fr>



On 3/18/20 2:26 PM, Christophe Leroy wrote:
> 
> 
> Le 18/03/2020 à 09:36, Ravi Bangoria a écrit :
>>
>>
>> On 3/17/20 4:07 PM, Christophe Leroy wrote:
>>>
>>>
>>> Le 09/03/2020 à 09:58, Ravi Bangoria a écrit :
>>>> So far powerpc hw supported only one watchpoint. But Future Power
>>>> architecture is introducing 2nd DAWR. Convert thread_struct->hw_brk
>>>> into an array.
>>>
>>> Looks like you are doing a lot more than that in this patch.
>>>
>>> Should this patch be splitted in two parts ?
>>
>> So far thread_struct->hw_brk was a normal variable so accessing it was simple.
>> Once it gets converted into an array, loop needs to be used to access it. So
>> all misc changes are basically converting simple access into loops.
>>
>> I don't see how this can be splitted.
>>
> 
> You could first change all thread_struct->hw_brk to thread_struct->hw_brk[0] or thread_struct->hw_brk[i] when you know that i can only be 0 for now. Then add the loops and new functions in a second patch.

Ok. I've already tried that :) But it looked unnecessary split to
me because _all_ hw_brk => hw_brk[0] changes will again need to be
changed to use loop in 2nd patch. So I thought it's better to do
both changes in one patch.

Thanks,
Ravi


^ permalink raw reply

* Re: [PATCH 09/15] powerpc/watchpoint: Convert thread_struct->hw_brk to an array
From: Christophe Leroy @ 2020-03-18  8:56 UTC (permalink / raw)
  To: Ravi Bangoria
  Cc: apopple, mikey, peterz, oleg, npiggin, linux-kernel, paulus,
	jolsa, fweisbec, naveen.n.rao, linuxppc-dev, mingo
In-Reply-To: <5efe2f41-2bf3-6927-aa6a-dcedb672c69d@linux.ibm.com>



Le 18/03/2020 à 09:36, Ravi Bangoria a écrit :
> 
> 
> On 3/17/20 4:07 PM, Christophe Leroy wrote:
>>
>>
>> Le 09/03/2020 à 09:58, Ravi Bangoria a écrit :
>>> So far powerpc hw supported only one watchpoint. But Future Power
>>> architecture is introducing 2nd DAWR. Convert thread_struct->hw_brk
>>> into an array.
>>
>> Looks like you are doing a lot more than that in this patch.
>>
>> Should this patch be splitted in two parts ?
> 
> So far thread_struct->hw_brk was a normal variable so accessing it was 
> simple.
> Once it gets converted into an array, loop needs to be used to access 
> it. So
> all misc changes are basically converting simple access into loops.
> 
> I don't see how this can be splitted.
> 

You could first change all thread_struct->hw_brk to 
thread_struct->hw_brk[0] or thread_struct->hw_brk[i] when you know that 
i can only be 0 for now. Then add the loops and new functions in a 
second patch.

Christophe

^ permalink raw reply

* Re: [PATCH 09/15] powerpc/watchpoint: Convert thread_struct->hw_brk to an array
From: Ravi Bangoria @ 2020-03-18  8:36 UTC (permalink / raw)
  To: Christophe Leroy
  Cc: apopple, mikey, Ravi Bangoria, peterz, oleg, npiggin,
	linux-kernel, paulus, jolsa, fweisbec, naveen.n.rao, linuxppc-dev,
	mingo
In-Reply-To: <0fc9489d-9955-f649-9007-ce6f5da345be@c-s.fr>



On 3/17/20 4:07 PM, Christophe Leroy wrote:
> 
> 
> Le 09/03/2020 à 09:58, Ravi Bangoria a écrit :
>> So far powerpc hw supported only one watchpoint. But Future Power
>> architecture is introducing 2nd DAWR. Convert thread_struct->hw_brk
>> into an array.
> 
> Looks like you are doing a lot more than that in this patch.
> 
> Should this patch be splitted in two parts ?

So far thread_struct->hw_brk was a normal variable so accessing it was simple.
Once it gets converted into an array, loop needs to be used to access it. So
all misc changes are basically converting simple access into loops.

I don't see how this can be splitted.

Thanks,
Ravi


^ permalink raw reply

* Re: [PATCH 3/3] mm/page_alloc: Keep memoryless cpuless node 0 offline
From: Srikar Dronamraju @ 2020-03-18  7:50 UTC (permalink / raw)
  To: Michal Hocko
  Cc: Linus Torvalds, linux-kernel, linux-mm, Mel Gorman,
	Kirill A. Shutemov, Christopher Lameter, linuxppc-dev,
	Andrew Morton, Vlastimil Babka
In-Reply-To: <20200316085425.GB11482@dhcp22.suse.cz>

* Michal Hocko <mhocko@kernel.org> [2020-03-16 09:54:25]:

> On Sun 15-03-20 14:20:05, Cristopher Lameter wrote:
> > On Wed, 11 Mar 2020, Srikar Dronamraju wrote:
> > 
> > > Currently Linux kernel with CONFIG_NUMA on a system with multiple
> > > possible nodes, marks node 0 as online at boot.  However in practice,
> > > there are systems which have node 0 as memoryless and cpuless.
> > 
> > Would it not be better and simpler to require that node 0 always has
> > memory (and processors)? A  mininum operational set?
> 
> I do not think you can simply ignore the reality. I cannot say that I am
> a fan of memoryless/cpuless numa configurations but they are a sad
> reality of different LPAR configurations. We have to deal with them.
> Besides that I do not really see any strong technical arguments to lack
> a support for those crippled configurations. We do have zonelists that
> allow to do reasonable decisions on memoryless nodes. So no, I do not
> think that this is a viable approach.
> 

I agree with Michal, kernel should accept the reality and work with
different Lpar configurations.

> > We can dynamically number the nodes right? So just make sure that the
> > firmware properly creates memory on node 0?
> 
> Are you suggesting that the OS would renumber NUMA nodes coming
> from FW just to satisfy node 0 existence? If yes then I believe this is
> really a bad idea because it would make HW/LPAR configuration matching
> to the resulting memory layout really hard to follow.
> 
> -- 
> Michal Hocko
> SUSE Labs

Michal, Vlastimil, Christoph and others, do you have any more comments,
suggestions or any other feedback. If not, can you please add your
reviewed-by, acked etc.

-- 
Thanks and Regards
Srikar Dronamraju


^ permalink raw reply

* Re: [PATCH 08/15] powerpc/watchpoint: Disable all available watchpoints when !dawr_force_enable
From: Ravi Bangoria @ 2020-03-18  7:32 UTC (permalink / raw)
  To: Christophe Leroy
  Cc: apopple, mikey, Ravi Bangoria, peterz, oleg, npiggin,
	linux-kernel, paulus, jolsa, fweisbec, naveen.n.rao, linuxppc-dev,
	mingo
In-Reply-To: <1381b9f9-4999-0e03-8344-af84a88fa074@c-s.fr>



On 3/17/20 4:05 PM, Christophe Leroy wrote:
> 
> 
> Le 09/03/2020 à 09:57, Ravi Bangoria a écrit :
>> Instead of disabling only first watchpoint, disable all available
>> watchpoints while clearing dawr_force_enable.
>>
>> Signed-off-by: Ravi Bangoria <ravi.bangoria@linux.ibm.com>
>> ---
>>   arch/powerpc/kernel/dawr.c | 10 +++++++---
>>   1 file changed, 7 insertions(+), 3 deletions(-)
>>
>> diff --git a/arch/powerpc/kernel/dawr.c b/arch/powerpc/kernel/dawr.c
>> index 311e51ee09f4..5c882f07ac7d 100644
>> --- a/arch/powerpc/kernel/dawr.c
>> +++ b/arch/powerpc/kernel/dawr.c
>> @@ -50,9 +50,13 @@ int set_dawr(struct arch_hw_breakpoint *brk, int nr)
>>       return 0;
>>   }
>> -static void set_dawr_cb(void *info)
>> +static void disable_dawrs(void *info)
> 
> Can you explain a bit more what you do exactly ? Why do you change the name of the function and why the parameter becomes NULL ? And why it doens't take into account the parameter anymore ?

Before:
   static void set_dawr_cb(void *info)
   {
           set_dawr(info);
   }
   
   static ssize_t dawr_write_file_bool(...)
   {
   	...
           /* If we are clearing, make sure all CPUs have the DAWR cleared */
           if (!dawr_force_enable)
                   smp_call_function(set_dawr_cb, &null_brk, 0);
   }

After:
   static void disable_dawrs(void *info)
   {
           struct arch_hw_breakpoint null_brk = {0};
           int i;
   
           for (i = 0; i < nr_wp_slots(); i++)
                   set_dawr(&null_brk, i);
   }
   
   static ssize_t dawr_write_file_bool(...)
   {
   	...
           /* If we are clearing, make sure all CPUs have the DAWR cleared */
           if (!dawr_force_enable)
                   smp_call_function(disable_dawrs, NULL, 0);
   }

We use callback function only for disabling watchpoint. Thus I renamed
it to disable_dawrs(). And we are passing null_brk as parameter which
is not really required while disabling watchpoint. So removed it.

Thanks,
Ravi


^ permalink raw reply

* Re: [PATCH 2/4] mm/slub: Use mem_node to allocate a new slab
From: Srikar Dronamraju @ 2020-03-18  7:29 UTC (permalink / raw)
  To: Vlastimil Babka
  Cc: Sachin Sant, Michal Hocko, linux-mm, Kirill Tkhai, Mel Gorman,
	Joonsoo Kim, Andrew Morton, Bharata B Rao, linuxppc-dev,
	Christopher Lameter
In-Reply-To: <abeaec0d-e9ea-28ce-5b9a-9a6d41ab38c9@suse.cz>

* Vlastimil Babka <vbabka@suse.cz> [2020-03-17 16:29:21]:

> > If we pass this node 0 (which is memoryless/cpuless) to
> > alloc_pages_node. Please note I am only setting node_numa_mem only
> > for offline nodes. However we could change this to set for all offline and
> > memoryless nodes.
> 
> That would indeed make sense.
> 
> But I guess that alloc_pages would still crash as the result of
> numa_to_mem_node() is not passed down to alloc_pages() without this patch. In
> __alloc_pages_node() we currently have "The node must be valid and online" so
> offline nodes don't have zonelists. Either they get them, or we indeed need
> something like this patch. But in order to not make get_any_partial() dead code,
> the final replacement of invalid node with a valid one should be done in
> alloc_slab_page() I guess?
> 

I am posting v2 with this change.

> >> node_to_mem_node() could be just a shortcut for the first zone's node in the
> >> zonelist, so that fallback follows the topology.

-- 
Thanks and Regards
Srikar Dronamraju


^ permalink raw reply

* [PATCH v2 4/4] powerpc/numa: Set fallback nodes for offline nodes
From: Srikar Dronamraju @ 2020-03-18  7:28 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Sachin Sant, Nathan Lynch, Srikar Dronamraju, Michal Hocko,
	linux-mm, Kirill Tkhai, Mel Gorman, Christopher Lameter,
	Bharata B Rao, linuxppc-dev, Joonsoo Kim, Vlastimil Babka
In-Reply-To: <20200318072810.9735-1-srikar@linux.vnet.ibm.com>

Currently fallback nodes for offline nodes aren't set. Hence by default
node 0 ends up being the default node. However node 0 might be offline.

Fix this by explicitly setting fallback node. Ensure first_memory_node
is set before kernel does explicit setting of fallback node.

Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: linux-mm@kvack.org
Cc: Mel Gorman <mgorman@suse.de>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Sachin Sant <sachinp@linux.vnet.ibm.com>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Christopher Lameter <cl@linux.com>
Cc: linuxppc-dev@lists.ozlabs.org
Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com>
Cc: Kirill Tkhai <ktkhai@virtuozzo.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Bharata B Rao <bharata@linux.ibm.com>
Cc: Nathan Lynch <nathanl@linux.ibm.com>

Reported-by: Sachin Sant <sachinp@linux.vnet.ibm.com>
Tested-by: Sachin Sant <sachinp@linux.vnet.ibm.com>
Signed-off-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
---
Changelog v1 -> v2:
- Handled comments from Bharata B Rao
	- Dont use dump_numa_cpu_topology to set fallback nodes

 arch/powerpc/include/asm/topology.h | 16 ++++++++++++++++
 arch/powerpc/kernel/smp.c           |  1 +
 2 files changed, 17 insertions(+)

diff --git a/arch/powerpc/include/asm/topology.h b/arch/powerpc/include/asm/topology.h
index 2db7ba789720..baa89364197c 100644
--- a/arch/powerpc/include/asm/topology.h
+++ b/arch/powerpc/include/asm/topology.h
@@ -62,6 +62,21 @@ static inline int early_cpu_to_node(int cpu)
 	 */
 	return (nid < 0) ? 0 : nid;
 }
+
+static inline int update_default_numa_mem(void)
+{
+	unsigned int node;
+
+	for_each_node(node) {
+		/*
+		 * For all possible but not yet online nodes, ensure their
+		 * node_numa_mem is set correctly so that kmalloc_node works
+		 * for such nodes.
+		 */
+		if (!node_online(node))
+			reset_numa_mem(node);
+	}
+}
 #else
 
 static inline int early_cpu_to_node(int cpu) { return 0; }
@@ -90,6 +105,7 @@ static inline int cpu_distance(__be32 *cpu1_assoc, __be32 *cpu2_assoc)
 	return 0;
 }
 
+static inline int update_default_numa_mem(void) {}
 #endif /* CONFIG_NUMA */
 
 #if defined(CONFIG_NUMA) && defined(CONFIG_PPC_SPLPAR)
diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c
index 37c12e3bab9e..d23faa70ea2d 100644
--- a/arch/powerpc/kernel/smp.c
+++ b/arch/powerpc/kernel/smp.c
@@ -1383,6 +1383,7 @@ void __init smp_cpus_done(unsigned int max_cpus)
 	if (smp_ops && smp_ops->bringup_done)
 		smp_ops->bringup_done();
 
+	update_default_numa_mem();
 	dump_numa_cpu_topology();
 
 #ifdef CONFIG_SCHED_SMT
-- 
2.18.1


^ permalink raw reply related

* [PATCH v2 3/4] mm: Implement reset_numa_mem
From: Srikar Dronamraju @ 2020-03-18  7:28 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Sachin Sant, Nathan Lynch, Srikar Dronamraju, Michal Hocko,
	linux-mm, Kirill Tkhai, Mel Gorman, Christopher Lameter,
	Bharata B Rao, linuxppc-dev, Joonsoo Kim, Vlastimil Babka
In-Reply-To: <20200318072810.9735-1-srikar@linux.vnet.ibm.com>

For a memoryless or offline nodes, node_numa_mem refers to a N_MEMORY
fallback node. Currently kernel has an API set_numa_mem that sets
node_numa_mem for memoryless node. However this API cannot be used for
offline nodes. Hence all offline nodes will have their node_numa_mem set
to 0. However systems can themselves have node 0 as offline i.e
memoryless and cpuless at this time. In such cases,
node_to_mem_node() fails to provide a N_MEMORY fallback node.

Mitigate this by having a new API that sets the default node_numa_mem for
offline nodes to be first_memory_node.

Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: linux-mm@kvack.org
Cc: Mel Gorman <mgorman@suse.de>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Sachin Sant <sachinp@linux.vnet.ibm.com>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Christopher Lameter <cl@linux.com>
Cc: linuxppc-dev@lists.ozlabs.org
Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com>
Cc: Kirill Tkhai <ktkhai@virtuozzo.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Bharata B Rao <bharata@linux.ibm.com>
Cc: Nathan Lynch <nathanl@linux.ibm.com>

Reported-by: Sachin Sant <sachinp@linux.vnet.ibm.com>
Tested-by: Sachin Sant <sachinp@linux.vnet.ibm.com>
Signed-off-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
---
 include/asm-generic/topology.h | 3 +++
 include/linux/topology.h       | 7 +++++++
 2 files changed, 10 insertions(+)

diff --git a/include/asm-generic/topology.h b/include/asm-generic/topology.h
index 238873739550..e803ee7850e6 100644
--- a/include/asm-generic/topology.h
+++ b/include/asm-generic/topology.h
@@ -68,6 +68,9 @@
 #ifndef set_numa_mem
 #define set_numa_mem(node)
 #endif
+#ifndef reset_numa_mem
+#define reset_numa_mem(node)
+#endif
 #ifndef set_cpu_numa_mem
 #define set_cpu_numa_mem(cpu, node)
 #endif
diff --git a/include/linux/topology.h b/include/linux/topology.h
index eb2fe6edd73c..bebda80038bf 100644
--- a/include/linux/topology.h
+++ b/include/linux/topology.h
@@ -147,6 +147,13 @@ static inline int node_to_mem_node(int node)
 }
 #endif
 
+#ifndef reset_numa_mem
+static inline void reset_numa_mem(int node)
+{
+	_node_numa_mem_[node] = first_memory_node;
+}
+#endif
+
 #ifndef numa_mem_id
 /* Returns the number of the nearest Node with memory */
 static inline int numa_mem_id(void)
-- 
2.18.1


^ permalink raw reply related

* [PATCH v2 2/4] mm/slub: Use mem_node to allocate a new slab
From: Srikar Dronamraju @ 2020-03-18  7:28 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Sachin Sant, Nathan Lynch, Srikar Dronamraju, Michal Hocko,
	linux-mm, Kirill Tkhai, Mel Gorman, Christopher Lameter,
	Bharata B Rao, linuxppc-dev, Joonsoo Kim, Vlastimil Babka
In-Reply-To: <20200318072810.9735-1-srikar@linux.vnet.ibm.com>

Currently while allocating a slab for a offline node, we use its
associated node_numa_mem to search for a partial slab. If we don't find
a partial slab, we try allocating a slab from the offline node using
__alloc_pages_node. However this is bound to fail.

NIP [c00000000039a300] __alloc_pages_nodemask+0x130/0x3b0
LR [c00000000039a3c4] __alloc_pages_nodemask+0x1f4/0x3b0
Call Trace:
[c0000008b36837f0] [c00000000039a3b4] __alloc_pages_nodemask+0x1e4/0x3b0 (unreliable)
[c0000008b3683870] [c0000000003d1ff8] new_slab+0x128/0xcf0
[c0000008b3683950] [c0000000003d6060] ___slab_alloc+0x410/0x820
[c0000008b3683a40] [c0000000003d64a4] __slab_alloc+0x34/0x60
[c0000008b3683a70] [c0000000003d78b0] __kmalloc_node+0x110/0x490
[c0000008b3683af0] [c000000000343a08] kvmalloc_node+0x58/0x110
[c0000008b3683b30] [c0000000003ffd44] mem_cgroup_css_online+0x104/0x270
[c0000008b3683b90] [c000000000234e08] online_css+0x48/0xd0
[c0000008b3683bc0] [c00000000023dedc] cgroup_apply_control_enable+0x2ec/0x4d0
[c0000008b3683ca0] [c0000000002416f8] cgroup_mkdir+0x228/0x5f0
[c0000008b3683d10] [c000000000520360] kernfs_iop_mkdir+0x90/0xf0
[c0000008b3683d50] [c00000000043e400] vfs_mkdir+0x110/0x230
[c0000008b3683da0] [c000000000441ee0] do_mkdirat+0xb0/0x1a0
[c0000008b3683e20] [c00000000000b278] system_call+0x5c/0x68

Mitigate this by allocating the new slab from the node_numa_mem.

Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: linux-mm@kvack.org
Cc: Mel Gorman <mgorman@suse.de>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Sachin Sant <sachinp@linux.vnet.ibm.com>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Christopher Lameter <cl@linux.com>
Cc: linuxppc-dev@lists.ozlabs.org
Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com>
Cc: Kirill Tkhai <ktkhai@virtuozzo.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Bharata B Rao <bharata@linux.ibm.com>
Cc: Nathan Lynch <nathanl@linux.ibm.com>

Reported-by: Sachin Sant <sachinp@linux.vnet.ibm.com>
Tested-by: Sachin Sant <sachinp@linux.vnet.ibm.com>
Signed-off-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
---
Changelog v1 -> v2:
- Handled comments from Vlastimil Babka
	- Now node gets set to node_numa_mem in new_slab_objects.

 mm/slub.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/mm/slub.c b/mm/slub.c
index 1c55bf7892bf..2dc603a84290 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -2475,6 +2475,9 @@ static inline void *new_slab_objects(struct kmem_cache *s, gfp_t flags,
 	if (freelist)
 		return freelist;
 
+	if (node != NUMA_NO_NODE && !node_present_pages(node))
+		node = node_to_mem_node(node);
+
 	page = new_slab(s, flags, node);
 	if (page) {
 		c = raw_cpu_ptr(s->cpu_slab);
@@ -2569,12 +2572,10 @@ static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,
 redo:
 
 	if (unlikely(!node_match(page, node))) {
-		int searchnode = node;
-
 		if (node != NUMA_NO_NODE && !node_present_pages(node))
-			searchnode = node_to_mem_node(node);
+			node = node_to_mem_node(node);
 
-		if (unlikely(!node_match(page, searchnode))) {
+		if (unlikely(!node_match(page, node))) {
 			stat(s, ALLOC_NODE_MISMATCH);
 			deactivate_slab(s, page, c->freelist, c);
 			goto new_slab;
-- 
2.18.1


^ permalink raw reply related

* [PATCH v2 1/4] mm: Check for node_online in node_present_pages
From: Srikar Dronamraju @ 2020-03-18  7:28 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Sachin Sant, Nathan Lynch, Srikar Dronamraju, Michal Hocko,
	linux-mm, Kirill Tkhai, Mel Gorman, Christopher Lameter,
	Bharata B Rao, linuxppc-dev, Joonsoo Kim, Vlastimil Babka
In-Reply-To: <20200318072810.9735-1-srikar@linux.vnet.ibm.com>

Calling a kmalloc_node on a possible node which is not yet onlined can
lead to panic. Currently node_present_pages() doesn't verify the node is
online before accessing the pgdat for the node. However pgdat struct may
not be available resulting in a crash.

NIP [c0000000003d55f4] ___slab_alloc+0x1f4/0x760
LR [c0000000003d5b94] __slab_alloc+0x34/0x60
Call Trace:
[c0000008b3783960] [c0000000003d5734] ___slab_alloc+0x334/0x760 (unreliable)
[c0000008b3783a40] [c0000000003d5b94] __slab_alloc+0x34/0x60
[c0000008b3783a70] [c0000000003d6fa0] __kmalloc_node+0x110/0x490
[c0000008b3783af0] [c0000000003443d8] kvmalloc_node+0x58/0x110
[c0000008b3783b30] [c0000000003fee38] mem_cgroup_css_online+0x108/0x270
[c0000008b3783b90] [c000000000235aa8] online_css+0x48/0xd0
[c0000008b3783bc0] [c00000000023eaec] cgroup_apply_control_enable+0x2ec/0x4d0
[c0000008b3783ca0] [c000000000242318] cgroup_mkdir+0x228/0x5f0
[c0000008b3783d10] [c00000000051e170] kernfs_iop_mkdir+0x90/0xf0
[c0000008b3783d50] [c00000000043dc00] vfs_mkdir+0x110/0x230
[c0000008b3783da0] [c000000000441c90] do_mkdirat+0xb0/0x1a0
[c0000008b3783e20] [c00000000000b278] system_call+0x5c/0x68

Fix this by verifying the node is online before accessing the pgdat
structure. Fix the same for node_spanned_pages() too.

Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: linux-mm@kvack.org
Cc: Mel Gorman <mgorman@suse.de>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Sachin Sant <sachinp@linux.vnet.ibm.com>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Christopher Lameter <cl@linux.com>
Cc: linuxppc-dev@lists.ozlabs.org
Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com>
Cc: Kirill Tkhai <ktkhai@virtuozzo.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Bharata B Rao <bharata@linux.ibm.com>
Cc: Nathan Lynch <nathanl@linux.ibm.com>

Reported-by: Sachin Sant <sachinp@linux.vnet.ibm.com>
Tested-by: Sachin Sant <sachinp@linux.vnet.ibm.com>
Signed-off-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
---
 include/linux/mmzone.h | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index f3f264826423..88078a3b95e5 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -756,8 +756,10 @@ typedef struct pglist_data {
 	atomic_long_t		vm_stat[NR_VM_NODE_STAT_ITEMS];
 } pg_data_t;
 
-#define node_present_pages(nid)	(NODE_DATA(nid)->node_present_pages)
-#define node_spanned_pages(nid)	(NODE_DATA(nid)->node_spanned_pages)
+#define node_present_pages(nid)		\
+	(node_online(nid) ? NODE_DATA(nid)->node_present_pages : 0)
+#define node_spanned_pages(nid)		\
+	(node_online(nid) ? NODE_DATA(nid)->node_spanned_pages : 0)
 #ifdef CONFIG_FLAT_NODE_MEM_MAP
 #define pgdat_page_nr(pgdat, pagenr)	((pgdat)->node_mem_map + (pagenr))
 #else
-- 
2.18.1


^ permalink raw reply related

* [PATCH v2 0/4] Fix kmalloc_node on offline nodes
From: Srikar Dronamraju @ 2020-03-18  7:28 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Sachin Sant, Nathan Lynch, Srikar Dronamraju, Michal Hocko,
	linux-mm, Kirill Tkhai, Mel Gorman, Christopher Lameter,
	Bharata B Rao, linuxppc-dev, Joonsoo Kim, Vlastimil Babka

Changelog v1 -> v2:
- Handled comments from Vlastimil Babka and Bharata B Rao
- Changes only in patch 2 and 4.

Sachin recently reported that linux-next was no more bootable on few
powerpc systems.
https://lore.kernel.org/linux-next/3381CD91-AB3D-4773-BA04-E7A072A63968@linux.vnet.ibm.com/

# numactl -H
available: 2 nodes (0-1)
node 0 cpus:
node 0 size: 0 MB
node 0 free: 0 MB
node 1 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
node 1 size: 35247 MB
node 1 free: 30907 MB
node distances:
node   0   1
  0:  10  40
  1:  40  10
#

Sachin bisected the problem to Commit a75056fc1e7c ("mm/memcontrol.c: allocate
shrinker_map on appropriate NUMA node")

The root cause analysis showed that mm/slub and powerpc/numa had some shortcomings
with respect to offline nodes.

This patch series is on top of patches posted at
https://lore.kernel.org/linuxppc-dev/20200311110237.5731-1-srikar@linux.vnet.ibm.com/t/#u

Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: linux-mm@kvack.org
Cc: Mel Gorman <mgorman@suse.de>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Sachin Sant <sachinp@linux.vnet.ibm.com>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Christopher Lameter <cl@linux.com>
Cc: linuxppc-dev@lists.ozlabs.org
Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com>
Cc: Kirill Tkhai <ktkhai@virtuozzo.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Bharata B Rao <bharata@linux.ibm.com>
Cc: Nathan Lynch <nathanl@linux.ibm.com>

Srikar Dronamraju (4):
  mm: Check for node_online in node_present_pages
  mm/slub: Use mem_node to allocate a new slab
  mm: Implement reset_numa_mem
  powerpc/numa: Set fallback nodes for offline nodes

 arch/powerpc/include/asm/topology.h | 16 ++++++++++++++++
 arch/powerpc/kernel/smp.c           |  1 +
 include/asm-generic/topology.h      |  3 +++
 include/linux/mmzone.h              |  6 ++++--
 include/linux/topology.h            |  7 +++++++
 mm/slub.c                           |  9 +++++----
 6 files changed, 36 insertions(+), 6 deletions(-)

-- 
2.18.1


^ permalink raw reply

* Re: [PATCH v3 3/9] powerpc/vas: Add VAS user space API
From: Daniel Axtens @ 2020-03-18  7:12 UTC (permalink / raw)
  To: Haren Myneni, herbert; +Cc: mikey, sukadev, linuxppc-dev, linux-crypto, npiggin
In-Reply-To: <1583541215.9256.35.camel@hbabu-laptop>

Haren Myneni <haren@linux.ibm.com> writes:

> On power9, userspace can send GZIP compression requests directly to NX
> once kernel establishes NX channel / window with VAS. This patch provides
> user space API which allows user space to establish channel using open
> VAS_TX_WIN_OPEN ioctl, mmap and close operations.
>
> Each window corresponds to file descriptor and application can open
> multiple windows. After the window is opened, VAS_TX_WIN_OPEN icoctl to
> open a window on specific VAS instance, mmap() system call to map
> the hardware address of engine's request queue into the application's
> virtual address space.
>
> Then the application can then submit one or more requests to the the
> engine by using the copy/paste instructions and pasting the CRBs to
> the virtual address (aka paste_address) returned by mmap().
>
> Only NX GZIP coprocessor type is supported right now and allow GZIP
> engine access via /dev/crypto/nx-gzip device node.
>
> Signed-off-by: Sukadev Bhattiprolu <sukadev@linux.vnet.ibm.com>
> Signed-off-by: Haren Myneni <haren@linux.ibm.com>
> ---
>  arch/powerpc/include/asm/vas.h              |  11 ++
>  arch/powerpc/platforms/powernv/Makefile     |   2 +-
>  arch/powerpc/platforms/powernv/vas-api.c    | 290 ++++++++++++++++++++++++++++
>  arch/powerpc/platforms/powernv/vas-window.c |   6 +-
>  arch/powerpc/platforms/powernv/vas.h        |   2 +
>  5 files changed, 307 insertions(+), 4 deletions(-)
>  create mode 100644 arch/powerpc/platforms/powernv/vas-api.c
>
> diff --git a/arch/powerpc/include/asm/vas.h b/arch/powerpc/include/asm/vas.h
> index f93e6b0..e064953 100644
> --- a/arch/powerpc/include/asm/vas.h
> +++ b/arch/powerpc/include/asm/vas.h
> @@ -163,4 +163,15 @@ struct vas_window *vas_tx_win_open(int vasid, enum vas_cop_type cop,
>   */
>  int vas_paste_crb(struct vas_window *win, int offset, bool re);
>  
> +/*
> + * Register / unregister coprocessor type to VAS API which will be exported
> + * to user space. Applications can use this API to open / close window
> + * which can be used to send / receive requests directly to cooprcessor.
> + *
> + * Only NX GZIP coprocessor type is supported now, but this API can be
> + * used for others in future.
> + */
> +int vas_register_coproc_api(struct module *mod);
> +void vas_unregister_coproc_api(void);
> +
>  #endif /* __ASM_POWERPC_VAS_H */
> diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile
> index 395789f..fe3f0fb 100644
> --- a/arch/powerpc/platforms/powernv/Makefile
> +++ b/arch/powerpc/platforms/powernv/Makefile
> @@ -17,7 +17,7 @@ obj-$(CONFIG_MEMORY_FAILURE)	+= opal-memory-errors.o
>  obj-$(CONFIG_OPAL_PRD)	+= opal-prd.o
>  obj-$(CONFIG_PERF_EVENTS) += opal-imc.o
>  obj-$(CONFIG_PPC_MEMTRACE)	+= memtrace.o
> -obj-$(CONFIG_PPC_VAS)	+= vas.o vas-window.o vas-debug.o vas-fault.o
> +obj-$(CONFIG_PPC_VAS)	+= vas.o vas-window.o vas-debug.o vas-fault.o vas-api.o
>  obj-$(CONFIG_OCXL_BASE)	+= ocxl.o
>  obj-$(CONFIG_SCOM_DEBUGFS) += opal-xscom.o
>  obj-$(CONFIG_PPC_SECURE_BOOT) += opal-secvar.o
> diff --git a/arch/powerpc/platforms/powernv/vas-api.c b/arch/powerpc/platforms/powernv/vas-api.c
> new file mode 100644
> index 0000000..3473a4a
> --- /dev/null
> +++ b/arch/powerpc/platforms/powernv/vas-api.c
> @@ -0,0 +1,290 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * VAS user space API for its accelerators (Only NX-GZIP is supported now)
> + * Copyright (C) 2019 Haren Myneni, IBM Corp
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/device.h>
> +#include <linux/cdev.h>
> +#include <linux/fs.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +#include <asm/vas.h>
> +#include <uapi/asm/vas-api.h>
> +#include "vas.h"
> +
> +/*
> + * The driver creates the device node that can be used as follows:
> + * For NX-GZIP
> + *
> + *	fd = open("/dev/crypto/nx-gzip", O_RDWR);
> + *	rc = ioctl(fd, VAS_TX_WIN_OPEN, &attr);
> + *	paste_addr = mmap(NULL, PAGE_SIZE, prot, MAP_SHARED, fd, 0ULL).
> + *	vas_copy(&crb, 0, 1);
> + *	vas_paste(paste_addr, 0, 1);
> + *	close(fd) or exit process to close window.
> + *
> + * where "vas_copy" and "vas_paste" are defined in copy-paste.h.
> + * copy/paste returns to the user space directly. So refer NX hardware
> + * documententation for excat copy/paste usage and completion / error

s/excat/exact/

I'm still experimenting with this so I might have more comments later,
but I wanted to send this before I forgot or lost it :)

Daniel

> + * conditions.
> + */
> +
> +static char	*coproc_dev_name = "nx-gzip";
> +static atomic_t	coproc_instid = ATOMIC_INIT(0);
> +
> +/*
> + * Wrapper object for the nx-gzip device - there is just one instance of
> + * this node for the whole system.
> + */
> +static struct coproc_dev {
> +	struct cdev cdev;
> +	struct device *device;
> +	char *name;
> +	dev_t devt;
> +	struct class *class;
> +} coproc_device;
> +
> +/*
> + * One instance per open of a nx-gzip device. Each coproc_instance is
> + * associated with a VAS window after the caller issues
> + * VAS_GZIP_TX_WIN_OPEN ioctl.
> + */
> +struct coproc_instance {
> +	int id;
> +	struct vas_window *txwin;
> +};
> +
> +static char *coproc_devnode(struct device *dev, umode_t *mode)
> +{
> +	return kasprintf(GFP_KERNEL, "crypto/%s", dev_name(dev));
> +}
> +
> +static int coproc_open(struct inode *inode, struct file *fp)
> +{
> +	struct coproc_instance *instance;
> +
> +	instance = kzalloc(sizeof(*instance), GFP_KERNEL);
> +	if (!instance)
> +		return -ENOMEM;
> +
> +	instance->id = atomic_inc_return(&coproc_instid);
> +
> +	fp->private_data = instance;
> +	return 0;
> +}
> +
> +static int coproc_ioc_tx_win_open(struct file *fp, unsigned long arg)
> +{
> +	int rc, vasid;
> +	struct vas_tx_win_attr txattr;
> +	struct vas_tx_win_open_attr uattr;
> +	void __user *uptr = (void __user *)arg;
> +	struct vas_window *txwin;
> +	struct coproc_instance *nxti = fp->private_data;
> +
> +	if (!nxti)
> +		return -EINVAL;
> +
> +	/*
> +	 * One window for file descriptor
> +	 */
> +	if (nxti->txwin)
> +		return -EEXIST;
> +
> +	rc = copy_from_user(&uattr, uptr, sizeof(uattr));
> +	if (rc) {
> +		pr_err("%s(): copy_from_user() returns %d\n", __func__, rc);
> +		return -EFAULT;
> +	}
> +
> +	if (uattr.version != 1) {
> +		pr_err("Invalid version\n");
> +		return -EINVAL;
> +	}
> +
> +	vasid = uattr.vas_id;
> +
> +	memset(&txattr, 0, sizeof(struct vas_tx_win_attr));
> +	vas_init_tx_win_attr(&txattr, VAS_COP_TYPE_GZIP);
> +
> +	txattr.lpid = mfspr(SPRN_LPID);
> +	txattr.pidr = mfspr(SPRN_PID);
> +	txattr.user_win = true;
> +	txattr.rsvd_txbuf_count = false;
> +	txattr.pswid = false;
> +	/*
> +	 * txattr.wcreds_max is set to VAS_WCREDS_DEFAULT (1024) in
> +	 * vas-window.c, but can be changed specific to GZIP depends
> +	 * on user space need.
> +	 * If needed to set txattr.wcreds_max here.
> +	 */
> +
> +	pr_devel("Pid %d: Opening txwin, PIDR %ld\n", txattr.pidr,
> +				mfspr(SPRN_PID));
> +
> +	txwin = vas_tx_win_open(vasid, VAS_COP_TYPE_GZIP, &txattr);
> +	if (IS_ERR(txwin)) {
> +		pr_err("%s() vas_tx_win_open() failed, %ld\n", __func__,
> +					PTR_ERR(txwin));
> +		return PTR_ERR(txwin);
> +	}
> +
> +	nxti->txwin = txwin;
> +
> +	return 0;
> +}
> +
> +static int coproc_release(struct inode *inode, struct file *fp)
> +{
> +	struct coproc_instance *instance;
> +
> +	instance = fp->private_data;
> +
> +	if (instance && instance->txwin) {
> +		vas_win_close(instance->txwin);
> +		instance->txwin = NULL;
> +	}
> +
> +	/*
> +	 * We don't know here if user has other receive windows
> +	 * open, so we can't really call clear_thread_tidr().
> +	 * So, once the process calls set_thread_tidr(), the
> +	 * TIDR value sticks around until process exits, resulting
> +	 * in an extra copy in restore_sprs().
> +	 */
> +
> +	kfree(instance);
> +	fp->private_data = NULL;
> +	atomic_dec(&coproc_instid);
> +
> +	return 0;
> +}
> +
> +static int coproc_mmap(struct file *fp, struct vm_area_struct *vma)
> +{
> +	int rc;
> +	pgprot_t prot;
> +	u64 paste_addr;
> +	unsigned long pfn;
> +	struct coproc_instance *instance = fp->private_data;
> +
> +	if ((vma->vm_end - vma->vm_start) > PAGE_SIZE) {
> +		pr_debug("%s(): size 0x%zx, PAGE_SIZE 0x%zx\n", __func__,
> +				(vma->vm_end - vma->vm_start), PAGE_SIZE);
> +		return -EINVAL;
> +	}
> +
> +	/* Ensure instance has an open send window */
> +	if (!instance->txwin) {
> +		pr_err("%s(): No send window open?\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	vas_win_paste_addr(instance->txwin, &paste_addr, NULL);
> +	pfn = paste_addr >> PAGE_SHIFT;
> +
> +	/* flags, page_prot from cxl_mmap(), except we want cachable */
> +	vma->vm_flags |= VM_IO | VM_PFNMAP;
> +	vma->vm_page_prot = pgprot_cached(vma->vm_page_prot);
> +
> +	prot = __pgprot(pgprot_val(vma->vm_page_prot) | _PAGE_DIRTY);
> +
> +	rc = remap_pfn_range(vma, vma->vm_start, pfn + vma->vm_pgoff,
> +			vma->vm_end - vma->vm_start, prot);
> +
> +	pr_devel("%s(): paste addr %llx at %lx, rc %d\n", __func__,
> +			paste_addr, vma->vm_start, rc);
> +
> +	return rc;
> +}
> +
> +static long coproc_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
> +{
> +	switch (cmd) {
> +	case VAS_TX_WIN_OPEN:
> +		return coproc_ioc_tx_win_open(fp, arg);
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static struct file_operations coproc_fops = {
> +	.open = coproc_open,
> +	.release = coproc_release,
> +	.mmap = coproc_mmap,
> +	.unlocked_ioctl = coproc_ioctl,
> +};
> +
> +/*
> + * Supporting only nx-gzip coprocessor type now, but this API code
> + * extended to other coprocessor types later.
> + */
> +int vas_register_coproc_api(struct module *mod)
> +{
> +	int rc = -EINVAL;
> +	dev_t devno;
> +
> +	rc = alloc_chrdev_region(&coproc_device.devt, 1, 1, "nx-gzip");
> +	if (rc) {
> +		pr_err("Unable to allocate coproc major number: %i\n", rc);
> +		return rc;
> +	}
> +
> +	pr_devel("NX-GZIP device allocated, dev [%i,%i]\n",
> +			MAJOR(coproc_device.devt), MINOR(coproc_device.devt));
> +
> +	coproc_device.class = class_create(mod, "nx-gzip");
> +	if (IS_ERR(coproc_device.class)) {
> +		rc = PTR_ERR(coproc_device.class);
> +		pr_err("Unable to create NX-GZIP class %d\n", rc);
> +		goto err_class;
> +	}
> +	coproc_device.class->devnode = coproc_devnode;
> +
> +	coproc_fops.owner = mod;
> +	cdev_init(&coproc_device.cdev, &coproc_fops);
> +
> +	devno = MKDEV(MAJOR(coproc_device.devt), 0);
> +	rc = cdev_add(&coproc_device.cdev, devno, 1);
> +	if (rc) {
> +		pr_err("cdev_add() failed %d\n", rc);
> +		goto err_cdev;
> +	}
> +
> +	coproc_device.device = device_create(coproc_device.class, NULL,
> +			devno, NULL, coproc_dev_name, MINOR(devno));
> +	if (IS_ERR(coproc_device.device)) {
> +		rc = PTR_ERR(coproc_device.device);
> +		pr_err("Unable to create coproc-%d %d\n", MINOR(devno), rc);
> +		goto err;
> +	}
> +
> +	pr_devel("%s: Added dev [%d,%d]\n", __func__, MAJOR(devno),
> +			MINOR(devno));
> +
> +	return 0;
> +
> +err:
> +	cdev_del(&coproc_device.cdev);
> +err_cdev:
> +	class_destroy(coproc_device.class);
> +err_class:
> +	unregister_chrdev_region(coproc_device.devt, 1);
> +	return rc;
> +}
> +EXPORT_SYMBOL_GPL(vas_register_coproc_api);
> +
> +void vas_unregister_coproc_api(void)
> +{
> +	dev_t devno;
> +
> +	cdev_del(&coproc_device.cdev);
> +	devno = MKDEV(MAJOR(coproc_device.devt), 0);
> +	device_destroy(coproc_device.class, devno);
> +
> +	class_destroy(coproc_device.class);
> +	unregister_chrdev_region(coproc_device.devt, 1);
> +}
> +EXPORT_SYMBOL_GPL(vas_unregister_coproc_api);
> diff --git a/arch/powerpc/platforms/powernv/vas-window.c b/arch/powerpc/platforms/powernv/vas-window.c
> index e9ab851..7484296 100644
> --- a/arch/powerpc/platforms/powernv/vas-window.c
> +++ b/arch/powerpc/platforms/powernv/vas-window.c
> @@ -26,7 +26,7 @@
>   * Compute the paste address region for the window @window using the
>   * ->paste_base_addr and ->paste_win_id_shift we got from device tree.
>   */
> -static void compute_paste_address(struct vas_window *window, u64 *addr, int *len)
> +void vas_win_paste_addr(struct vas_window *window, u64 *addr, int *len)
>  {
>  	int winid;
>  	u64 base, shift;
> @@ -80,7 +80,7 @@ static void *map_paste_region(struct vas_window *txwin)
>  		goto free_name;
>  
>  	txwin->paste_addr_name = name;
> -	compute_paste_address(txwin, &start, &len);
> +	vas_win_paste_addr(txwin, &start, &len);
>  
>  	if (!request_mem_region(start, len, name)) {
>  		pr_devel("%s(): request_mem_region(0x%llx, %d) failed\n",
> @@ -138,7 +138,7 @@ static void unmap_paste_region(struct vas_window *window)
>  	u64 busaddr_start;
>  
>  	if (window->paste_kaddr) {
> -		compute_paste_address(window, &busaddr_start, &len);
> +		vas_win_paste_addr(window, &busaddr_start, &len);
>  		unmap_region(window->paste_kaddr, busaddr_start, len);
>  		window->paste_kaddr = NULL;
>  		kfree(window->paste_addr_name);
> diff --git a/arch/powerpc/platforms/powernv/vas.h b/arch/powerpc/platforms/powernv/vas.h
> index 8c39a7d..a10abed 100644
> --- a/arch/powerpc/platforms/powernv/vas.h
> +++ b/arch/powerpc/platforms/powernv/vas.h
> @@ -431,6 +431,8 @@ struct vas_winctx {
>  extern void vas_return_credit(struct vas_window *window, bool tx);
>  extern struct vas_window *vas_pswid_to_window(struct vas_instance *vinst,
>  						uint32_t pswid);
> +extern void vas_win_paste_addr(struct vas_window *window, u64 *addr,
> +					int *len);
>  
>  static inline int vas_window_pid(struct vas_window *window)
>  {
> -- 
> 1.8.3.1

^ permalink raw reply

* Re: [PATCH 07/15] powerpc/watchpoint: Get watchpoint count dynamically while disabling them
From: Ravi Bangoria @ 2020-03-18  6:57 UTC (permalink / raw)
  To: Christophe Leroy
  Cc: apopple, mikey, Ravi Bangoria, peterz, oleg, npiggin,
	linux-kernel, paulus, jolsa, fweisbec, naveen.n.rao, linuxppc-dev,
	mingo
In-Reply-To: <c73b77fd-b983-2c5c-75bb-4b2f47a94d92@c-s.fr>



On 3/17/20 4:02 PM, Christophe Leroy wrote:
> 
> 
> Le 09/03/2020 à 09:57, Ravi Bangoria a écrit :
>> Instead of disabling only one watchpooint, get num of available
>> watchpoints dynamically and disable all of them.
>>
>> Signed-off-by: Ravi Bangoria <ravi.bangoria@linux.ibm.com>
>> ---
>>   arch/powerpc/include/asm/hw_breakpoint.h | 15 +++++++--------
>>   1 file changed, 7 insertions(+), 8 deletions(-)
>>
>> diff --git a/arch/powerpc/include/asm/hw_breakpoint.h b/arch/powerpc/include/asm/hw_breakpoint.h
>> index 980ac7d9f267..ec61e2b7195c 100644
>> --- a/arch/powerpc/include/asm/hw_breakpoint.h
>> +++ b/arch/powerpc/include/asm/hw_breakpoint.h
>> @@ -75,14 +75,13 @@ extern void ptrace_triggered(struct perf_event *bp,
>>               struct perf_sample_data *data, struct pt_regs *regs);
>>   static inline void hw_breakpoint_disable(void)
>>   {
>> -    struct arch_hw_breakpoint brk;
>> -
>> -    brk.address = 0;
>> -    brk.type = 0;
>> -    brk.len = 0;
>> -    brk.hw_len = 0;
>> -    if (ppc_breakpoint_available())
>> -        __set_breakpoint(&brk, 0);
>> +    int i;
>> +    struct arch_hw_breakpoint null_brk = {0};
>> +
>> +    if (ppc_breakpoint_available()) {
> 
> I think this test should go into nr_wp_slots() which should return zero when no breakpoint is available.

Seems possible. Will change it in next version.

Thanks,
Ravi


^ permalink raw reply

* Re: [PATCH 5/5] selftests/powerpc: Add README for GZIP engine tests
From: Daniel Axtens @ 2020-03-18  6:40 UTC (permalink / raw)
  To: Raphael Moreira Zinsly, linuxppc-dev, linux-crypto
  Cc: abali, haren, herbert, Raphael Moreira Zinsly
In-Reply-To: <20200316180714.18631-6-rzinsly@linux.ibm.com>

This is a good readme, the instructions for compiling and testing work.

Reviewed-by: Daniel Axtens <dja@axtens.net>

Regards,
Daniel

Raphael Moreira Zinsly <rzinsly@linux.ibm.com> writes:

> Include a README file with the instructions to use the
> testcases at selftests/powerpc/nx-gzip.
>
> Signed-off-by: Bulent Abali <abali@us.ibm.com>
> Signed-off-by: Raphael Moreira Zinsly <rzinsly@linux.ibm.com>
> ---
>  .../powerpc/nx-gzip/99-nx-gzip.rules          |  1 +
>  .../testing/selftests/powerpc/nx-gzip/README  | 44 +++++++++++++++++++
>  2 files changed, 45 insertions(+)
>  create mode 100644 tools/testing/selftests/powerpc/nx-gzip/99-nx-gzip.rules
>  create mode 100644 tools/testing/selftests/powerpc/nx-gzip/README
>
> diff --git a/tools/testing/selftests/powerpc/nx-gzip/99-nx-gzip.rules b/tools/testing/selftests/powerpc/nx-gzip/99-nx-gzip.rules
> new file mode 100644
> index 000000000000..5a7118495cb3
> --- /dev/null
> +++ b/tools/testing/selftests/powerpc/nx-gzip/99-nx-gzip.rules
> @@ -0,0 +1 @@
> +SUBSYSTEM=="nxgzip", KERNEL=="nx-gzip", MODE="0666"
> diff --git a/tools/testing/selftests/powerpc/nx-gzip/README b/tools/testing/selftests/powerpc/nx-gzip/README
> new file mode 100644
> index 000000000000..ff0c817a65c5
> --- /dev/null
> +++ b/tools/testing/selftests/powerpc/nx-gzip/README
> @@ -0,0 +1,44 @@
> +Test the nx-gzip function:
> +=========================
> +
> +Verify that following device exists:
> +  /dev/crypto/nx-gzip
> +If you get a permission error run as sudo or set the device permissions:
> +   sudo chmod go+rw /dev/crypto/nx-gzip
> +However, chmod may not survive across boots. You may create a udev file such
> +as:
> +   /etc/udev/rules.d/99-nx-gzip.rules
> +
> +
> +Then make and run:
> +$ make
> +gcc -O3 -I./inc -o gzfht_test gzfht_test.c gzip_vas.c
> +gcc -O3 -I./inc -o gunz_test gunz_test.c gzip_vas.c
> +
> +
> +Compress any file using Fixed Huffman mode. Output will have a .nx.gz suffix:
> +$ ./gzfht_test gzip_vas.c
> +file gzip_vas.c read, 5276 bytes
> +compressed 5276 to 2564 bytes total, crc32 checksum = b937a37d
> +
> +
> +Uncompress the previous output. Output will have a .nx.gunzip suffix:
> +$ ./gunz_test gzip_vas.c.nx.gz
> +gzHeader FLG 0
> +00 00 00 00 04 03
> +gzHeader MTIME, XFL, OS ignored
> +computed checksum b937a37d isize 0000149c
> +stored   checksum b937a37d isize 0000149c
> +decomp is complete: fclose
> +
> +
> +Compare two files:
> +$ sha1sum gzip_vas.c.nx.gz.nx.gunzip gzip_vas.c
> +f041cd8581e8d920f79f6ce7f65411be5d026c2a  gzip_vas.c.nx.gz.nx.gunzip
> +f041cd8581e8d920f79f6ce7f65411be5d026c2a  gzip_vas.c
> +
> +
> +Note that the code here are intended for testing the nx-gzip hardware function.
> +They are not intended for demonstrating performance or compression ratio.
> +For more information and source code consider using:
> +https://github.com/libnxz/power-gzip
> -- 
> 2.21.0

^ permalink raw reply

* Re: [PATCH 05/15] powerpc/watchpoint: Provide DAWR number to set_dawr
From: Ravi Bangoria @ 2020-03-18  6:18 UTC (permalink / raw)
  To: Christophe Leroy
  Cc: apopple, mikey, Ravi Bangoria, peterz, oleg, npiggin,
	linux-kernel, paulus, jolsa, fweisbec, naveen.n.rao, linuxppc-dev,
	mingo
In-Reply-To: <4704ba5d-2bc3-38f6-8097-b8a850592461@c-s.fr>



On 3/17/20 3:58 PM, Christophe Leroy wrote:
> 
> 
> Le 09/03/2020 à 09:57, Ravi Bangoria a écrit :
>> Introduce new parameter 'nr' to set_dawr() which indicates which DAWR
>> should be programed.
> 
> While we are at it (In another patch I think), we should do the same to set_dabr() so that we can use both DABR and DABR2

This series is for DAWR only and does not support DABR2. I'll look
at how other book3s family processors provides DABR2 supports.

Thanks,
Ravi


^ permalink raw reply

* Re: [PATCH 4/5] selftests/powerpc: Add NX-GZIP engine decompress testcase
From: Daniel Axtens @ 2020-03-18  6:18 UTC (permalink / raw)
  To: Raphael Moreira Zinsly, linuxppc-dev, linux-crypto
  Cc: abali, haren, herbert, Raphael Moreira Zinsly
In-Reply-To: <20200316180714.18631-5-rzinsly@linux.ibm.com>

[-- Attachment #1: Type: text/plain, Size: 2501 bytes --]

Raphael Moreira Zinsly <rzinsly@linux.ibm.com> writes:

> Include a decompression testcase for the powerpc NX-GZIP
> engine.

I compiled gzip with the AFL++ fuzzer and generated a corpus of tests to
run against this decompressor. I also fuzzed the decompressor
directly. I found a few issues. I _think_ they're just in the userspace
but I'm a bit too early in the process to know.

I realise this is self-test code but:
a) it stops me testing more deeply, and
b) it looks like some of this code is shared with https://github.com/libnxz/power-gzip/

The issues I've found are:

1) In the ERR_NX_DATA_LENGTH case, the decompressor doesn't check that
   you're making forward progress, so you can provoke it into an
   infinite loop.

Here's an _extremely_ ugly fix:

diff --git a/tools/testing/selftests/powerpc/nx-gzip/gunz_test.c b/tools/testing/selftests/powerpc/nx-gzip/gunz_test.c
index 653de92698cc..236a1f567656 100644
--- a/tools/testing/selftests/powerpc/nx-gzip/gunz_test.c
+++ b/tools/testing/selftests/powerpc/nx-gzip/gunz_test.c
@@ -343,6 +343,8 @@ int decompress_file(int argc, char **argv, void *devhandle)
        nx_dde_t dde_out[6] __attribute__((aligned (128)));
        int pgfault_retries;
 
+       int last_first_used = 0;
+
        /* when using mmap'ed files */
        off_t input_file_offset;
 
@@ -642,6 +644,11 @@ int decompress_file(int argc, char **argv, void *devhandle)
        first_used = fifo_used_first_bytes(cur_in, used_in, fifo_in_len);
        last_used = fifo_used_last_bytes(cur_in, used_in, fifo_in_len);
 
+       if (first_used > 0 && last_first_used > 0) {
+               assert(first_used != last_first_used);
+       }
+       last_first_used = first_used;
+
        if (first_used > 0)
                nx_append_dde(ddl_in, fifo_in + cur_in, first_used);
 

2) It looks like you can provoke an out-of-bounds write. I've seen both
infinte loops printing something that seems to come from the file
content like:

57201: Got signal 11 si_code 3, si_addr 0xcacacacacacacac8

or a less bizzare address like

19285: Got signal 11 si_code 1, si_addr 0x7fffcf1b0000

Depending on the build I've also seen the stack smasher protection fire.

I don't understand the code well enough to figure out how this comes to
be just yet.

I've included a few test cases as attachments. I've preconverted them
with xxd to avoid anything that might flag suspicious gzip files!
Decompress them then use `xxd -r attachment testcase.gz` to convert them
back.

Regards,
Daniel


[-- Attachment #2: infloop.bz2 --]
[-- Type: application/octet-stream, Size: 79 bytes --]

[-- Attachment #3: sig1.bz2 --]
[-- Type: application/octet-stream, Size: 6100 bytes --]

[-- Attachment #4: sig676767.bz2 --]
[-- Type: application/octet-stream, Size: 1632 bytes --]

[-- Attachment #5: sigededed.bz2 --]
[-- Type: application/octet-stream, Size: 7267 bytes --]

[-- Attachment #6: Type: text/plain, Size: 33293 bytes --]



>
> Signed-off-by: Bulent Abali <abali@us.ibm.com>
> Signed-off-by: Raphael Moreira Zinsly <rzinsly@linux.ibm.com>
> ---
>  .../selftests/powerpc/nx-gzip/Makefile        |    7 +-
>  .../selftests/powerpc/nx-gzip/gunz_test.c     | 1058 +++++++++++++++++
>  2 files changed, 1062 insertions(+), 3 deletions(-)
>  create mode 100644 tools/testing/selftests/powerpc/nx-gzip/gunz_test.c
>
> diff --git a/tools/testing/selftests/powerpc/nx-gzip/Makefile b/tools/testing/selftests/powerpc/nx-gzip/Makefile
> index ab903f63bbbd..82abc19a49a0 100644
> --- a/tools/testing/selftests/powerpc/nx-gzip/Makefile
> +++ b/tools/testing/selftests/powerpc/nx-gzip/Makefile
> @@ -1,9 +1,9 @@
>  CC = gcc
>  CFLAGS = -O3
>  INC = ./inc
> -SRC = gzfht_test.c
> +SRC = gzfht_test.c gunz_test.c
>  OBJ = $(SRC:.c=.o)
> -TESTS = gzfht_test
> +TESTS = gzfht_test gunz_test
>  EXTRA_SOURCES = gzip_vas.c
>  
>  all:	$(TESTS)
> @@ -16,6 +16,7 @@ $(TESTS): $(OBJ)
>  
>  run_tests: $(TESTS)
>  	./gzfht_test gzip_vas.c
> +	./gunz_test gzip_vas.c.nx.gz
>  
>  clean:
> -	rm -f $(TESTS) *.o *~ *.gz
> +	rm -f $(TESTS) *.o *~ *.gz *.gunzip
> diff --git a/tools/testing/selftests/powerpc/nx-gzip/gunz_test.c b/tools/testing/selftests/powerpc/nx-gzip/gunz_test.c
> new file mode 100644
> index 000000000000..653de92698cc
> --- /dev/null
> +++ b/tools/testing/selftests/powerpc/nx-gzip/gunz_test.c
> @@ -0,0 +1,1058 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * P9 gunzip sample code for demonstrating the P9 NX hardware
> + * interface.  Not intended for productive uses or for performance or
> + * compression ratio measurements.  Note also that /dev/crypto/gzip,
> + * VAS and skiboot support are required
> + *
> + * Copyright 2020 IBM Corp.
> + *
> + * Author: Bulent Abali <abali@us.ibm.com>
> + *
> + * https://github.com/libnxz/power-gzip for zlib api and other utils
> + * Definitions of acronyms used here.  See
> + * P9 NX Gzip Accelerator User's Manual for details
> + *
> + * adler/crc: 32 bit checksums appended to stream tail
> + * ce:       completion extension
> + * cpb:      coprocessor parameter block (metadata)
> + * crb:      coprocessor request block (command)
> + * csb:      coprocessor status block (status)
> + * dht:      dynamic huffman table
> + * dde:      data descriptor element (address, length)
> + * ddl:      list of ddes
> + * dh/fh:    dynamic and fixed huffman types
> + * fc:       coprocessor function code
> + * histlen:  history/dictionary length
> + * history:  sliding window of up to 32KB of data
> + * lzcount:  Deflate LZ symbol counts
> + * rembytecnt: remaining byte count
> + * sfbt:     source final block type; last block's type during decomp
> + * spbc:     source processed byte count
> + * subc:     source unprocessed bit count
> + * tebc:     target ending bit count; valid bits in the last byte
> + * tpbc:     target processed byte count
> + * vas:      virtual accelerator switch; the user mode interface
> + */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <stdint.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <sys/time.h>
> +#include <sys/fcntl.h>
> +#include <sys/mman.h>
> +#include <endian.h>
> +#include <bits/endian.h>
> +#include <sys/ioctl.h>
> +#include <assert.h>
> +#include <errno.h>
> +#include <signal.h>
> +#include "nxu.h"
> +#include "nx.h"
> +
> +int nx_dbg = 0;
> +FILE *nx_gzip_log = NULL;
> +
> +#define NX_MIN(X, Y) (((X) < (Y))?(X):(Y))
> +#define NX_MAX(X, Y) (((X) > (Y))?(X):(Y))
> +
> +#define mb()     asm volatile("sync" ::: "memory")
> +#define rmb()    asm volatile("lwsync" ::: "memory")
> +#define wmb()    rmb()
> +
> +const int fifo_in_len = 1<<24;
> +const int fifo_out_len = 1<<24;
> +const int page_sz = 1<<16;
> +const int line_sz = 1<<7;
> +const int window_max = 1<<15;
> +const int retry_max = 50;
> +
> +extern void *nx_fault_storage_address;
> +extern void *nx_function_begin(int function, int pri);
> +extern int nx_function_end(void *handle);
> +
> +/*
> + * Fault in pages prior to NX job submission.  wr=1 may be required to
> + * touch writeable pages.  System zero pages do not fault-in the page as
> + * intended.  Typically set wr=1 for NX target pages and set wr=0 for
> + * NX source pages.
> + */
> +static int nx_touch_pages(void *buf, long buf_len, long page_len, int wr)
> +{
> +	char *begin = buf;
> +	char *end = (char *) buf + buf_len - 1;
> +	volatile char t;
> +
> +	assert(buf_len >= 0 && !!buf);
> +
> +	NXPRT(fprintf(stderr, "touch %p %p len 0x%lx wr=%d\n", buf,
> +			buf + buf_len, buf_len, wr));
> +
> +	if (buf_len <= 0 || buf == NULL)
> +		return -1;
> +
> +	do {
> +		t = *begin;
> +		if (wr)
> +			*begin = t;
> +		begin = begin + page_len;
> +	} while (begin < end);
> +
> +	/* When buf_sz is small or buf tail is in another page. */
> +	t = *end;
> +	if (wr)
> +		*end = t;
> +
> +	return 0;
> +}
> +
> +void sigsegv_handler(int sig, siginfo_t *info, void *ctx)
> +{
> +	fprintf(stderr, "%d: Got signal %d si_code %d, si_addr %p\n", getpid(),
> +	       sig, info->si_code, info->si_addr);
> +
> +	nx_fault_storage_address = info->si_addr;
> +}
> +
> +/*
> + * Adds an (address, len) pair to the list of ddes (ddl) and updates
> + * the base dde.  ddl[0] is the only dde in a direct dde which
> + * contains a single (addr,len) pair.  For more pairs, ddl[0] becomes
> + * the indirect (base) dde that points to a list of direct ddes.
> + * See Section 6.4 of the NX-gzip user manual for DDE description.
> + * Addr=NULL, len=0 clears the ddl[0].  Returns the total number of
> + * bytes in ddl.  Caller is responsible for allocting the array of
> + * nx_dde_t *ddl.  If N addresses are required in the scatter-gather
> + * list, the ddl array must have N+1 entries minimum.
> + */
> +static inline uint32_t nx_append_dde(nx_dde_t *ddl, void *addr, uint32_t len)
> +{
> +	uint32_t ddecnt;
> +	uint32_t bytes;
> +
> +	if (addr == NULL && len == 0) {
> +		clearp_dde(ddl);
> +		return 0;
> +	}
> +
> +	NXPRT(fprintf(stderr, "%d: nx_append_dde addr %p len %x\n", __LINE__,
> +			addr, len));
> +
> +	/* Number of ddes in the dde list ; == 0 when it is a direct dde */
> +	ddecnt = getpnn(ddl, dde_count);
> +	bytes = getp32(ddl, ddebc);
> +
> +	if (ddecnt == 0 && bytes == 0) {
> +		/* First dde is unused; make it a direct dde */
> +		bytes = len;
> +		putp32(ddl, ddebc, bytes);
> +		putp64(ddl, ddead, (uint64_t) addr);
> +	} else if (ddecnt == 0) {
> +		/* Converting direct to indirect dde
> +		 * ddl[0] becomes head dde of ddl
> +		 * copy direct to indirect first.
> +		 */
> +		ddl[1] = ddl[0];
> +
> +		/* Add the new dde next */
> +		clear_dde(ddl[2]);
> +		put32(ddl[2], ddebc, len);
> +		put64(ddl[2], ddead, (uint64_t) addr);
> +
> +		/* Ddl head points to 2 direct ddes */
> +		ddecnt = 2;
> +		putpnn(ddl, dde_count, ddecnt);
> +		bytes = bytes + len;
> +		putp32(ddl, ddebc, bytes);
> +		/* Pointer to the first direct dde */
> +		putp64(ddl, ddead, (uint64_t) &ddl[1]);
> +	} else {
> +		/* Append a dde to an existing indirect ddl */
> +		++ddecnt;
> +		clear_dde(ddl[ddecnt]);
> +		put64(ddl[ddecnt], ddead, (uint64_t) addr);
> +		put32(ddl[ddecnt], ddebc, len);
> +
> +		putpnn(ddl, dde_count, ddecnt);
> +		bytes = bytes + len;
> +		putp32(ddl, ddebc, bytes); /* byte sum of all dde */
> +	}
> +	return bytes;
> +}
> +
> +/*
> + * Touch specified number of pages represented in number bytes
> + * beginning from the first buffer in a dde list.
> + * Do not touch the pages past buf_sz-th byte's page.
> + *
> + * Set buf_sz = 0 to touch all pages described by the ddep.
> + */
> +static int nx_touch_pages_dde(nx_dde_t *ddep, long buf_sz, long page_sz,
> +				int wr)
> +{
> +	uint32_t indirect_count;
> +	uint32_t buf_len;
> +	long total;
> +	uint64_t buf_addr;
> +	nx_dde_t *dde_list;
> +	int i;
> +
> +	assert(!!ddep);
> +
> +	indirect_count = getpnn(ddep, dde_count);
> +
> +	NXPRT(fprintf(stderr, "nx_touch_pages_dde dde_count %d request len \
> +			0x%lx\n", indirect_count, buf_sz));
> +
> +	if (indirect_count == 0) {
> +		/* Direct dde */
> +		buf_len = getp32(ddep, ddebc);
> +		buf_addr = getp64(ddep, ddead);
> +
> +		NXPRT(fprintf(stderr, "touch direct ddebc 0x%x ddead %p\n",
> +				buf_len, (void *)buf_addr));
> +
> +		if (buf_sz == 0)
> +			nx_touch_pages((void *)buf_addr, buf_len, page_sz, wr);
> +		else
> +			nx_touch_pages((void *)buf_addr, NX_MIN(buf_len,
> +					buf_sz), page_sz, wr);
> +
> +		return ERR_NX_OK;
> +	}
> +
> +	/* Indirect dde */
> +	if (indirect_count > MAX_DDE_COUNT)
> +		return ERR_NX_EXCESSIVE_DDE;
> +
> +	/* First address of the list */
> +	dde_list = (nx_dde_t *) getp64(ddep, ddead);
> +
> +	if (buf_sz == 0)
> +		buf_sz = getp32(ddep, ddebc);
> +
> +	total = 0;
> +	for (i = 0; i < indirect_count; i++) {
> +		buf_len = get32(dde_list[i], ddebc);
> +		buf_addr = get64(dde_list[i], ddead);
> +		total += buf_len;
> +
> +		NXPRT(fprintf(stderr, "touch loop len 0x%x ddead %p total \
> +				0x%lx\n", buf_len, (void *)buf_addr, total));
> +
> +		/* Touching fewer pages than encoded in the ddebc */
> +		if (total > buf_sz) {
> +			buf_len = NX_MIN(buf_len, total - buf_sz);
> +			nx_touch_pages((void *)buf_addr, buf_len, page_sz, wr);
> +			NXPRT(fprintf(stderr, "touch loop break len 0x%x \
> +				      ddead %p\n", buf_len, (void *)buf_addr));
> +			break;
> +		}
> +		nx_touch_pages((void *)buf_addr, buf_len, page_sz, wr);
> +	}
> +	return ERR_NX_OK;
> +}
> +
> +/*
> + * Src and dst buffers are supplied in scatter gather lists.
> + * NX function code and other parameters supplied in cmdp.
> + */
> +static int nx_submit_job(nx_dde_t *src, nx_dde_t *dst, nx_gzip_crb_cpb_t *cmdp,
> +			 void *handle)
> +{
> +	int cc;
> +	uint64_t csbaddr;
> +
> +	memset((void *)&cmdp->crb.csb, 0, sizeof(cmdp->crb.csb));
> +
> +	cmdp->crb.source_dde = *src;
> +	cmdp->crb.target_dde = *dst;
> +
> +	/* Status, output byte count in tpbc */
> +	csbaddr = ((uint64_t) &cmdp->crb.csb) & csb_address_mask;
> +	put64(cmdp->crb, csb_address, csbaddr);
> +
> +	/* NX reports input bytes in spbc; cleared */
> +	cmdp->cpb.out_spbc_comp_wrap = 0;
> +	cmdp->cpb.out_spbc_comp_with_count = 0;
> +	cmdp->cpb.out_spbc_decomp = 0;
> +
> +	/* Clear output */
> +	put32(cmdp->cpb, out_crc, INIT_CRC);
> +	put32(cmdp->cpb, out_adler, INIT_ADLER);
> +
> +	cc = nxu_run_job(cmdp, handle);
> +
> +	if (!cc)
> +		cc = getnn(cmdp->crb.csb, csb_cc);	/* CC Table 6-8 */
> +
> +	return cc;
> +}
> +
> +/* fifo queue management */
> +#define fifo_used_bytes(used) (used)
> +#define fifo_free_bytes(used, len) ((len)-(used))
> +/* amount of free bytes in the first and last parts */
> +#define fifo_free_first_bytes(cur, used, len)  ((((cur)+(used)) <= (len)) \
> +						  ? (len)-((cur)+(used)) : 0)
> +#define fifo_free_last_bytes(cur, used, len)   ((((cur)+(used)) <= (len)) \
> +						  ? (cur) : (len)-(used))
> +/* amount of used bytes in the first and last parts */
> +#define fifo_used_first_bytes(cur, used, len)  ((((cur)+(used)) <= (len)) \
> +						  ? (used) : (len)-(cur))
> +#define fifo_used_last_bytes(cur, used, len)   ((((cur)+(used)) <= (len)) \
> +						  ? 0 : ((used)+(cur))-(len))
> +/* first and last free parts start here */
> +#define fifo_free_first_offset(cur, used)      ((cur)+(used))
> +#define fifo_free_last_offset(cur, used, len)  \
> +					   fifo_used_last_bytes(cur, used, len)
> +/* first and last used parts start here */
> +#define fifo_used_first_offset(cur)            (cur)
> +#define fifo_used_last_offset(cur)             (0)
> +
> +int decompress_file(int argc, char **argv, void *devhandle)
> +{
> +	FILE *inpf;
> +	FILE *outf;
> +
> +	int c, expect, i, cc, rc = 0;
> +	char gzfname[1024];
> +
> +	/* Queuing, file ops, byte counting */
> +	char *fifo_in, *fifo_out;
> +	int used_in, cur_in, used_out, cur_out, read_sz, n;
> +	int first_free, last_free, first_used, last_used;
> +	int first_offset, last_offset;
> +	int write_sz, free_space, source_sz;
> +	int source_sz_estimate, target_sz_estimate;
> +	uint64_t last_comp_ratio; /* 1000 max */
> +	uint64_t total_out;
> +	int is_final, is_eof;
> +
> +	/* nx hardware */
> +	int sfbt, subc, spbc, tpbc, nx_ce, fc, resuming = 0;
> +	int history_len = 0;
> +	nx_gzip_crb_cpb_t cmd, *cmdp;
> +	nx_dde_t *ddl_in;
> +	nx_dde_t dde_in[6] __attribute__((aligned (128)));
> +	nx_dde_t *ddl_out;
> +	nx_dde_t dde_out[6] __attribute__((aligned (128)));
> +	int pgfault_retries;
> +
> +	/* when using mmap'ed files */
> +	off_t input_file_offset;
> +
> +	if (argc > 2) {
> +		fprintf(stderr, "usage: %s <fname> or stdin\n", argv[0]);
> +		fprintf(stderr, "    writes to stdout or <fname>.nx.gunzip\n");
> +		return -1;
> +	}
> +
> +	if (argc == 1) {
> +		inpf = stdin;
> +		outf = stdout;
> +	} else if (argc == 2) {
> +		char w[1024];
> +		char *wp;
> +		inpf = fopen(argv[1], "r");
> +		if (inpf == NULL) {
> +			perror(argv[1]);
> +			return -1;
> +		}
> +
> +		/* Make a new file name to write to.  Ignoring '.gz' */
> +		wp = (NULL != (wp = strrchr(argv[1], '/'))) ? ++wp : argv[1];
> +		strcpy(w, wp);
> +		strcat(w, ".nx.gunzip");
> +
> +		outf = fopen(w, "w");
> +		if (outf == NULL) {
> +			perror(w);
> +			return -1;
> +		}
> +	}
> +
> +#define GETINPC(X) fgetc(X)
> +
> +	/* Decode the gzip header */
> +	c = GETINPC(inpf); expect = 0x1f; /* ID1 */
> +	if (c != expect)
> +		goto err1;
> +
> +	c = GETINPC(inpf); expect = 0x8b; /* ID2 */
> +	if (c != expect)
> +		goto err1;
> +
> +	c = GETINPC(inpf); expect = 0x08; /* CM */
> +	if (c != expect)
> +		goto err1;
> +
> +	int flg = GETINPC(inpf); /* FLG */
> +	if (flg & 0b11100000 || flg & 0b100)
> +		goto err2;
> +
> +	fprintf(stderr, "gzHeader FLG %x\n", flg);
> +
> +	/* Read 6 bytes; ignoring the MTIME, XFL, OS fields in this
> +	 * sample code.
> +	 */
> +	for (i = 0; i < 6; i++) {
> +		char tmp[10];
> +		if (EOF == (tmp[i] = GETINPC(inpf)))
> +			goto err3;
> +		fprintf(stderr, "%02x ", tmp[i]);
> +		if (i == 5)
> +			fprintf(stderr, "\n");
> +	}
> +	fprintf(stderr, "gzHeader MTIME, XFL, OS ignored\n");
> +
> +	/* FNAME */
> +	if (flg & 0b1000) {
> +		int k = 0;
> +		do {
> +			if (EOF == (c = GETINPC(inpf)))
> +				goto err3;
> +			gzfname[k++] = c;
> +		} while (c);
> +		fprintf(stderr, "gzHeader FNAME: %s\n", gzfname);
> +	}
> +
> +	/* FHCRC */
> +	if (flg & 0b10) {
> +		c = GETINPC(inpf); c = GETINPC(inpf);
> +		fprintf(stderr, "gzHeader FHCRC: ignored\n");
> +	}
> +
> +	used_in = cur_in = used_out = cur_out = 0;
> +	is_final = is_eof = 0;
> +
> +	/* Allocate one page larger to prevent page faults due to NX
> +	 * overfetching.
> +	 * Either do this (char*)(uintptr_t)aligned_alloc or use
> +	 * -std=c11 flag to make the int-to-pointer warning go away.
> +	 */
> +	assert((fifo_in  = (char *)(uintptr_t)aligned_alloc(line_sz,
> +				   fifo_in_len + page_sz)) != NULL);
> +	assert((fifo_out = (char *)(uintptr_t)aligned_alloc(line_sz,
> +				   fifo_out_len + page_sz + line_sz)) != NULL);
> +	/* Leave unused space due to history rounding rules */
> +	fifo_out = fifo_out + line_sz;
> +	nx_touch_pages(fifo_out, fifo_out_len, page_sz, 1);
> +
> +	ddl_in  = &dde_in[0];
> +	ddl_out = &dde_out[0];
> +	cmdp = &cmd;
> +	memset(&cmdp->crb, 0, sizeof(cmdp->crb));
> +
> +read_state:
> +
> +	/* Read from .gz file */
> +
> +	NXPRT(fprintf(stderr, "read_state:\n"));
> +
> +	if (is_eof != 0)
> +		goto write_state;
> +
> +	/* We read in to fifo_in in two steps: first: read in to from
> +	 * cur_in to the end of the buffer.  last: if free space wrapped
> +	 * around, read from fifo_in offset 0 to offset cur_in.
> +	 */
> +
> +	/* Reset fifo head to reduce unnecessary wrap arounds */
> +	cur_in = (used_in == 0) ? 0 : cur_in;
> +
> +	/* Free space total is reduced by a gap */
> +	free_space = NX_MAX(0, fifo_free_bytes(used_in, fifo_in_len)
> +			    - line_sz);
> +
> +	/* Free space may wrap around as first and last */
> +	first_free = fifo_free_first_bytes(cur_in, used_in, fifo_in_len);
> +	last_free  = fifo_free_last_bytes(cur_in, used_in, fifo_in_len);
> +
> +	/* Start offsets of the free memory */
> +	first_offset = fifo_free_first_offset(cur_in, used_in);
> +	last_offset  = fifo_free_last_offset(cur_in, used_in, fifo_in_len);
> +
> +	/* Reduce read_sz because of the line_sz gap */
> +	read_sz = NX_MIN(free_space, first_free);
> +	n = 0;
> +	if (read_sz > 0) {
> +		/* Read in to offset cur_in + used_in */
> +		n = fread(fifo_in + first_offset, 1, read_sz, inpf);
> +		used_in = used_in + n;
> +		free_space = free_space - n;
> +		assert(n <= read_sz);
> +		if (n != read_sz) {
> +			/* Either EOF or error; exit the read loop */
> +			is_eof = 1;
> +			goto write_state;
> +		}
> +	}
> +
> +	/* If free space wrapped around */
> +	if (last_free > 0) {
> +		/* Reduce read_sz because of the line_sz gap */
> +		read_sz = NX_MIN(free_space, last_free);
> +		n = 0;
> +		if (read_sz > 0) {
> +			n = fread(fifo_in + last_offset, 1, read_sz, inpf);
> +			used_in = used_in + n;       /* Increase used space */
> +			free_space = free_space - n; /* Decrease free space */
> +			assert(n <= read_sz);
> +			if (n != read_sz) {
> +				/* Either EOF or error; exit the read loop */
> +				is_eof = 1;
> +				goto write_state;
> +			}
> +		}
> +	}
> +
> +	/* At this point we have used_in bytes in fifo_in with the
> +	 * data head starting at cur_in and possibly wrapping around.
> +	 */
> +
> +write_state:
> +
> +	/* Write decompressed data to output file */
> +
> +	NXPRT(fprintf(stderr, "write_state:\n"));
> +
> +	if (used_out == 0)
> +		goto decomp_state;
> +
> +	/* If fifo_out has data waiting, write it out to the file to
> +	 * make free target space for the accelerator used bytes in
> +	 * the first and last parts of fifo_out.
> +	 */
> +
> +	first_used = fifo_used_first_bytes(cur_out, used_out, fifo_out_len);
> +	last_used  = fifo_used_last_bytes(cur_out, used_out, fifo_out_len);
> +
> +	write_sz = first_used;
> +
> +	n = 0;
> +	if (write_sz > 0) {
> +		n = fwrite(fifo_out + cur_out, 1, write_sz, outf);
> +		used_out = used_out - n;
> +		/* Move head of the fifo */
> +		cur_out = (cur_out + n) % fifo_out_len;
> +		assert(n <= write_sz);
> +		if (n != write_sz) {
> +			fprintf(stderr, "error: write\n");
> +			rc = -1;
> +			goto err5;
> +		}
> +	}
> +
> +	if (last_used > 0) { /* If more data available in the last part */
> +		write_sz = last_used; /* Keep it here for later */
> +		n = 0;
> +		if (write_sz > 0) {
> +			n = fwrite(fifo_out, 1, write_sz, outf);
> +			used_out = used_out - n;
> +			cur_out = (cur_out + n) % fifo_out_len;
> +			assert(n <= write_sz);
> +			if (n != write_sz) {
> +				fprintf(stderr, "error: write\n");
> +				rc = -1;
> +				goto err5;
> +			}
> +		}
> +	}
> +
> +decomp_state:
> +
> +	/* NX decompresses input data */
> +
> +	NXPRT(fprintf(stderr, "decomp_state:\n"));
> +
> +	if (is_final)
> +		goto finish_state;
> +
> +	/* Address/len lists */
> +	clearp_dde(ddl_in);
> +	clearp_dde(ddl_out);
> +
> +	/* FC, CRC, HistLen, Table 6-6 */
> +	if (resuming) {
> +		/* Resuming a partially decompressed input.
> +		 * The key to resume is supplying the 32KB
> +		 * dictionary (history) to NX, which is basically
> +		 * the last 32KB of output produced.
> +		 */
> +		fc = GZIP_FC_DECOMPRESS_RESUME;
> +
> +		cmdp->cpb.in_crc   = cmdp->cpb.out_crc;
> +		cmdp->cpb.in_adler = cmdp->cpb.out_adler;
> +
> +		/* Round up the history size to quadword.  Section 2.10 */
> +		history_len = (history_len + 15) / 16;
> +		putnn(cmdp->cpb, in_histlen, history_len);
> +		history_len = history_len * 16; /* bytes */
> +
> +		if (history_len > 0) {
> +			/* Chain in the history buffer to the DDE list */
> +			if (cur_out >= history_len) {
> +				nx_append_dde(ddl_in, fifo_out
> +					      + (cur_out - history_len),
> +					      history_len);
> +			} else {
> +				nx_append_dde(ddl_in, fifo_out
> +					      + ((fifo_out_len + cur_out)
> +					      - history_len),
> +					      history_len - cur_out);
> +				/* Up to 32KB history wraps around fifo_out */
> +				nx_append_dde(ddl_in, fifo_out, cur_out);
> +			}
> +
> +		}
> +	} else {
> +		/* First decompress job */
> +		fc = GZIP_FC_DECOMPRESS;
> +
> +		history_len = 0;
> +		/* Writing 0 clears out subc as well */
> +		cmdp->cpb.in_histlen = 0;
> +		total_out = 0;
> +
> +		put32(cmdp->cpb, in_crc, INIT_CRC);
> +		put32(cmdp->cpb, in_adler, INIT_ADLER);
> +		put32(cmdp->cpb, out_crc, INIT_CRC);
> +		put32(cmdp->cpb, out_adler, INIT_ADLER);
> +
> +		/* Assuming 10% compression ratio initially; use the
> +		 * most recently measured compression ratio as a
> +		 * heuristic to estimate the input and output
> +		 * sizes.  If we give too much input, the target buffer
> +		 * overflows and NX cycles are wasted, and then we
> +		 * must retry with smaller input size.  1000 is 100%.
> +		 */
> +		last_comp_ratio = 100UL;
> +	}
> +	cmdp->crb.gzip_fc = 0;
> +	putnn(cmdp->crb, gzip_fc, fc);
> +
> +	/*
> +	 * NX source buffers
> +	 */
> +	first_used = fifo_used_first_bytes(cur_in, used_in, fifo_in_len);
> +	last_used = fifo_used_last_bytes(cur_in, used_in, fifo_in_len);
> +
> +	if (first_used > 0)
> +		nx_append_dde(ddl_in, fifo_in + cur_in, first_used);
> +
> +	if (last_used > 0)
> +		nx_append_dde(ddl_in, fifo_in, last_used);
> +
> +	/*
> +	 * NX target buffers
> +	 */
> +	first_free = fifo_free_first_bytes(cur_out, used_out, fifo_out_len);
> +	last_free = fifo_free_last_bytes(cur_out, used_out, fifo_out_len);
> +
> +	/* Reduce output free space amount not to overwrite the history */
> +	int target_max = NX_MAX(0, fifo_free_bytes(used_out, fifo_out_len)
> +				- (1<<16));
> +
> +	NXPRT(fprintf(stderr, "target_max %d (0x%x)\n", target_max,
> +		      target_max));
> +
> +	first_free = NX_MIN(target_max, first_free);
> +	if (first_free > 0) {
> +		first_offset = fifo_free_first_offset(cur_out, used_out);
> +		nx_append_dde(ddl_out, fifo_out + first_offset, first_free);
> +	}
> +
> +	if (last_free > 0) {
> +		last_free = NX_MIN(target_max - first_free, last_free);
> +		if (last_free > 0) {
> +			last_offset = fifo_free_last_offset(cur_out, used_out,
> +							    fifo_out_len);
> +			nx_append_dde(ddl_out, fifo_out + last_offset,
> +				      last_free);
> +		}
> +	}
> +
> +	/* Target buffer size is used to limit the source data size
> +	 * based on previous measurements of compression ratio.
> +	 */
> +
> +	/* source_sz includes history */
> +	source_sz = getp32(ddl_in, ddebc);
> +	assert(source_sz > history_len);
> +	source_sz = source_sz - history_len;
> +
> +	/* Estimating how much source is needed to 3/4 fill a
> +	 * target_max size target buffer.  If we overshoot, then NX
> +	 * must repeat the job with smaller input and we waste
> +	 * bandwidth.  If we undershoot then we use more NX calls than
> +	 * necessary.
> +	 */
> +
> +	source_sz_estimate = ((uint64_t)target_max * last_comp_ratio * 3UL)
> +				/ 4000;
> +
> +	if (source_sz_estimate < source_sz) {
> +		/* Target might be small, therefore limiting the
> +		 * source data.
> +		 */
> +		source_sz = source_sz_estimate;
> +		target_sz_estimate = target_max;
> +	} else {
> +		/* Source file might be small, therefore limiting target
> +		 * touch pages to a smaller value to save processor cycles.
> +		 */
> +		target_sz_estimate = ((uint64_t)source_sz * 1000UL)
> +					/ (last_comp_ratio + 1);
> +		target_sz_estimate = NX_MIN(2 * target_sz_estimate,
> +					    target_max);
> +	}
> +
> +	source_sz = source_sz + history_len;
> +
> +	/* Some NX condition codes require submitting the NX job again.
> +	 * Kernel doesn't handle NX page faults. Expects user code to
> +	 * touch pages.
> +	 */
> +	pgfault_retries = retry_max;
> +
> +restart_nx:
> +
> +	putp32(ddl_in, ddebc, source_sz);
> +
> +	/* Fault in pages */
> +	nx_touch_pages_dde(ddl_in, 0, page_sz, 0);
> +	nx_touch_pages_dde(ddl_out, target_sz_estimate, page_sz, 1);
> +
> +	/* Send job to NX */
> +	cc = nx_submit_job(ddl_in, ddl_out, cmdp, devhandle);
> +
> +	switch (cc) {
> +
> +	case ERR_NX_TRANSLATION:
> +
> +		/* We touched the pages ahead of time.  In the most common case
> +		 * we shouldn't be here.  But may be some pages were paged out.
> +		 * Kernel should have placed the faulting address to fsaddr.
> +		 */
> +		NXPRT(fprintf(stderr, "ERR_NX_TRANSLATION %p\n",
> +			      (void *)cmdp->crb.csb.fsaddr));
> +
> +		/* Touch 1 byte, read-only  */
> +		nx_touch_pages((void *)cmdp->crb.csb.fsaddr, 1, page_sz, 0);
> +
> +		if (pgfault_retries == retry_max) {
> +			/* Try once with exact number of pages */
> +			--pgfault_retries;
> +			goto restart_nx;
> +		} else if (pgfault_retries > 0) {
> +			/* If still faulting try fewer input pages
> +			 * assuming memory outage
> +			 */
> +			if (source_sz > page_sz)
> +				source_sz = NX_MAX(source_sz / 2, page_sz);
> +			--pgfault_retries;
> +			goto restart_nx;
> +		} else {
> +			fprintf(stderr, "cannot make progress; too many page \
> +				fault retries cc= %d\n", cc);
> +			rc = -1;
> +			goto err5;
> +		}
> +
> +	case ERR_NX_DATA_LENGTH:
> +
> +		NXPRT(fprintf(stderr, "ERR_NX_DATA_LENGTH; not an error \
> +			      usually; stream may have trailing data\n"));
> +
> +		/* Not an error in the most common case; it just says
> +		 * there is trailing data that we must examine.
> +		 *
> +		 * CC=3 CE(1)=0 CE(0)=1 indicates partial completion
> +		 * Fig.6-7 and Table 6-8.
> +		 */
> +		nx_ce = get_csb_ce_ms3b(cmdp->crb.csb);
> +
> +		if (!csb_ce_termination(nx_ce) &&
> +		    csb_ce_partial_completion(nx_ce)) {
> +			/* Check CPB for more information
> +			 * spbc and tpbc are valid
> +			 */
> +			sfbt = getnn(cmdp->cpb, out_sfbt); /* Table 6-4 */
> +			subc = getnn(cmdp->cpb, out_subc); /* Table 6-4 */
> +			spbc = get32(cmdp->cpb, out_spbc_decomp);
> +			tpbc = get32(cmdp->crb.csb, tpbc);
> +			assert(target_max >= tpbc);
> +
> +			goto ok_cc3; /* not an error */
> +		} else {
> +			/* History length error when CE(1)=1 CE(0)=0. */
> +			rc = -1;
> +			fprintf(stderr, "history length error cc= %d\n", cc);
> +			goto err5;
> +		}
> +
> +	case ERR_NX_TARGET_SPACE:
> +
> +		/* Target buffer not large enough; retry smaller input
> +		 * data; give at least 1 byte.  SPBC/TPBC are not valid.
> +		 */
> +		assert(source_sz > history_len);
> +		source_sz = ((source_sz - history_len + 2) / 2) + history_len;
> +		NXPRT(fprintf(stderr, "ERR_NX_TARGET_SPACE; retry with \
> +			      smaller input data src %d hist %d\n", source_sz,
> +			      history_len));
> +		goto restart_nx;
> +
> +	case ERR_NX_OK:
> +
> +		/* This should not happen for gzip formatted data;
> +		 * we need trailing crc and isize
> +		 */
> +		fprintf(stderr, "ERR_NX_OK\n");
> +		spbc = get32(cmdp->cpb, out_spbc_decomp);
> +		tpbc = get32(cmdp->crb.csb, tpbc);
> +		assert(target_max >= tpbc);
> +		assert(spbc >= history_len);
> +		source_sz = spbc - history_len;
> +		goto offsets_state;
> +
> +	default:
> +		fprintf(stderr, "error: cc= %d\n", cc);
> +		rc = -1;
> +		goto err5;
> +	}
> +
> +ok_cc3:
> +
> +	NXPRT(fprintf(stderr, "cc3: sfbt: %x\n", sfbt));
> +
> +	assert(spbc > history_len);
> +	source_sz = spbc - history_len;
> +
> +	/* Table 6-4: Source Final Block Type (SFBT) describes the
> +	 * last processed deflate block and clues the software how to
> +	 * resume the next job.  SUBC indicates how many input bits NX
> +	 * consumed but did not process.  SPBC indicates how many
> +	 * bytes of source were given to the accelerator including
> +	 * history bytes.
> +	 */
> +
> +	switch (sfbt) {
> +		int dhtlen;
> +
> +	case 0b0000: /* Deflate final EOB received */
> +
> +		/* Calculating the checksum start position. */
> +
> +		source_sz = source_sz - subc / 8;
> +		is_final = 1;
> +		break;
> +
> +		/* Resume decompression cases are below. Basically
> +		 * indicates where NX has suspended and how to resume
> +		 * the input stream.
> +		 */
> +
> +	case 0b1000: /* Within a literal block; use rembytecount */
> +	case 0b1001: /* Within a literal block; use rembytecount; bfinal=1 */
> +
> +		/* Supply the partially processed source byte again */
> +		source_sz = source_sz - ((subc + 7) / 8);
> +
> +		/* SUBC LS 3bits: number of bits in the first source byte need
> +		 * to be processed.
> +		 * 000 means all 8 bits;  Table 6-3
> +		 * Clear subc, histlen, sfbt, rembytecnt, dhtlen
> +		 */
> +		cmdp->cpb.in_subc = 0;
> +		cmdp->cpb.in_sfbt = 0;
> +		putnn(cmdp->cpb, in_subc, subc % 8);
> +		putnn(cmdp->cpb, in_sfbt, sfbt);
> +		putnn(cmdp->cpb, in_rembytecnt, getnn(cmdp->cpb,
> +						      out_rembytecnt));
> +		break;
> +
> +	case 0b1010: /* Within a FH block; */
> +	case 0b1011: /* Within a FH block; bfinal=1 */
> +
> +		source_sz = source_sz - ((subc + 7) / 8);
> +
> +		/* Clear subc, histlen, sfbt, rembytecnt, dhtlen */
> +		cmdp->cpb.in_subc = 0;
> +		cmdp->cpb.in_sfbt = 0;
> +		putnn(cmdp->cpb, in_subc, subc % 8);
> +		putnn(cmdp->cpb, in_sfbt, sfbt);
> +		break;
> +
> +	case 0b1100: /* Within a DH block; */
> +	case 0b1101: /* Within a DH block; bfinal=1 */
> +
> +		source_sz = source_sz - ((subc + 7) / 8);
> +
> +		/* Clear subc, histlen, sfbt, rembytecnt, dhtlen */
> +		cmdp->cpb.in_subc = 0;
> +		cmdp->cpb.in_sfbt = 0;
> +		putnn(cmdp->cpb, in_subc, subc % 8);
> +		putnn(cmdp->cpb, in_sfbt, sfbt);
> +
> +		dhtlen = getnn(cmdp->cpb, out_dhtlen);
> +		putnn(cmdp->cpb, in_dhtlen, dhtlen);
> +		assert(dhtlen >= 42);
> +
> +		/* Round up to a qword */
> +		dhtlen = (dhtlen + 127) / 128;
> +
> +		while (dhtlen > 0) { /* Copy dht from cpb.out to cpb.in */
> +			--dhtlen;
> +			cmdp->cpb.in_dht[dhtlen] = cmdp->cpb.out_dht[dhtlen];
> +		}
> +		break;
> +
> +	case 0b1110: /* Within a block header; bfinal=0; */
> +		     /* Also given if source data exactly ends (SUBC=0) with
> +		      * EOB code with BFINAL=0.  Means the next byte will
> +		      * contain a block header.
> +		      */
> +	case 0b1111: /* within a block header with BFINAL=1. */
> +
> +		source_sz = source_sz - ((subc + 7) / 8);
> +
> +		/* Clear subc, histlen, sfbt, rembytecnt, dhtlen */
> +		cmdp->cpb.in_subc = 0;
> +		cmdp->cpb.in_sfbt = 0;
> +		putnn(cmdp->cpb, in_subc, subc % 8);
> +		putnn(cmdp->cpb, in_sfbt, sfbt);
> +	}
> +
> +offsets_state:
> +
> +	/* Adjust the source and target buffer offsets and lengths  */
> +
> +	NXPRT(fprintf(stderr, "offsets_state:\n"));
> +
> +	/* Delete input data from fifo_in */
> +	used_in = used_in - source_sz;
> +	cur_in = (cur_in + source_sz) % fifo_in_len;
> +	input_file_offset = input_file_offset + source_sz;
> +
> +	/* Add output data to fifo_out */
> +	used_out = used_out + tpbc;
> +
> +	assert(used_out <= fifo_out_len);
> +
> +	total_out = total_out + tpbc;
> +
> +	/* Deflate history is 32KB max.  No need to supply more
> +	 * than 32KB on a resume.
> +	 */
> +	history_len = (total_out > window_max) ? window_max : total_out;
> +
> +	/* To estimate expected expansion in the next NX job; 500 means 50%.
> +	 * Deflate best case is around 1 to 1000.
> +	 */
> +	last_comp_ratio = (1000UL * ((uint64_t)source_sz + 1))
> +			  / ((uint64_t)tpbc + 1);
> +	last_comp_ratio = NX_MAX(NX_MIN(1000UL, last_comp_ratio), 1);
> +	NXPRT(fprintf(stderr, "comp_ratio %ld source_sz %d spbc %d tpbc %d\n",
> +		      last_comp_ratio, source_sz, spbc, tpbc));
> +
> +	resuming = 1;
> +
> +finish_state:
> +
> +	NXPRT(fprintf(stderr, "finish_state:\n"));
> +
> +	if (is_final) {
> +		if (used_out)
> +			goto write_state; /* More data to write out */
> +		else if (used_in < 8) {
> +			/* Need at least 8 more bytes containing gzip crc
> +			 * and isize.
> +			 */
> +			rc = -1;
> +			goto err4;
> +		} else {
> +			/* Compare checksums and exit */
> +			int i;
> +			char tail[8];
> +			uint32_t cksum, isize;
> +			for (i = 0; i < 8; i++)
> +				tail[i] = fifo_in[(cur_in + i) % fifo_in_len];
> +			fprintf(stderr, "computed checksum %08x isize %08x\n",
> +				cmdp->cpb.out_crc, (uint32_t) (total_out
> +				% (1ULL<<32)));
> +			cksum = (tail[0] | tail[1]<<8 | tail[2]<<16
> +				| tail[3]<<24);
> +			isize = (tail[4] | tail[5]<<8 | tail[6]<<16
> +				| tail[7]<<24);
> +			fprintf(stderr, "stored   checksum %08x isize %08x\n",
> +				cksum, isize);
> +
> +			if (cksum == cmdp->cpb.out_crc && isize == (uint32_t)
> +			    (total_out % (1ULL<<32))) {
> +				rc = 0;	goto ok1;
> +			} else {
> +				rc = -1; goto err4;
> +			}
> +		}
> +	} else
> +		goto read_state;
> +
> +	return -1;
> +
> +err1:
> +	fprintf(stderr, "error: not a gzip file, expect %x, read %x\n",
> +		expect, c);
> +	return -1;
> +
> +err2:
> +	fprintf(stderr, "error: the FLG byte is wrong or not handled by this \
> +		code sample\n");
> +	return -1;
> +
> +err3:
> +	fprintf(stderr, "error: gzip header\n");
> +	return -1;
> +
> +err4:
> +	fprintf(stderr, "error: checksum\n");
> +
> +err5:
> +ok1:
> +	fprintf(stderr, "decomp is complete: fclose\n");
> +	fclose(outf);
> +
> +	return rc;
> +}
> +
> +
> +int main(int argc, char **argv)
> +{
> +	int rc;
> +	struct sigaction act;
> +	void *handle;
> +
> +	act.sa_handler = 0;
> +	act.sa_sigaction = sigsegv_handler;
> +	act.sa_flags = SA_SIGINFO;
> +	act.sa_restorer = 0;
> +	sigemptyset(&act.sa_mask);
> +	sigaction(SIGSEGV, &act, NULL);
> +
> +	handle = nx_function_begin(NX_FUNC_COMP_GZIP, 0);
> +	if (!handle) {
> +		fprintf(stderr, "Unable to init NX, errno %d\n", errno);
> +		exit(-1);
> +	}
> +
> +	rc = decompress_file(argc, argv, handle);
> +
> +	nx_function_end(handle);
> +
> +	return rc;
> +}
> -- 
> 2.21.0

^ permalink raw reply related

* [PATCHv2] selftests/powerpc: Turn off timeout setting for benchmarks, dscr, signal, tm
From: Po-Hsu Lin @ 2020-03-18  6:00 UTC (permalink / raw)
  To: linux-kselftest; +Cc: linuxppc-dev, linux-kernel, paulus, shuah

Some specific tests in powerpc can take longer than the default 45
seconds that added in commit 852c8cbf34d3 ("selftests/kselftest/runner.sh:
Add 45 second timeout per test") to run, the following test result was
collected across 2 Power8 nodes and 1 Power9 node in our pool:
  powerpc/benchmarks/futex_bench - 52s
  powerpc/dscr/dscr_sysfs_test - 116s
  powerpc/signal/signal_fuzzer - 88s
  powerpc/tm/tm_unavailable_test - 168s
  powerpc/tm/tm-poison - 240s

Thus they will fail with TIMEOUT error. Disable the timeout setting
for these sub-tests to allow them finish properly.

https://bugs.launchpad.net/bugs/1864642
Fixes: 852c8cbf34d3 ("selftests/kselftest/runner.sh: Add 45 second timeout per test")
Signed-off-by: Po-Hsu Lin <po-hsu.lin@canonical.com>
---
 tools/testing/selftests/powerpc/benchmarks/Makefile | 2 ++
 tools/testing/selftests/powerpc/benchmarks/settings | 1 +
 tools/testing/selftests/powerpc/dscr/Makefile       | 2 ++
 tools/testing/selftests/powerpc/dscr/settings       | 1 +
 tools/testing/selftests/powerpc/signal/Makefile     | 2 ++
 tools/testing/selftests/powerpc/signal/settings     | 1 +
 tools/testing/selftests/powerpc/tm/Makefile         | 2 ++
 tools/testing/selftests/powerpc/tm/settings         | 1 +
 8 files changed, 12 insertions(+)
 create mode 100644 tools/testing/selftests/powerpc/benchmarks/settings
 create mode 100644 tools/testing/selftests/powerpc/dscr/settings
 create mode 100644 tools/testing/selftests/powerpc/signal/settings
 create mode 100644 tools/testing/selftests/powerpc/tm/settings

diff --git a/tools/testing/selftests/powerpc/benchmarks/Makefile b/tools/testing/selftests/powerpc/benchmarks/Makefile
index d40300a..a32a6ab 100644
--- a/tools/testing/selftests/powerpc/benchmarks/Makefile
+++ b/tools/testing/selftests/powerpc/benchmarks/Makefile
@@ -2,6 +2,8 @@
 TEST_GEN_PROGS := gettimeofday context_switch fork mmap_bench futex_bench null_syscall
 TEST_GEN_FILES := exec_target
 
+TEST_FILES := settings
+
 CFLAGS += -O2
 
 top_srcdir = ../../../../..
diff --git a/tools/testing/selftests/powerpc/benchmarks/settings b/tools/testing/selftests/powerpc/benchmarks/settings
new file mode 100644
index 0000000..e7b9417
--- /dev/null
+++ b/tools/testing/selftests/powerpc/benchmarks/settings
@@ -0,0 +1 @@
+timeout=0
diff --git a/tools/testing/selftests/powerpc/dscr/Makefile b/tools/testing/selftests/powerpc/dscr/Makefile
index 5df4763..cfa6eed 100644
--- a/tools/testing/selftests/powerpc/dscr/Makefile
+++ b/tools/testing/selftests/powerpc/dscr/Makefile
@@ -3,6 +3,8 @@ TEST_GEN_PROGS := dscr_default_test dscr_explicit_test dscr_user_test	\
 	      dscr_inherit_test dscr_inherit_exec_test dscr_sysfs_test	\
 	      dscr_sysfs_thread_test
 
+TEST_FILES := settings
+
 top_srcdir = ../../../../..
 include ../../lib.mk
 
diff --git a/tools/testing/selftests/powerpc/dscr/settings b/tools/testing/selftests/powerpc/dscr/settings
new file mode 100644
index 0000000..e7b9417
--- /dev/null
+++ b/tools/testing/selftests/powerpc/dscr/settings
@@ -0,0 +1 @@
+timeout=0
diff --git a/tools/testing/selftests/powerpc/signal/Makefile b/tools/testing/selftests/powerpc/signal/Makefile
index 113838f..153fafc 100644
--- a/tools/testing/selftests/powerpc/signal/Makefile
+++ b/tools/testing/selftests/powerpc/signal/Makefile
@@ -5,6 +5,8 @@ CFLAGS += -maltivec
 $(OUTPUT)/signal_tm: CFLAGS += -mhtm
 $(OUTPUT)/sigfuz: CFLAGS += -pthread -m64
 
+TEST_FILES := settings
+
 top_srcdir = ../../../../..
 include ../../lib.mk
 
diff --git a/tools/testing/selftests/powerpc/signal/settings b/tools/testing/selftests/powerpc/signal/settings
new file mode 100644
index 0000000..e7b9417
--- /dev/null
+++ b/tools/testing/selftests/powerpc/signal/settings
@@ -0,0 +1 @@
+timeout=0
diff --git a/tools/testing/selftests/powerpc/tm/Makefile b/tools/testing/selftests/powerpc/tm/Makefile
index b15a1a3..7b99d09 100644
--- a/tools/testing/selftests/powerpc/tm/Makefile
+++ b/tools/testing/selftests/powerpc/tm/Makefile
@@ -7,6 +7,8 @@ TEST_GEN_PROGS := tm-resched-dscr tm-syscall tm-signal-msr-resv tm-signal-stack
 	$(SIGNAL_CONTEXT_CHK_TESTS) tm-sigreturn tm-signal-sigreturn-nt \
 	tm-signal-context-force-tm tm-poison
 
+TEST_FILES := settings
+
 top_srcdir = ../../../../..
 include ../../lib.mk
 
diff --git a/tools/testing/selftests/powerpc/tm/settings b/tools/testing/selftests/powerpc/tm/settings
new file mode 100644
index 0000000..e7b9417
--- /dev/null
+++ b/tools/testing/selftests/powerpc/tm/settings
@@ -0,0 +1 @@
+timeout=0
-- 
2.7.4

^ permalink raw reply related

* Re: [PATCH 03/15] powerpc/watchpoint: Introduce function to get nr watchpoints dynamically
From: Ravi Bangoria @ 2020-03-18  5:50 UTC (permalink / raw)
  To: Christophe Leroy
  Cc: apopple, mikey, Ravi Bangoria, peterz, oleg, npiggin,
	linux-kernel, paulus, jolsa, fweisbec, naveen.n.rao, linuxppc-dev,
	mingo
In-Reply-To: <53b8bf54-200f-6f37-5870-e641b35f373c@c-s.fr>


>> diff --git a/arch/powerpc/include/asm/hw_breakpoint.h b/arch/powerpc/include/asm/hw_breakpoint.h
>> index f2f8d8aa8e3b..741c4f7573c4 100644
>> --- a/arch/powerpc/include/asm/hw_breakpoint.h
>> +++ b/arch/powerpc/include/asm/hw_breakpoint.h
>> @@ -43,6 +43,8 @@ struct arch_hw_breakpoint {
>>   #define DABR_MAX_LEN    8
>>   #define DAWR_MAX_LEN    512
>> +extern int nr_wp_slots(void);
> 
> 'extern' keyword is unneeded and irrelevant here. Please remove it. Even checkpatch is unhappy (https://openpower.xyz/job/snowpatch/job/snowpatch-linux-checkpatch/12172//artifact/linux/checkpatch.log)

Sure.

...
>> diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
>> index 110db94cdf3c..6d4b029532e2 100644
>> --- a/arch/powerpc/kernel/process.c
>> +++ b/arch/powerpc/kernel/process.c
>> @@ -835,6 +835,12 @@ static inline bool hw_brk_match(struct arch_hw_breakpoint *a,
>>       return true;
>>   }
>> +/* Returns total number of data breakpoints available. */
>> +int nr_wp_slots(void)
>> +{
>> +    return HBP_NUM_MAX;
>> +}
>> +
> 
> This is not worth a global function. At least it should be a static function located in hw_breakpoint.c. But it would be even better to have it as a static inline in asm/hw_breakpoint.h

Makes sense. Will change it.

Thanks.


^ permalink raw reply

* Re: [PATCH] mm/hugetlb: Fix build failure with HUGETLB_PAGE but not HUGEBTLBFS
From: Andrew Morton @ 2020-03-18  5:02 UTC (permalink / raw)
  To: Christophe Leroy
  Cc: Nick Piggin, Andi Kleen, linux-kernel, linux-mm, Adam Litke,
	Nishanth Aravamudan, linuxppc-dev, Mike Kravetz
In-Reply-To: <7e8c3a3c9a587b9cd8a2f146df32a421b961f3a2.1584432148.git.christophe.leroy@c-s.fr>

On Tue, 17 Mar 2020 08:04:14 +0000 (UTC) Christophe Leroy <christophe.leroy@c-s.fr> wrote:

> When CONFIG_HUGETLB_PAGE is set but not CONFIG_HUGETLBFS, the
> following build failure is encoutered:
> 
> In file included from arch/powerpc/mm/fault.c:33:0:
> ./include/linux/hugetlb.h: In function 'hstate_inode':
> ./include/linux/hugetlb.h:477:9: error: implicit declaration of function 'HUGETLBFS_SB' [-Werror=implicit-function-declaration]
>   return HUGETLBFS_SB(i->i_sb)->hstate;
>          ^
> ./include/linux/hugetlb.h:477:30: error: invalid type argument of '->' (have 'int')
>   return HUGETLBFS_SB(i->i_sb)->hstate;
>                               ^
> 
> Gate hstate_inode() with CONFIG_HUGETLBFS instead of CONFIG_HUGETLB_PAGE.
> 
> Reported-by: kbuild test robot <lkp@intel.com>
> Link: https://patchwork.ozlabs.org/patch/1255548/#2386036
> Fixes: a137e1cc6d6e ("hugetlbfs: per mount huge page sizes")

A 12 year old build error?  If accurate, that has to be a world record.

> Cc: stable@vger.kernel.org

I think I'll remove this.  Obviously nobody is suffering from this problem!



^ permalink raw reply

* Re: Slub: Increased mem consumption on cpu,mem-less node powerpc guest
From: Bharata B Rao @ 2020-03-18  4:46 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Sachin Sant, Andrew Morton, linuxppc-dev, aneesh.kumar,
	Michal Hocko, Pekka Enberg, linux-mm, David Rientjes,
	Christoph Lameter, Joonsoo Kim, Vlastimil Babka
In-Reply-To: <20200318032044.GC4879@linux.vnet.ibm.com>

On Wed, Mar 18, 2020 at 08:50:44AM +0530, Srikar Dronamraju wrote:
> * Vlastimil Babka <vbabka@suse.cz> [2020-03-17 17:45:15]:
> 
> > On 3/17/20 5:25 PM, Srikar Dronamraju wrote:
> > > * Vlastimil Babka <vbabka@suse.cz> [2020-03-17 16:56:04]:
> > > 
> > >> 
> > >> I wonder why do you get a memory leak while Sachin in the same situation [1]
> > >> gets a crash? I don't understand anything anymore.
> > > 
> > > Sachin was testing on linux-next which has Kirill's patch which modifies
> > > slub to use kmalloc_node instead of kmalloc. While Bharata is testing on
> > > upstream, which doesn't have this. 
> > 
> > Yes, that Kirill's patch was about the memcg shrinker map allocation. But the
> > patch hunk that Bharata posted as a "hack" that fixes the problem, it follows
> > that there has to be something else that calls kmalloc_node(node) where node is
> > one that doesn't have present pages.
> > 
> > He mentions alloc_fair_sched_group() which has:
> > 
> >         for_each_possible_cpu(i) {
> >                 cfs_rq = kzalloc_node(sizeof(struct cfs_rq),
> >                                       GFP_KERNEL, cpu_to_node(i));
> > ...
> >                 se = kzalloc_node(sizeof(struct sched_entity),
> >                                   GFP_KERNEL, cpu_to_node(i));
> > 
> 
> 
> Sachin's experiment.
> Upstream-next/ memcg /
> possible nodes were 0-31
> online nodes were 0-1
> kmalloc_node called for_each_node / for_each_possible_node.
> This would crash while allocating slab from !N_ONLINE nodes.
> 
> Bharata's experiment.
> Upstream
> possible nodes were 0-1
> online nodes were 0-1
> kmalloc_node called for_each_online_node/ for_each_possible_cpu
> i.e kmalloc is called for N_ONLINE nodes.
> So wouldn't crash
> 
> Even if his possible nodes were 0-256. I don't think we have kmalloc_node
> being called in !N_ONLINE nodes. Hence its not crashing.
> If we see the above code that you quote, kzalloc_node is using cpu_to_node
> which in Bharata's case will always return 1.
> 
> 
> > I assume one of these structs is 1k and other 512 bytes (rounded) and that for
> > some possible cpu's cpu_to_node(i) will be 0, which has no present pages. And as
> > Bharata pasted, node_to_mem_node(0) = 0

Correct, these two kazalloc_node() calls for all possible cpus are
causing increased slab memory consumption in my case.

> > So this looks like the same scenario, but it doesn't crash? Is the node 0
> > actually online here, and/or does it have N_NORMAL_MEMORY state?
> 

Node 0 is online, but N_NORMAL_MEMORY state is empty. In fact memory
leak goes away if I insert the below check/assignment in the slab
alloc code path:

+       if (!node_isset(node, node_states[N_NORMAL_MEMORY]))
+               node = NUMA_NO_NODE;

Regards,
Bharata.


^ permalink raw reply

* Re: [PATCH kernel 0/5] powerpc/powenv/ioda: Allow huge DMA window at 4GB
From: Alexey Kardashevskiy @ 2020-03-18  4:31 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Alistair Popple, Alex Williamson, kvm-ppc, David Gibson
In-Reply-To: <20200218073650.16149-1-aik@ozlabs.ru>



On 18/02/2020 18:36, Alexey Kardashevskiy wrote:
> Here is an attempt to support bigger DMA space for devices
> supporting DMA masks less than 59 bits (GPUs come into mind
> first). POWER9 PHBs have an option to map 2 windows at 0
> and select a windows based on DMA address being below or above
> 4GB.
> 
> This adds the "iommu=iommu_bypass" kernel parameter and
> supports VFIO+pseries machine - current this requires telling
> upstream+unmodified QEMU about this via
> -global spapr-pci-host-bridge.dma64_win_addr=0x100000000
> or per-phb property. 4/4 advertises the new option but
> there is no automation around it in QEMU (should it be?).
> 
> For now it is either 1<<59 or 4GB mode; dynamic switching is
> not supported (could be via sysfs).
> 
> This is a rebased version of
> https://lore.kernel.org/kvm/20191202015953.127902-1-aik@ozlabs.ru/
> 
> This is based on sha1
> 71c3a888cbca Linus Torvalds "Merge tag 'powerpc-5.6-1' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux".
> 
> Please comment. Thanks.


Anyone from the POWERPC side wants to comment? Thanks,


> 
> 
> 
> Alexey Kardashevskiy (5):
>   powerpc/powernv/ioda: Move TCE bypass base to PE
>   powerpc/powernv/ioda: Rework for huge DMA window at 4GB
>   powerpc/powernv/ioda: Allow smaller TCE table levels
>   powerpc/powernv/phb4: Add 4GB IOMMU bypass mode
>   vfio/spapr_tce: Advertise and allow a huge DMA windows at 4GB
> 
>  arch/powerpc/include/asm/iommu.h              |   2 +
>  arch/powerpc/include/asm/opal-api.h           |   9 +-
>  arch/powerpc/include/asm/opal.h               |   2 +
>  arch/powerpc/platforms/powernv/pci.h          |   2 +-
>  include/uapi/linux/vfio.h                     |   2 +
>  arch/powerpc/platforms/powernv/npu-dma.c      |   1 +
>  arch/powerpc/platforms/powernv/opal-call.c    |   2 +
>  arch/powerpc/platforms/powernv/pci-ioda-tce.c |   4 +-
>  arch/powerpc/platforms/powernv/pci-ioda.c     | 229 ++++++++++++++----
>  drivers/vfio/vfio_iommu_spapr_tce.c           |  10 +-
>  10 files changed, 207 insertions(+), 56 deletions(-)
> 

-- 
Alexey

^ permalink raw reply

* Re: [PATCH 4/5] selftests/powerpc: Add NX-GZIP engine decompress testcase
From: Daniel Axtens @ 2020-03-18  4:31 UTC (permalink / raw)
  To: Raphael Moreira Zinsly, linuxppc-dev, linux-crypto
  Cc: abali, haren, herbert, Raphael Moreira Zinsly
In-Reply-To: <20200316180714.18631-5-rzinsly@linux.ibm.com>

Raphael Moreira Zinsly <rzinsly@linux.ibm.com> writes:

> Include a decompression testcase for the powerpc NX-GZIP
> engine.
>
> Signed-off-by: Bulent Abali <abali@us.ibm.com>
> Signed-off-by: Raphael Moreira Zinsly <rzinsly@linux.ibm.com>
> ---
>  .../selftests/powerpc/nx-gzip/Makefile        |    7 +-
>  .../selftests/powerpc/nx-gzip/gunz_test.c     | 1058 +++++++++++++++++
>  2 files changed, 1062 insertions(+), 3 deletions(-)
>  create mode 100644 tools/testing/selftests/powerpc/nx-gzip/gunz_test.c
>
> diff --git a/tools/testing/selftests/powerpc/nx-gzip/Makefile b/tools/testing/selftests/powerpc/nx-gzip/Makefile
> index ab903f63bbbd..82abc19a49a0 100644
> --- a/tools/testing/selftests/powerpc/nx-gzip/Makefile
> +++ b/tools/testing/selftests/powerpc/nx-gzip/Makefile
> @@ -1,9 +1,9 @@
>  CC = gcc
>  CFLAGS = -O3
>  INC = ./inc
> -SRC = gzfht_test.c
> +SRC = gzfht_test.c gunz_test.c
>  OBJ = $(SRC:.c=.o)
> -TESTS = gzfht_test
> +TESTS = gzfht_test gunz_test
>  EXTRA_SOURCES = gzip_vas.c
>  
>  all:	$(TESTS)
> @@ -16,6 +16,7 @@ $(TESTS): $(OBJ)
>  
>  run_tests: $(TESTS)
>  	./gzfht_test gzip_vas.c
> +	./gunz_test gzip_vas.c.nx.gz
>  
>  clean:
> -	rm -f $(TESTS) *.o *~ *.gz
> +	rm -f $(TESTS) *.o *~ *.gz *.gunzip
> diff --git a/tools/testing/selftests/powerpc/nx-gzip/gunz_test.c b/tools/testing/selftests/powerpc/nx-gzip/gunz_test.c
> new file mode 100644
> index 000000000000..653de92698cc
> --- /dev/null
> +++ b/tools/testing/selftests/powerpc/nx-gzip/gunz_test.c
> @@ -0,0 +1,1058 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * P9 gunzip sample code for demonstrating the P9 NX hardware
> + * interface.  Not intended for productive uses or for performance or
> + * compression ratio measurements.  Note also that /dev/crypto/gzip,
> + * VAS and skiboot support are required
> + *
> + * Copyright 2020 IBM Corp.
> + *
> + * Author: Bulent Abali <abali@us.ibm.com>
> + *
> + * https://github.com/libnxz/power-gzip for zlib api and other utils
> + * Definitions of acronyms used here.  See
> + * P9 NX Gzip Accelerator User's Manual for details
> + *
> + * adler/crc: 32 bit checksums appended to stream tail
> + * ce:       completion extension
> + * cpb:      coprocessor parameter block (metadata)
> + * crb:      coprocessor request block (command)
> + * csb:      coprocessor status block (status)
> + * dht:      dynamic huffman table
> + * dde:      data descriptor element (address, length)
> + * ddl:      list of ddes
> + * dh/fh:    dynamic and fixed huffman types
> + * fc:       coprocessor function code
> + * histlen:  history/dictionary length
> + * history:  sliding window of up to 32KB of data
> + * lzcount:  Deflate LZ symbol counts
> + * rembytecnt: remaining byte count
> + * sfbt:     source final block type; last block's type during decomp
> + * spbc:     source processed byte count
> + * subc:     source unprocessed bit count
> + * tebc:     target ending bit count; valid bits in the last byte
> + * tpbc:     target processed byte count
> + * vas:      virtual accelerator switch; the user mode interface
> + */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <stdint.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <sys/time.h>
> +#include <sys/fcntl.h>
> +#include <sys/mman.h>
> +#include <endian.h>
> +#include <bits/endian.h>
> +#include <sys/ioctl.h>
> +#include <assert.h>
> +#include <errno.h>
> +#include <signal.h>
> +#include "nxu.h"
> +#include "nx.h"
> +
> +int nx_dbg = 0;
> +FILE *nx_gzip_log = NULL;
> +
> +#define NX_MIN(X, Y) (((X) < (Y))?(X):(Y))
> +#define NX_MAX(X, Y) (((X) > (Y))?(X):(Y))
> +
> +#define mb()     asm volatile("sync" ::: "memory")
> +#define rmb()    asm volatile("lwsync" ::: "memory")
> +#define wmb()    rmb()
> +
> +const int fifo_in_len = 1<<24;
> +const int fifo_out_len = 1<<24;
> +const int page_sz = 1<<16;
> +const int line_sz = 1<<7;
> +const int window_max = 1<<15;
> +const int retry_max = 50;
> +
> +extern void *nx_fault_storage_address;
> +extern void *nx_function_begin(int function, int pri);
> +extern int nx_function_end(void *handle);
> +
> +/*
> + * Fault in pages prior to NX job submission.  wr=1 may be required to
> + * touch writeable pages.  System zero pages do not fault-in the page as
> + * intended.  Typically set wr=1 for NX target pages and set wr=0 for
> + * NX source pages.
> + */
> +static int nx_touch_pages(void *buf, long buf_len, long page_len, int wr)
> +{
> +	char *begin = buf;
> +	char *end = (char *) buf + buf_len - 1;
> +	volatile char t;
> +
> +	assert(buf_len >= 0 && !!buf);
> +
> +	NXPRT(fprintf(stderr, "touch %p %p len 0x%lx wr=%d\n", buf,
> +			buf + buf_len, buf_len, wr));
> +
> +	if (buf_len <= 0 || buf == NULL)
> +		return -1;
> +
> +	do {
> +		t = *begin;
> +		if (wr)
> +			*begin = t;
> +		begin = begin + page_len;
> +	} while (begin < end);
> +
> +	/* When buf_sz is small or buf tail is in another page. */
> +	t = *end;
> +	if (wr)
> +		*end = t;
> +
> +	return 0;
> +}
> +
> +void sigsegv_handler(int sig, siginfo_t *info, void *ctx)
> +{
> +	fprintf(stderr, "%d: Got signal %d si_code %d, si_addr %p\n", getpid(),
> +	       sig, info->si_code, info->si_addr);
> +
> +	nx_fault_storage_address = info->si_addr;
> +}
> +
> +/*
> + * Adds an (address, len) pair to the list of ddes (ddl) and updates
> + * the base dde.  ddl[0] is the only dde in a direct dde which
> + * contains a single (addr,len) pair.  For more pairs, ddl[0] becomes
> + * the indirect (base) dde that points to a list of direct ddes.
> + * See Section 6.4 of the NX-gzip user manual for DDE description.
> + * Addr=NULL, len=0 clears the ddl[0].  Returns the total number of
> + * bytes in ddl.  Caller is responsible for allocting the array of
> + * nx_dde_t *ddl.  If N addresses are required in the scatter-gather
> + * list, the ddl array must have N+1 entries minimum.
> + */
> +static inline uint32_t nx_append_dde(nx_dde_t *ddl, void *addr, uint32_t len)
> +{
> +	uint32_t ddecnt;
> +	uint32_t bytes;
> +
> +	if (addr == NULL && len == 0) {
> +		clearp_dde(ddl);
> +		return 0;
> +	}
> +
> +	NXPRT(fprintf(stderr, "%d: nx_append_dde addr %p len %x\n", __LINE__,
> +			addr, len));
> +
> +	/* Number of ddes in the dde list ; == 0 when it is a direct dde */
> +	ddecnt = getpnn(ddl, dde_count);
> +	bytes = getp32(ddl, ddebc);
> +
> +	if (ddecnt == 0 && bytes == 0) {
> +		/* First dde is unused; make it a direct dde */
> +		bytes = len;
> +		putp32(ddl, ddebc, bytes);
> +		putp64(ddl, ddead, (uint64_t) addr);
> +	} else if (ddecnt == 0) {
> +		/* Converting direct to indirect dde
> +		 * ddl[0] becomes head dde of ddl
> +		 * copy direct to indirect first.
> +		 */
> +		ddl[1] = ddl[0];
> +
> +		/* Add the new dde next */
> +		clear_dde(ddl[2]);
> +		put32(ddl[2], ddebc, len);
> +		put64(ddl[2], ddead, (uint64_t) addr);
> +
> +		/* Ddl head points to 2 direct ddes */
> +		ddecnt = 2;
> +		putpnn(ddl, dde_count, ddecnt);
> +		bytes = bytes + len;
> +		putp32(ddl, ddebc, bytes);
> +		/* Pointer to the first direct dde */
> +		putp64(ddl, ddead, (uint64_t) &ddl[1]);
> +	} else {
> +		/* Append a dde to an existing indirect ddl */
> +		++ddecnt;
> +		clear_dde(ddl[ddecnt]);
> +		put64(ddl[ddecnt], ddead, (uint64_t) addr);
> +		put32(ddl[ddecnt], ddebc, len);
> +
> +		putpnn(ddl, dde_count, ddecnt);
> +		bytes = bytes + len;
> +		putp32(ddl, ddebc, bytes); /* byte sum of all dde */
> +	}
> +	return bytes;
> +}
> +
> +/*
> + * Touch specified number of pages represented in number bytes
> + * beginning from the first buffer in a dde list.
> + * Do not touch the pages past buf_sz-th byte's page.
> + *
> + * Set buf_sz = 0 to touch all pages described by the ddep.
> + */
> +static int nx_touch_pages_dde(nx_dde_t *ddep, long buf_sz, long page_sz,
> +				int wr)
> +{
> +	uint32_t indirect_count;
> +	uint32_t buf_len;
> +	long total;
> +	uint64_t buf_addr;
> +	nx_dde_t *dde_list;
> +	int i;
> +
> +	assert(!!ddep);
> +
> +	indirect_count = getpnn(ddep, dde_count);
> +
> +	NXPRT(fprintf(stderr, "nx_touch_pages_dde dde_count %d request len \
> +			0x%lx\n", indirect_count, buf_sz));
You use \ to break a string into multiple lines throughout this test
case.

It leads to things like this being printed:

ERR_NX_DATA_LENGTH; not an error                              usually; stream may have trailing data

Notice the big chunk of whitespace in the middle.

Could you use this instead please:

+	NXPRT(fprintf(stderr, "nx_touch_pages_dde dde_count %d request len "
+			"0x%lx\n", indirect_count, buf_sz));

Regards,
Daniel

> +
> +	if (indirect_count == 0) {
> +		/* Direct dde */
> +		buf_len = getp32(ddep, ddebc);
> +		buf_addr = getp64(ddep, ddead);
> +
> +		NXPRT(fprintf(stderr, "touch direct ddebc 0x%x ddead %p\n",
> +				buf_len, (void *)buf_addr));
> +
> +		if (buf_sz == 0)
> +			nx_touch_pages((void *)buf_addr, buf_len, page_sz, wr);
> +		else
> +			nx_touch_pages((void *)buf_addr, NX_MIN(buf_len,
> +					buf_sz), page_sz, wr);
> +
> +		return ERR_NX_OK;
> +	}
> +
> +	/* Indirect dde */
> +	if (indirect_count > MAX_DDE_COUNT)
> +		return ERR_NX_EXCESSIVE_DDE;
> +
> +	/* First address of the list */
> +	dde_list = (nx_dde_t *) getp64(ddep, ddead);
> +
> +	if (buf_sz == 0)
> +		buf_sz = getp32(ddep, ddebc);
> +
> +	total = 0;
> +	for (i = 0; i < indirect_count; i++) {
> +		buf_len = get32(dde_list[i], ddebc);
> +		buf_addr = get64(dde_list[i], ddead);
> +		total += buf_len;
> +
> +		NXPRT(fprintf(stderr, "touch loop len 0x%x ddead %p total \
> +				0x%lx\n", buf_len, (void *)buf_addr, total));
> +
> +		/* Touching fewer pages than encoded in the ddebc */
> +		if (total > buf_sz) {
> +			buf_len = NX_MIN(buf_len, total - buf_sz);
> +			nx_touch_pages((void *)buf_addr, buf_len, page_sz, wr);
> +			NXPRT(fprintf(stderr, "touch loop break len 0x%x \
> +				      ddead %p\n", buf_len, (void *)buf_addr));
> +			break;
> +		}
> +		nx_touch_pages((void *)buf_addr, buf_len, page_sz, wr);
> +	}
> +	return ERR_NX_OK;
> +}
> +
> +/*
> + * Src and dst buffers are supplied in scatter gather lists.
> + * NX function code and other parameters supplied in cmdp.
> + */
> +static int nx_submit_job(nx_dde_t *src, nx_dde_t *dst, nx_gzip_crb_cpb_t *cmdp,
> +			 void *handle)
> +{
> +	int cc;
> +	uint64_t csbaddr;
> +
> +	memset((void *)&cmdp->crb.csb, 0, sizeof(cmdp->crb.csb));
> +
> +	cmdp->crb.source_dde = *src;
> +	cmdp->crb.target_dde = *dst;
> +
> +	/* Status, output byte count in tpbc */
> +	csbaddr = ((uint64_t) &cmdp->crb.csb) & csb_address_mask;
> +	put64(cmdp->crb, csb_address, csbaddr);
> +
> +	/* NX reports input bytes in spbc; cleared */
> +	cmdp->cpb.out_spbc_comp_wrap = 0;
> +	cmdp->cpb.out_spbc_comp_with_count = 0;
> +	cmdp->cpb.out_spbc_decomp = 0;
> +
> +	/* Clear output */
> +	put32(cmdp->cpb, out_crc, INIT_CRC);
> +	put32(cmdp->cpb, out_adler, INIT_ADLER);
> +
> +	cc = nxu_run_job(cmdp, handle);
> +
> +	if (!cc)
> +		cc = getnn(cmdp->crb.csb, csb_cc);	/* CC Table 6-8 */
> +
> +	return cc;
> +}
> +
> +/* fifo queue management */
> +#define fifo_used_bytes(used) (used)
> +#define fifo_free_bytes(used, len) ((len)-(used))
> +/* amount of free bytes in the first and last parts */
> +#define fifo_free_first_bytes(cur, used, len)  ((((cur)+(used)) <= (len)) \
> +						  ? (len)-((cur)+(used)) : 0)
> +#define fifo_free_last_bytes(cur, used, len)   ((((cur)+(used)) <= (len)) \
> +						  ? (cur) : (len)-(used))
> +/* amount of used bytes in the first and last parts */
> +#define fifo_used_first_bytes(cur, used, len)  ((((cur)+(used)) <= (len)) \
> +						  ? (used) : (len)-(cur))
> +#define fifo_used_last_bytes(cur, used, len)   ((((cur)+(used)) <= (len)) \
> +						  ? 0 : ((used)+(cur))-(len))
> +/* first and last free parts start here */
> +#define fifo_free_first_offset(cur, used)      ((cur)+(used))
> +#define fifo_free_last_offset(cur, used, len)  \
> +					   fifo_used_last_bytes(cur, used, len)
> +/* first and last used parts start here */
> +#define fifo_used_first_offset(cur)            (cur)
> +#define fifo_used_last_offset(cur)             (0)
> +
> +int decompress_file(int argc, char **argv, void *devhandle)
> +{
> +	FILE *inpf;
> +	FILE *outf;
> +
> +	int c, expect, i, cc, rc = 0;
> +	char gzfname[1024];
> +
> +	/* Queuing, file ops, byte counting */
> +	char *fifo_in, *fifo_out;
> +	int used_in, cur_in, used_out, cur_out, read_sz, n;
> +	int first_free, last_free, first_used, last_used;
> +	int first_offset, last_offset;
> +	int write_sz, free_space, source_sz;
> +	int source_sz_estimate, target_sz_estimate;
> +	uint64_t last_comp_ratio; /* 1000 max */
> +	uint64_t total_out;
> +	int is_final, is_eof;
> +
> +	/* nx hardware */
> +	int sfbt, subc, spbc, tpbc, nx_ce, fc, resuming = 0;
> +	int history_len = 0;
> +	nx_gzip_crb_cpb_t cmd, *cmdp;
> +	nx_dde_t *ddl_in;
> +	nx_dde_t dde_in[6] __attribute__((aligned (128)));
> +	nx_dde_t *ddl_out;
> +	nx_dde_t dde_out[6] __attribute__((aligned (128)));
> +	int pgfault_retries;
> +
> +	/* when using mmap'ed files */
> +	off_t input_file_offset;
> +
> +	if (argc > 2) {
> +		fprintf(stderr, "usage: %s <fname> or stdin\n", argv[0]);
> +		fprintf(stderr, "    writes to stdout or <fname>.nx.gunzip\n");
> +		return -1;
> +	}
> +
> +	if (argc == 1) {
> +		inpf = stdin;
> +		outf = stdout;
> +	} else if (argc == 2) {
> +		char w[1024];
> +		char *wp;
> +		inpf = fopen(argv[1], "r");
> +		if (inpf == NULL) {
> +			perror(argv[1]);
> +			return -1;
> +		}
> +
> +		/* Make a new file name to write to.  Ignoring '.gz' */
> +		wp = (NULL != (wp = strrchr(argv[1], '/'))) ? ++wp : argv[1];
> +		strcpy(w, wp);
> +		strcat(w, ".nx.gunzip");
> +
> +		outf = fopen(w, "w");
> +		if (outf == NULL) {
> +			perror(w);
> +			return -1;
> +		}
> +	}
> +
> +#define GETINPC(X) fgetc(X)
> +
> +	/* Decode the gzip header */
> +	c = GETINPC(inpf); expect = 0x1f; /* ID1 */
> +	if (c != expect)
> +		goto err1;
> +
> +	c = GETINPC(inpf); expect = 0x8b; /* ID2 */
> +	if (c != expect)
> +		goto err1;
> +
> +	c = GETINPC(inpf); expect = 0x08; /* CM */
> +	if (c != expect)
> +		goto err1;
> +
> +	int flg = GETINPC(inpf); /* FLG */
> +	if (flg & 0b11100000 || flg & 0b100)
> +		goto err2;
> +
> +	fprintf(stderr, "gzHeader FLG %x\n", flg);
> +
> +	/* Read 6 bytes; ignoring the MTIME, XFL, OS fields in this
> +	 * sample code.
> +	 */
> +	for (i = 0; i < 6; i++) {
> +		char tmp[10];
> +		if (EOF == (tmp[i] = GETINPC(inpf)))
> +			goto err3;
> +		fprintf(stderr, "%02x ", tmp[i]);
> +		if (i == 5)
> +			fprintf(stderr, "\n");
> +	}
> +	fprintf(stderr, "gzHeader MTIME, XFL, OS ignored\n");
> +
> +	/* FNAME */
> +	if (flg & 0b1000) {
> +		int k = 0;
> +		do {
> +			if (EOF == (c = GETINPC(inpf)))
> +				goto err3;
> +			gzfname[k++] = c;
> +		} while (c);
> +		fprintf(stderr, "gzHeader FNAME: %s\n", gzfname);
> +	}
> +
> +	/* FHCRC */
> +	if (flg & 0b10) {
> +		c = GETINPC(inpf); c = GETINPC(inpf);
> +		fprintf(stderr, "gzHeader FHCRC: ignored\n");
> +	}
> +
> +	used_in = cur_in = used_out = cur_out = 0;
> +	is_final = is_eof = 0;
> +
> +	/* Allocate one page larger to prevent page faults due to NX
> +	 * overfetching.
> +	 * Either do this (char*)(uintptr_t)aligned_alloc or use
> +	 * -std=c11 flag to make the int-to-pointer warning go away.
> +	 */
> +	assert((fifo_in  = (char *)(uintptr_t)aligned_alloc(line_sz,
> +				   fifo_in_len + page_sz)) != NULL);
> +	assert((fifo_out = (char *)(uintptr_t)aligned_alloc(line_sz,
> +				   fifo_out_len + page_sz + line_sz)) != NULL);
> +	/* Leave unused space due to history rounding rules */
> +	fifo_out = fifo_out + line_sz;
> +	nx_touch_pages(fifo_out, fifo_out_len, page_sz, 1);
> +
> +	ddl_in  = &dde_in[0];
> +	ddl_out = &dde_out[0];
> +	cmdp = &cmd;
> +	memset(&cmdp->crb, 0, sizeof(cmdp->crb));
> +
> +read_state:
> +
> +	/* Read from .gz file */
> +
> +	NXPRT(fprintf(stderr, "read_state:\n"));
> +
> +	if (is_eof != 0)
> +		goto write_state;
> +
> +	/* We read in to fifo_in in two steps: first: read in to from
> +	 * cur_in to the end of the buffer.  last: if free space wrapped
> +	 * around, read from fifo_in offset 0 to offset cur_in.
> +	 */
> +
> +	/* Reset fifo head to reduce unnecessary wrap arounds */
> +	cur_in = (used_in == 0) ? 0 : cur_in;
> +
> +	/* Free space total is reduced by a gap */
> +	free_space = NX_MAX(0, fifo_free_bytes(used_in, fifo_in_len)
> +			    - line_sz);
> +
> +	/* Free space may wrap around as first and last */
> +	first_free = fifo_free_first_bytes(cur_in, used_in, fifo_in_len);
> +	last_free  = fifo_free_last_bytes(cur_in, used_in, fifo_in_len);
> +
> +	/* Start offsets of the free memory */
> +	first_offset = fifo_free_first_offset(cur_in, used_in);
> +	last_offset  = fifo_free_last_offset(cur_in, used_in, fifo_in_len);
> +
> +	/* Reduce read_sz because of the line_sz gap */
> +	read_sz = NX_MIN(free_space, first_free);
> +	n = 0;
> +	if (read_sz > 0) {
> +		/* Read in to offset cur_in + used_in */
> +		n = fread(fifo_in + first_offset, 1, read_sz, inpf);
> +		used_in = used_in + n;
> +		free_space = free_space - n;
> +		assert(n <= read_sz);
> +		if (n != read_sz) {
> +			/* Either EOF or error; exit the read loop */
> +			is_eof = 1;
> +			goto write_state;
> +		}
> +	}
> +
> +	/* If free space wrapped around */
> +	if (last_free > 0) {
> +		/* Reduce read_sz because of the line_sz gap */
> +		read_sz = NX_MIN(free_space, last_free);
> +		n = 0;
> +		if (read_sz > 0) {
> +			n = fread(fifo_in + last_offset, 1, read_sz, inpf);
> +			used_in = used_in + n;       /* Increase used space */
> +			free_space = free_space - n; /* Decrease free space */
> +			assert(n <= read_sz);
> +			if (n != read_sz) {
> +				/* Either EOF or error; exit the read loop */
> +				is_eof = 1;
> +				goto write_state;
> +			}
> +		}
> +	}
> +
> +	/* At this point we have used_in bytes in fifo_in with the
> +	 * data head starting at cur_in and possibly wrapping around.
> +	 */
> +
> +write_state:
> +
> +	/* Write decompressed data to output file */
> +
> +	NXPRT(fprintf(stderr, "write_state:\n"));
> +
> +	if (used_out == 0)
> +		goto decomp_state;
> +
> +	/* If fifo_out has data waiting, write it out to the file to
> +	 * make free target space for the accelerator used bytes in
> +	 * the first and last parts of fifo_out.
> +	 */
> +
> +	first_used = fifo_used_first_bytes(cur_out, used_out, fifo_out_len);
> +	last_used  = fifo_used_last_bytes(cur_out, used_out, fifo_out_len);
> +
> +	write_sz = first_used;
> +
> +	n = 0;
> +	if (write_sz > 0) {
> +		n = fwrite(fifo_out + cur_out, 1, write_sz, outf);
> +		used_out = used_out - n;
> +		/* Move head of the fifo */
> +		cur_out = (cur_out + n) % fifo_out_len;
> +		assert(n <= write_sz);
> +		if (n != write_sz) {
> +			fprintf(stderr, "error: write\n");
> +			rc = -1;
> +			goto err5;
> +		}
> +	}
> +
> +	if (last_used > 0) { /* If more data available in the last part */
> +		write_sz = last_used; /* Keep it here for later */
> +		n = 0;
> +		if (write_sz > 0) {
> +			n = fwrite(fifo_out, 1, write_sz, outf);
> +			used_out = used_out - n;
> +			cur_out = (cur_out + n) % fifo_out_len;
> +			assert(n <= write_sz);
> +			if (n != write_sz) {
> +				fprintf(stderr, "error: write\n");
> +				rc = -1;
> +				goto err5;
> +			}
> +		}
> +	}
> +
> +decomp_state:
> +
> +	/* NX decompresses input data */
> +
> +	NXPRT(fprintf(stderr, "decomp_state:\n"));
> +
> +	if (is_final)
> +		goto finish_state;
> +
> +	/* Address/len lists */
> +	clearp_dde(ddl_in);
> +	clearp_dde(ddl_out);
> +
> +	/* FC, CRC, HistLen, Table 6-6 */
> +	if (resuming) {
> +		/* Resuming a partially decompressed input.
> +		 * The key to resume is supplying the 32KB
> +		 * dictionary (history) to NX, which is basically
> +		 * the last 32KB of output produced.
> +		 */
> +		fc = GZIP_FC_DECOMPRESS_RESUME;
> +
> +		cmdp->cpb.in_crc   = cmdp->cpb.out_crc;
> +		cmdp->cpb.in_adler = cmdp->cpb.out_adler;
> +
> +		/* Round up the history size to quadword.  Section 2.10 */
> +		history_len = (history_len + 15) / 16;
> +		putnn(cmdp->cpb, in_histlen, history_len);
> +		history_len = history_len * 16; /* bytes */
> +
> +		if (history_len > 0) {
> +			/* Chain in the history buffer to the DDE list */
> +			if (cur_out >= history_len) {
> +				nx_append_dde(ddl_in, fifo_out
> +					      + (cur_out - history_len),
> +					      history_len);
> +			} else {
> +				nx_append_dde(ddl_in, fifo_out
> +					      + ((fifo_out_len + cur_out)
> +					      - history_len),
> +					      history_len - cur_out);
> +				/* Up to 32KB history wraps around fifo_out */
> +				nx_append_dde(ddl_in, fifo_out, cur_out);
> +			}
> +
> +		}
> +	} else {
> +		/* First decompress job */
> +		fc = GZIP_FC_DECOMPRESS;
> +
> +		history_len = 0;
> +		/* Writing 0 clears out subc as well */
> +		cmdp->cpb.in_histlen = 0;
> +		total_out = 0;
> +
> +		put32(cmdp->cpb, in_crc, INIT_CRC);
> +		put32(cmdp->cpb, in_adler, INIT_ADLER);
> +		put32(cmdp->cpb, out_crc, INIT_CRC);
> +		put32(cmdp->cpb, out_adler, INIT_ADLER);
> +
> +		/* Assuming 10% compression ratio initially; use the
> +		 * most recently measured compression ratio as a
> +		 * heuristic to estimate the input and output
> +		 * sizes.  If we give too much input, the target buffer
> +		 * overflows and NX cycles are wasted, and then we
> +		 * must retry with smaller input size.  1000 is 100%.
> +		 */
> +		last_comp_ratio = 100UL;
> +	}
> +	cmdp->crb.gzip_fc = 0;
> +	putnn(cmdp->crb, gzip_fc, fc);
> +
> +	/*
> +	 * NX source buffers
> +	 */
> +	first_used = fifo_used_first_bytes(cur_in, used_in, fifo_in_len);
> +	last_used = fifo_used_last_bytes(cur_in, used_in, fifo_in_len);
> +
> +	if (first_used > 0)
> +		nx_append_dde(ddl_in, fifo_in + cur_in, first_used);
> +
> +	if (last_used > 0)
> +		nx_append_dde(ddl_in, fifo_in, last_used);
> +
> +	/*
> +	 * NX target buffers
> +	 */
> +	first_free = fifo_free_first_bytes(cur_out, used_out, fifo_out_len);
> +	last_free = fifo_free_last_bytes(cur_out, used_out, fifo_out_len);
> +
> +	/* Reduce output free space amount not to overwrite the history */
> +	int target_max = NX_MAX(0, fifo_free_bytes(used_out, fifo_out_len)
> +				- (1<<16));
> +
> +	NXPRT(fprintf(stderr, "target_max %d (0x%x)\n", target_max,
> +		      target_max));
> +
> +	first_free = NX_MIN(target_max, first_free);
> +	if (first_free > 0) {
> +		first_offset = fifo_free_first_offset(cur_out, used_out);
> +		nx_append_dde(ddl_out, fifo_out + first_offset, first_free);
> +	}
> +
> +	if (last_free > 0) {
> +		last_free = NX_MIN(target_max - first_free, last_free);
> +		if (last_free > 0) {
> +			last_offset = fifo_free_last_offset(cur_out, used_out,
> +							    fifo_out_len);
> +			nx_append_dde(ddl_out, fifo_out + last_offset,
> +				      last_free);
> +		}
> +	}
> +
> +	/* Target buffer size is used to limit the source data size
> +	 * based on previous measurements of compression ratio.
> +	 */
> +
> +	/* source_sz includes history */
> +	source_sz = getp32(ddl_in, ddebc);
> +	assert(source_sz > history_len);
> +	source_sz = source_sz - history_len;
> +
> +	/* Estimating how much source is needed to 3/4 fill a
> +	 * target_max size target buffer.  If we overshoot, then NX
> +	 * must repeat the job with smaller input and we waste
> +	 * bandwidth.  If we undershoot then we use more NX calls than
> +	 * necessary.
> +	 */
> +
> +	source_sz_estimate = ((uint64_t)target_max * last_comp_ratio * 3UL)
> +				/ 4000;
> +
> +	if (source_sz_estimate < source_sz) {
> +		/* Target might be small, therefore limiting the
> +		 * source data.
> +		 */
> +		source_sz = source_sz_estimate;
> +		target_sz_estimate = target_max;
> +	} else {
> +		/* Source file might be small, therefore limiting target
> +		 * touch pages to a smaller value to save processor cycles.
> +		 */
> +		target_sz_estimate = ((uint64_t)source_sz * 1000UL)
> +					/ (last_comp_ratio + 1);
> +		target_sz_estimate = NX_MIN(2 * target_sz_estimate,
> +					    target_max);
> +	}
> +
> +	source_sz = source_sz + history_len;
> +
> +	/* Some NX condition codes require submitting the NX job again.
> +	 * Kernel doesn't handle NX page faults. Expects user code to
> +	 * touch pages.
> +	 */
> +	pgfault_retries = retry_max;
> +
> +restart_nx:
> +
> +	putp32(ddl_in, ddebc, source_sz);
> +
> +	/* Fault in pages */
> +	nx_touch_pages_dde(ddl_in, 0, page_sz, 0);
> +	nx_touch_pages_dde(ddl_out, target_sz_estimate, page_sz, 1);
> +
> +	/* Send job to NX */
> +	cc = nx_submit_job(ddl_in, ddl_out, cmdp, devhandle);
> +
> +	switch (cc) {
> +
> +	case ERR_NX_TRANSLATION:
> +
> +		/* We touched the pages ahead of time.  In the most common case
> +		 * we shouldn't be here.  But may be some pages were paged out.
> +		 * Kernel should have placed the faulting address to fsaddr.
> +		 */
> +		NXPRT(fprintf(stderr, "ERR_NX_TRANSLATION %p\n",
> +			      (void *)cmdp->crb.csb.fsaddr));
> +
> +		/* Touch 1 byte, read-only  */
> +		nx_touch_pages((void *)cmdp->crb.csb.fsaddr, 1, page_sz, 0);
> +
> +		if (pgfault_retries == retry_max) {
> +			/* Try once with exact number of pages */
> +			--pgfault_retries;
> +			goto restart_nx;
> +		} else if (pgfault_retries > 0) {
> +			/* If still faulting try fewer input pages
> +			 * assuming memory outage
> +			 */
> +			if (source_sz > page_sz)
> +				source_sz = NX_MAX(source_sz / 2, page_sz);
> +			--pgfault_retries;
> +			goto restart_nx;
> +		} else {
> +			fprintf(stderr, "cannot make progress; too many page \
> +				fault retries cc= %d\n", cc);
> +			rc = -1;
> +			goto err5;
> +		}
> +
> +	case ERR_NX_DATA_LENGTH:
> +
> +		NXPRT(fprintf(stderr, "ERR_NX_DATA_LENGTH; not an error \
> +			      usually; stream may have trailing data\n"));
> +
> +		/* Not an error in the most common case; it just says
> +		 * there is trailing data that we must examine.
> +		 *
> +		 * CC=3 CE(1)=0 CE(0)=1 indicates partial completion
> +		 * Fig.6-7 and Table 6-8.
> +		 */
> +		nx_ce = get_csb_ce_ms3b(cmdp->crb.csb);
> +
> +		if (!csb_ce_termination(nx_ce) &&
> +		    csb_ce_partial_completion(nx_ce)) {
> +			/* Check CPB for more information
> +			 * spbc and tpbc are valid
> +			 */
> +			sfbt = getnn(cmdp->cpb, out_sfbt); /* Table 6-4 */
> +			subc = getnn(cmdp->cpb, out_subc); /* Table 6-4 */
> +			spbc = get32(cmdp->cpb, out_spbc_decomp);
> +			tpbc = get32(cmdp->crb.csb, tpbc);
> +			assert(target_max >= tpbc);
> +
> +			goto ok_cc3; /* not an error */
> +		} else {
> +			/* History length error when CE(1)=1 CE(0)=0. */
> +			rc = -1;
> +			fprintf(stderr, "history length error cc= %d\n", cc);
> +			goto err5;
> +		}
> +
> +	case ERR_NX_TARGET_SPACE:
> +
> +		/* Target buffer not large enough; retry smaller input
> +		 * data; give at least 1 byte.  SPBC/TPBC are not valid.
> +		 */
> +		assert(source_sz > history_len);
> +		source_sz = ((source_sz - history_len + 2) / 2) + history_len;
> +		NXPRT(fprintf(stderr, "ERR_NX_TARGET_SPACE; retry with \
> +			      smaller input data src %d hist %d\n", source_sz,
> +			      history_len));
> +		goto restart_nx;
> +
> +	case ERR_NX_OK:
> +
> +		/* This should not happen for gzip formatted data;
> +		 * we need trailing crc and isize
> +		 */
> +		fprintf(stderr, "ERR_NX_OK\n");
> +		spbc = get32(cmdp->cpb, out_spbc_decomp);
> +		tpbc = get32(cmdp->crb.csb, tpbc);
> +		assert(target_max >= tpbc);
> +		assert(spbc >= history_len);
> +		source_sz = spbc - history_len;
> +		goto offsets_state;
> +
> +	default:
> +		fprintf(stderr, "error: cc= %d\n", cc);
> +		rc = -1;
> +		goto err5;
> +	}
> +
> +ok_cc3:
> +
> +	NXPRT(fprintf(stderr, "cc3: sfbt: %x\n", sfbt));
> +
> +	assert(spbc > history_len);
> +	source_sz = spbc - history_len;
> +
> +	/* Table 6-4: Source Final Block Type (SFBT) describes the
> +	 * last processed deflate block and clues the software how to
> +	 * resume the next job.  SUBC indicates how many input bits NX
> +	 * consumed but did not process.  SPBC indicates how many
> +	 * bytes of source were given to the accelerator including
> +	 * history bytes.
> +	 */
> +
> +	switch (sfbt) {
> +		int dhtlen;
> +
> +	case 0b0000: /* Deflate final EOB received */
> +
> +		/* Calculating the checksum start position. */
> +
> +		source_sz = source_sz - subc / 8;
> +		is_final = 1;
> +		break;
> +
> +		/* Resume decompression cases are below. Basically
> +		 * indicates where NX has suspended and how to resume
> +		 * the input stream.
> +		 */
> +
> +	case 0b1000: /* Within a literal block; use rembytecount */
> +	case 0b1001: /* Within a literal block; use rembytecount; bfinal=1 */
> +
> +		/* Supply the partially processed source byte again */
> +		source_sz = source_sz - ((subc + 7) / 8);
> +
> +		/* SUBC LS 3bits: number of bits in the first source byte need
> +		 * to be processed.
> +		 * 000 means all 8 bits;  Table 6-3
> +		 * Clear subc, histlen, sfbt, rembytecnt, dhtlen
> +		 */
> +		cmdp->cpb.in_subc = 0;
> +		cmdp->cpb.in_sfbt = 0;
> +		putnn(cmdp->cpb, in_subc, subc % 8);
> +		putnn(cmdp->cpb, in_sfbt, sfbt);
> +		putnn(cmdp->cpb, in_rembytecnt, getnn(cmdp->cpb,
> +						      out_rembytecnt));
> +		break;
> +
> +	case 0b1010: /* Within a FH block; */
> +	case 0b1011: /* Within a FH block; bfinal=1 */
> +
> +		source_sz = source_sz - ((subc + 7) / 8);
> +
> +		/* Clear subc, histlen, sfbt, rembytecnt, dhtlen */
> +		cmdp->cpb.in_subc = 0;
> +		cmdp->cpb.in_sfbt = 0;
> +		putnn(cmdp->cpb, in_subc, subc % 8);
> +		putnn(cmdp->cpb, in_sfbt, sfbt);
> +		break;
> +
> +	case 0b1100: /* Within a DH block; */
> +	case 0b1101: /* Within a DH block; bfinal=1 */
> +
> +		source_sz = source_sz - ((subc + 7) / 8);
> +
> +		/* Clear subc, histlen, sfbt, rembytecnt, dhtlen */
> +		cmdp->cpb.in_subc = 0;
> +		cmdp->cpb.in_sfbt = 0;
> +		putnn(cmdp->cpb, in_subc, subc % 8);
> +		putnn(cmdp->cpb, in_sfbt, sfbt);
> +
> +		dhtlen = getnn(cmdp->cpb, out_dhtlen);
> +		putnn(cmdp->cpb, in_dhtlen, dhtlen);
> +		assert(dhtlen >= 42);
> +
> +		/* Round up to a qword */
> +		dhtlen = (dhtlen + 127) / 128;
> +
> +		while (dhtlen > 0) { /* Copy dht from cpb.out to cpb.in */
> +			--dhtlen;
> +			cmdp->cpb.in_dht[dhtlen] = cmdp->cpb.out_dht[dhtlen];
> +		}
> +		break;
> +
> +	case 0b1110: /* Within a block header; bfinal=0; */
> +		     /* Also given if source data exactly ends (SUBC=0) with
> +		      * EOB code with BFINAL=0.  Means the next byte will
> +		      * contain a block header.
> +		      */
> +	case 0b1111: /* within a block header with BFINAL=1. */
> +
> +		source_sz = source_sz - ((subc + 7) / 8);
> +
> +		/* Clear subc, histlen, sfbt, rembytecnt, dhtlen */
> +		cmdp->cpb.in_subc = 0;
> +		cmdp->cpb.in_sfbt = 0;
> +		putnn(cmdp->cpb, in_subc, subc % 8);
> +		putnn(cmdp->cpb, in_sfbt, sfbt);
> +	}
> +
> +offsets_state:
> +
> +	/* Adjust the source and target buffer offsets and lengths  */
> +
> +	NXPRT(fprintf(stderr, "offsets_state:\n"));
> +
> +	/* Delete input data from fifo_in */
> +	used_in = used_in - source_sz;
> +	cur_in = (cur_in + source_sz) % fifo_in_len;
> +	input_file_offset = input_file_offset + source_sz;
> +
> +	/* Add output data to fifo_out */
> +	used_out = used_out + tpbc;
> +
> +	assert(used_out <= fifo_out_len);
> +
> +	total_out = total_out + tpbc;
> +
> +	/* Deflate history is 32KB max.  No need to supply more
> +	 * than 32KB on a resume.
> +	 */
> +	history_len = (total_out > window_max) ? window_max : total_out;
> +
> +	/* To estimate expected expansion in the next NX job; 500 means 50%.
> +	 * Deflate best case is around 1 to 1000.
> +	 */
> +	last_comp_ratio = (1000UL * ((uint64_t)source_sz + 1))
> +			  / ((uint64_t)tpbc + 1);
> +	last_comp_ratio = NX_MAX(NX_MIN(1000UL, last_comp_ratio), 1);
> +	NXPRT(fprintf(stderr, "comp_ratio %ld source_sz %d spbc %d tpbc %d\n",
> +		      last_comp_ratio, source_sz, spbc, tpbc));
> +
> +	resuming = 1;
> +
> +finish_state:
> +
> +	NXPRT(fprintf(stderr, "finish_state:\n"));
> +
> +	if (is_final) {
> +		if (used_out)
> +			goto write_state; /* More data to write out */
> +		else if (used_in < 8) {
> +			/* Need at least 8 more bytes containing gzip crc
> +			 * and isize.
> +			 */
> +			rc = -1;
> +			goto err4;
> +		} else {
> +			/* Compare checksums and exit */
> +			int i;
> +			char tail[8];
> +			uint32_t cksum, isize;
> +			for (i = 0; i < 8; i++)
> +				tail[i] = fifo_in[(cur_in + i) % fifo_in_len];
> +			fprintf(stderr, "computed checksum %08x isize %08x\n",
> +				cmdp->cpb.out_crc, (uint32_t) (total_out
> +				% (1ULL<<32)));
> +			cksum = (tail[0] | tail[1]<<8 | tail[2]<<16
> +				| tail[3]<<24);
> +			isize = (tail[4] | tail[5]<<8 | tail[6]<<16
> +				| tail[7]<<24);
> +			fprintf(stderr, "stored   checksum %08x isize %08x\n",
> +				cksum, isize);
> +
> +			if (cksum == cmdp->cpb.out_crc && isize == (uint32_t)
> +			    (total_out % (1ULL<<32))) {
> +				rc = 0;	goto ok1;
> +			} else {
> +				rc = -1; goto err4;
> +			}
> +		}
> +	} else
> +		goto read_state;
> +
> +	return -1;
> +
> +err1:
> +	fprintf(stderr, "error: not a gzip file, expect %x, read %x\n",
> +		expect, c);
> +	return -1;
> +
> +err2:
> +	fprintf(stderr, "error: the FLG byte is wrong or not handled by this \
> +		code sample\n");
> +	return -1;
> +
> +err3:
> +	fprintf(stderr, "error: gzip header\n");
> +	return -1;
> +
> +err4:
> +	fprintf(stderr, "error: checksum\n");
> +
> +err5:
> +ok1:
> +	fprintf(stderr, "decomp is complete: fclose\n");
> +	fclose(outf);
> +
> +	return rc;
> +}
> +
> +
> +int main(int argc, char **argv)
> +{
> +	int rc;
> +	struct sigaction act;
> +	void *handle;
> +
> +	act.sa_handler = 0;
> +	act.sa_sigaction = sigsegv_handler;
> +	act.sa_flags = SA_SIGINFO;
> +	act.sa_restorer = 0;
> +	sigemptyset(&act.sa_mask);
> +	sigaction(SIGSEGV, &act, NULL);
> +
> +	handle = nx_function_begin(NX_FUNC_COMP_GZIP, 0);
> +	if (!handle) {
> +		fprintf(stderr, "Unable to init NX, errno %d\n", errno);
> +		exit(-1);
> +	}
> +
> +	rc = decompress_file(argc, argv, handle);
> +
> +	nx_function_end(handle);
> +
> +	return rc;
> +}
> -- 
> 2.21.0

^ permalink raw reply

* Re: [PATCH 1/5] selftests/powerpc: Add header files for GZIP engine test
From: Daniel Axtens @ 2020-03-18  3:48 UTC (permalink / raw)
  To: Raphael Moreira Zinsly, linuxppc-dev, linux-crypto
  Cc: abali, haren, herbert, Raphael Moreira Zinsly
In-Reply-To: <20200316180714.18631-2-rzinsly@linux.ibm.com>

Hi,

This is throwing a number of snowpatch warnings, as well as a whitespace
warning when I apply it. Please could you check the warnings at
https://patchwork.ozlabs.org/patch/1255779/

It looks like the rest of the series also throws some warnings - please
check those also.

Kind regards,
Daniel


Raphael Moreira Zinsly <rzinsly@linux.ibm.com> writes:

> Add files to access the powerpc NX-GZIP engine in user space.
>
> Signed-off-by: Bulent Abali <abali@us.ibm.com>
> Signed-off-by: Raphael Moreira Zinsly <rzinsly@linux.ibm.com>
> ---
>  .../selftests/powerpc/nx-gzip/inc/crb.h       | 170 ++++++++++++++++++
>  .../selftests/powerpc/nx-gzip/inc/nx-gzip.h   |  27 +++
>  .../powerpc/nx-gzip/inc/nx-helpers.h          |  53 ++++++
>  .../selftests/powerpc/nx-gzip/inc/nx.h        |  30 ++++
>  4 files changed, 280 insertions(+)
>  create mode 100644 tools/testing/selftests/powerpc/nx-gzip/inc/crb.h
>  create mode 100644 tools/testing/selftests/powerpc/nx-gzip/inc/nx-gzip.h
>  create mode 100644 tools/testing/selftests/powerpc/nx-gzip/inc/nx-helpers.h
>  create mode 100644 tools/testing/selftests/powerpc/nx-gzip/inc/nx.h
>
> diff --git a/tools/testing/selftests/powerpc/nx-gzip/inc/crb.h b/tools/testing/selftests/powerpc/nx-gzip/inc/crb.h
> new file mode 100644
> index 000000000000..6af25fb8461a
> --- /dev/null
> +++ b/tools/testing/selftests/powerpc/nx-gzip/inc/crb.h
> @@ -0,0 +1,170 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +#ifndef __CRB_H
> +#define __CRB_H
> +#include <linux/types.h>
> +
> +typedef unsigned char u8;
> +typedef unsigned int u32;
> +typedef unsigned long long u64;
> +
> +/* From nx-842.h */
> +
> +/* CCW 842 CI/FC masks
> + * NX P8 workbook, section 4.3.1, figure 4-6
> + * "CI/FC Boundary by NX CT type"
> + */
> +#define CCW_CI_842              (0x00003ff8)
> +#define CCW_FC_842              (0x00000007)
> +
> +/* end - nx-842.h */
> +
> +#ifndef __aligned
> +#define __aligned(x)            __attribute__((aligned(x)))
> +#endif
> +
> +#ifndef __packed
> +#define __packed        __attribute__((packed))
> +#endif
> +
> +/* Chapter 6.5.8 Coprocessor-Completion Block (CCB) */
> +
> +#define CCB_VALUE		(0x3fffffffffffffff)
> +#define CCB_ADDRESS		(0xfffffffffffffff8)
> +#define CCB_CM			(0x0000000000000007)
> +#define CCB_CM0			(0x0000000000000004)
> +#define CCB_CM12		(0x0000000000000003)
> +
> +#define CCB_CM0_ALL_COMPLETIONS	(0x0)
> +#define CCB_CM0_LAST_IN_CHAIN	(0x4)
> +#define CCB_CM12_STORE		(0x0)
> +#define CCB_CM12_INTERRUPT	(0x1)
> +
> +#define CCB_SIZE		(0x10)
> +#define CCB_ALIGN		CCB_SIZE
> +
> +struct coprocessor_completion_block {
> +	__be64 value;
> +	__be64 address;
> +} __packed __aligned(CCB_ALIGN);
> +
> +
> +/* Chapter 6.5.7 Coprocessor-Status Block (CSB) */
> +
> +#define CSB_V			(0x80)
> +#define CSB_F			(0x04)
> +#define CSB_CH			(0x03)
> +#define CSB_CE_INCOMPLETE	(0x80)
> +#define CSB_CE_TERMINATION	(0x40)
> +#define CSB_CE_TPBC		(0x20)
> +
> +#define CSB_CC_SUCCESS		(0)
> +#define CSB_CC_INVALID_ALIGN	(1)
> +#define CSB_CC_OPERAND_OVERLAP	(2)
> +#define CSB_CC_DATA_LENGTH	(3)
> +#define CSB_CC_TRANSLATION	(5)
> +#define CSB_CC_PROTECTION	(6)
> +#define CSB_CC_RD_EXTERNAL	(7)
> +#define CSB_CC_INVALID_OPERAND	(8)
> +#define CSB_CC_PRIVILEGE	(9)
> +#define CSB_CC_INTERNAL		(10)
> +#define CSB_CC_WR_EXTERNAL	(12)
> +#define CSB_CC_NOSPC		(13)
> +#define CSB_CC_EXCESSIVE_DDE	(14)
> +#define CSB_CC_WR_TRANSLATION	(15)
> +#define CSB_CC_WR_PROTECTION	(16)
> +#define CSB_CC_UNKNOWN_CODE	(17)
> +#define CSB_CC_ABORT		(18)
> +#define CSB_CC_TRANSPORT	(20)
> +#define CSB_CC_SEGMENTED_DDL	(31)
> +#define CSB_CC_PROGRESS_POINT	(32)
> +#define CSB_CC_DDE_OVERFLOW	(33)
> +#define CSB_CC_SESSION		(34)
> +#define CSB_CC_PROVISION	(36)
> +#define CSB_CC_CHAIN		(37)
> +#define CSB_CC_SEQUENCE		(38)
> +#define CSB_CC_HW		(39)
> +
> +#define CSB_SIZE		(0x10)
> +#define CSB_ALIGN		CSB_SIZE
> +
> +struct coprocessor_status_block {
> +	u8 flags;
> +	u8 cs;
> +	u8 cc;
> +	u8 ce;
> +	__be32 count;
> +	__be64 address;
> +} __packed __aligned(CSB_ALIGN);
> +
> +
> +/* Chapter 6.5.10 Data-Descriptor List (DDL)
> + * each list contains one or more Data-Descriptor Entries (DDE)
> + */
> +
> +#define DDE_P			(0x8000)
> +
> +#define DDE_SIZE		(0x10)
> +#define DDE_ALIGN		DDE_SIZE
> +
> +struct data_descriptor_entry {
> +	__be16 flags;
> +	u8 count;
> +	u8 index;
> +	__be32 length;
> +	__be64 address;
> +} __packed __aligned(DDE_ALIGN);
> +
> +
> +/* Chapter 6.5.2 Coprocessor-Request Block (CRB) */
> +
> +#define CRB_SIZE		(0x80)
> +#define CRB_ALIGN		(0x100) /* Errata: requires 256 alignment */
> +
> +
> +/* Coprocessor Status Block field
> + *   ADDRESS	address of CSB
> + *   C		CCB is valid
> + *   AT		0 = addrs are virtual, 1 = addrs are phys
> + *   M		enable perf monitor
> + */
> +#define CRB_CSB_ADDRESS		(0xfffffffffffffff0)
> +#define CRB_CSB_C		(0x0000000000000008)
> +#define CRB_CSB_AT		(0x0000000000000002)
> +#define CRB_CSB_M		(0x0000000000000001)
> +
> +struct coprocessor_request_block {
> +	__be32 ccw;
> +	__be32 flags;
> +	__be64 csb_addr;
> +
> +	struct data_descriptor_entry source;
> +	struct data_descriptor_entry target;
> +
> +	struct coprocessor_completion_block ccb;
> +
> +	u8 reserved[48];
> +
> +	struct coprocessor_status_block csb;
> +} __packed __aligned(CRB_ALIGN);
> +
> +#define crb_csb_addr(c)         __be64_to_cpu(c->csb_addr)
> +#define crb_nx_fault_addr(c)    __be64_to_cpu(c->stamp.nx.fault_storage_addr)
> +#define crb_nx_flags(c)         c->stamp.nx.flags
> +#define crb_nx_fault_status(c)  c->stamp.nx.fault_status
> +#define crb_nx_pswid(c)		c->stamp.nx.pswid;
> +
> +
> +/* RFC02167 Initiate Coprocessor Instructions document
> + * Chapter 8.2.1.1.1 RS
> + * Chapter 8.2.3 Coprocessor Directive
> + * Chapter 8.2.4 Execution
> + *
> + * The CCW must be converted to BE before passing to icswx()
> + */
> +
> +#define CCW_PS                  (0xff000000)
> +#define CCW_CT                  (0x00ff0000)
> +#define CCW_CD                  (0x0000ffff)
> +#define CCW_CL                  (0x0000c000)
> +
> +#endif
> diff --git a/tools/testing/selftests/powerpc/nx-gzip/inc/nx-gzip.h b/tools/testing/selftests/powerpc/nx-gzip/inc/nx-gzip.h
> new file mode 100644
> index 000000000000..75482c45574d
> --- /dev/null
> +++ b/tools/testing/selftests/powerpc/nx-gzip/inc/nx-gzip.h
> @@ -0,0 +1,27 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * Copyright 2020 IBM Corp.
> + *
> + */
> +
> +#ifndef _UAPI_MISC_VAS_H
> +#define _UAPI_MISC_VAS_H
> +
> +#include <asm/ioctl.h>
> +
> +#define VAS_FLAGS_PIN_WINDOW	0x1
> +#define VAS_FLAGS_HIGH_PRI	0x2
> +
> +#define VAS_FTW_SETUP		_IOW('v', 1, struct vas_gzip_setup_attr)
> +#define VAS_842_TX_WIN_OPEN	_IOW('v', 2, struct vas_gzip_setup_attr)
> +#define VAS_GZIP_TX_WIN_OPEN	_IOW('v', 0x20, struct vas_gzip_setup_attr)
> +
> +struct vas_gzip_setup_attr {
> +	int32_t		version;
> +	int16_t		vas_id;
> +	int16_t		reserved1;
> +	int64_t		flags;
> +	int64_t		reserved2[6];
> +};
> +
> +#endif /* _UAPI_MISC_VAS_H */
> diff --git a/tools/testing/selftests/powerpc/nx-gzip/inc/nx-helpers.h b/tools/testing/selftests/powerpc/nx-gzip/inc/nx-helpers.h
> new file mode 100644
> index 000000000000..201cf9f86a97
> --- /dev/null
> +++ b/tools/testing/selftests/powerpc/nx-gzip/inc/nx-helpers.h
> @@ -0,0 +1,53 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +#include <sys/time.h>
> +#include <asm/byteorder.h>
> +#include <stdint.h>
> +#include "crb.h"
> +
> +#define cpu_to_be32		__cpu_to_be32
> +#define cpu_to_be64		__cpu_to_be64
> +#define be32_to_cpu		__be32_to_cpu
> +#define be64_to_cpu		__be64_to_cpu
> +
> +/*
> + * Several helpers/macros below were copied from the tree
> + * (kernel.h, nx-842.h, nx-ftw.h, asm-compat.h etc)
> + */
> +
> +/* from kernel.h */
> +#define IS_ALIGNED(x, a)	(((x) & ((typeof(x))(a) - 1)) == 0)
> +#define __round_mask(x, y)	((__typeof__(x))((y)-1))
> +#define round_up(x, y)		((((x)-1) | __round_mask(x, y))+1)
> +#define round_down(x, y)	((x) & ~__round_mask(x, y))
> +
> +#define min_t(t, x, y)	((x) < (y) ? (x) : (y))
> +/*
> + * Get/Set bit fields. (from nx-842.h)
> + */
> +#define GET_FIELD(m, v)         (((v) & (m)) >> MASK_LSH(m))
> +#define MASK_LSH(m)             (__builtin_ffsl(m) - 1)
> +#define SET_FIELD(m, v, val)    \
> +		(((v) & ~(m)) | ((((typeof(v))(val)) << MASK_LSH(m)) & (m)))
> +
> +/* From asm-compat.h */
> +#define __stringify_in_c(...)	#__VA_ARGS__
> +#define stringify_in_c(...)	__stringify_in_c(__VA_ARGS__) " "
> +
> +#define	pr_debug
> +#define	pr_debug_ratelimited	printf
> +#define	pr_err			printf
> +#define	pr_err_ratelimited	printf
> +
> +#define WARN_ON_ONCE(x)		if (x) \
> +				printf("WARNING: %s:%d\n", __func__, __LINE__)
> +
> +extern void dump_buffer(char *msg, char *buf, int len);
> +extern void *alloc_aligned_mem(int len, int align, char *msg);
> +extern void get_payload(char *buf, int len);
> +extern void time_add(struct timeval *in, int seconds, struct timeval *out);
> +
> +typedef int bool;
> +extern bool time_after(struct timeval *a, struct timeval *b);
> +extern long time_delta(struct timeval *a, struct timeval *b);
> +extern void dump_dde(struct data_descriptor_entry *dde, char *msg);
> +extern void copy_paste_crb_data(struct coprocessor_request_block *crb);
> diff --git a/tools/testing/selftests/powerpc/nx-gzip/inc/nx.h b/tools/testing/selftests/powerpc/nx-gzip/inc/nx.h
> new file mode 100644
> index 000000000000..08c93f7fb96c
> --- /dev/null
> +++ b/tools/testing/selftests/powerpc/nx-gzip/inc/nx.h
> @@ -0,0 +1,30 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * Copyright 2020 IBM Corp.
> + *
> + */
> +
> +#define	NX_FUNC_COMP_842	1
> +#define NX_FUNC_COMP_GZIP	2
> +
> +typedef int bool;
> +
> +struct nx842_func_args {
> +	bool use_crc;
> +	bool decompress;		/* true: decompress; false compress */
> +	bool move_data;
> +	int timeout;			/* seconds */
> +};
> +
> +typedef struct {
> +	int len;
> +	char *buf;
> +} nxbuf_t;
> +
> +/* @function should be EFT (aka 842), GZIP etc */
> +extern void *nx_function_begin(int function, int pri);
> +
> +extern int nx_function(void *handle, nxbuf_t *in, nxbuf_t *out, void *arg);
> +
> +extern int nx_function_end(void *handle);
> +
> -- 
> 2.21.0

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox