From: Leonardo Bras <hidden> Date: 2020-07-03 06:20:01
There are some devices in which a hypervisor may only allow 1 DMA window
to exist at a time, and in those cases, a DDW is never created to them,
since the default DMA window keeps using this resource.
LoPAR recommends this procedure:
1. Remove the default DMA window,
2. Query for which configs the DDW can be created,
3. Create a DDW.
Patch #1:
Create defines for outputs of ibm,ddw-applicable, so it's easier to
identify them.
Patch #2:
- After LoPAR level 2.8, there is an extension that can make
ibm,query-pe-dma-windows to have 6 outputs instead of 5. This changes the
order of the outputs, and that can cause some trouble.
- query_ddw() was updated to check how many outputs the
ibm,query-pe-dma-windows is supposed to have, update the rtas_call() and
deal correctly with the outputs in both cases.
- This patch looks somehow unrelated to the series, but it can avoid future
problems on DDW creation.
Patch #3 moves the window-removing code from remove_ddw() to
remove_dma_window(), creating a way to delete any DMA window, so it can be
used to delete the default DMA window.
Patch #4 makes use of the remove_dma_window() from patch #3 to remove the
default DMA window before query_ddw(). It also implements a new rtas call
to recover the default DMA window, in case anything fails after it was
removed, and a DDW couldn't be created.
Patch #5:
Instead of destroying the created DDW if it doesn't map the whole
partition, make use of it instead of the default DMA window as it improves
performance.
Patch #6:
Does some renaming of 'direct window' to 'dma window', given the DDW
created can now be also used in indirect mapping if direct mapping is not
available.
All patches were tested into an LPAR with an Ethernet VF:
4005:01:00.0 Ethernet controller: Mellanox Technologies MT27700 Family
[ConnectX-4 Virtual Function]
Patch #5 It was tested with a 64GB DDW which did not map the whole
partition (128G). Performance improvement noticed by using the DDW instead
of the default DMA window:
64 thread write throughput: +203.0%
64 thread read throughput: +17.5%
1 thread write throughput: +20.5%
1 thread read throughput: +3.43%
Average write latency: -23.0%
Average read latency: -2.26%
---
Changes since v2:
- Change the way ibm,ddw-extensions is accessed, using a proper function
instead of doing this inline everytime it's used.
- Remove previous patch #6, as it doesn't look like it would be useful.
- Add new patch, for changing names from direct* to dma*, as indirect
mapping can be used from now on.
- Fix some typos, corrects some define usage.
- v2 link: http://patchwork.ozlabs.org/project/linuxppc-dev/list/?series=185433&state=%2A&archive=both
Changes since v1:
- Add defines for ibm,ddw-applicable and ibm,ddw-extensions outputs
- Merge aux function query_ddw_out_sz() into query_ddw()
- Merge reset_dma_window() patch (prev. #2) into remove default DMA
window patch (#4).
- Keep device_node *np name instead of using pdn in remove_*()
- Rename 'device_node *pdn' into 'parent' in new functions
- Rename dfl_win to default_win
- Only remove the default DMA window if there is no window available
in first query.
- Check if default DMA window can be restored before removing it.
- Fix 'unitialized use' (found by travis mpe:ci-test)
- New patches #5 and #6
- v1 link: http://patchwork.ozlabs.org/project/linuxppc-dev/list/?series=184420&state=%2A&archive=both
Special thanks for Alexey Kardashevskiy and Oliver O'Halloran for
the feedback provided!
Leonardo Bras (6):
powerpc/pseries/iommu: Create defines for operations in
ibm,ddw-applicable
powerpc/pseries/iommu: Update call to ibm,query-pe-dma-windows
powerpc/pseries/iommu: Move window-removing part of remove_ddw into
remove_dma_window
powerpc/pseries/iommu: Remove default DMA window before creating DDW
powerpc/pseries/iommu: Make use of DDW even if it does not map the
partition
powerpc/pseries/iommu: Rename "direct window" to "dma window"
arch/powerpc/platforms/pseries/iommu.c | 379 ++++++++++++++++++-------
1 file changed, 269 insertions(+), 110 deletions(-)
--
2.25.4
From: Leonardo Bras <hidden> Date: 2020-07-03 06:20:05
Create defines to help handling ibm,ddw-applicable values, avoiding
confusion about the index of given operations.
Signed-off-by: Leonardo Bras <redacted>
---
arch/powerpc/platforms/pseries/iommu.c | 43 ++++++++++++++++----------
1 file changed, 26 insertions(+), 17 deletions(-)
@@ -798,15 +806,15 @@ static void remove_ddw(struct device_node *np, bool remove_prop)pr_debug("%pOF successfully cleared tces in window.\n",np);-ret=rtas_call(ddw_avail[2],1,1,NULL,liobn);+ret=rtas_call(ddw_avail[DDW_REMOVE_PE_DMA_WIN],1,1,NULL,liobn);if(ret)pr_warn("%pOF: failed to remove direct window: rtas returned ""%d to ibm,remove-pe-dma-window(%x) %llx\n",-np,ret,ddw_avail[2],liobn);+np,ret,ddw_avail[DDW_REMOVE_PE_DMA_WIN],liobn);elsepr_debug("%pOF: successfully removed direct window: rtas returned ""%d to ibm,remove-pe-dma-window(%x) %llx\n",-np,ret,ddw_avail[2],liobn);+np,ret,ddw_avail[DDW_REMOVE_PE_DMA_WIN],liobn);delprop:if(remove_prop)
@@ -889,11 +897,11 @@ static int query_ddw(struct pci_dev *dev, const u32 *ddw_avail,buid=pdn->phb->buid;cfg_addr=((pdn->busno<<16)|(pdn->devfn<<8));-ret=rtas_call(ddw_avail[0],3,5,(u32*)query,-cfg_addr,BUID_HI(buid),BUID_LO(buid));+ret=rtas_call(ddw_avail[DDW_QUERY_PE_DMA_WIN],3,5,(u32*)query,+cfg_addr,BUID_HI(buid),BUID_LO(buid));dev_info(&dev->dev,"ibm,query-pe-dma-windows(%x) %x %x %x"-" returned %d\n",ddw_avail[0],cfg_addr,BUID_HI(buid),-BUID_LO(buid),ret);+" returned %d\n",ddw_avail[DDW_QUERY_PE_DMA_WIN],cfg_addr,+BUID_HI(buid),BUID_LO(buid),ret);returnret;}
@@ -920,15 +928,16 @@ static int create_ddw(struct pci_dev *dev, const u32 *ddw_avail,do{/* extra outputs are LIOBN and dma-addr (hi, lo) */-ret=rtas_call(ddw_avail[1],5,4,(u32*)create,-cfg_addr,BUID_HI(buid),BUID_LO(buid),-page_shift,window_shift);+ret=rtas_call(ddw_avail[DDW_CREATE_PE_DMA_WIN],5,4,+(u32*)create,cfg_addr,BUID_HI(buid),+BUID_LO(buid),page_shift,window_shift);}while(rtas_busy_delay(ret));dev_info(&dev->dev,"ibm,create-pe-dma-window(%x) %x %x %x %x %x returned %d "-"(liobn = 0x%x starting addr = %x %x)\n",ddw_avail[1],-cfg_addr,BUID_HI(buid),BUID_LO(buid),page_shift,-window_shift,ret,create->liobn,create->addr_hi,create->addr_lo);+"(liobn = 0x%x starting addr = %x %x)\n",+ddw_avail[DDW_CREATE_PE_DMA_WIN],cfg_addr,BUID_HI(buid),+BUID_LO(buid),page_shift,window_shift,ret,create->liobn,+create->addr_hi,create->addr_lo);returnret;}
From: Leonardo Bras <hidden> Date: 2020-07-03 06:20:09
From LoPAR level 2.8, "ibm,ddw-extensions" index 3 can make the number of
outputs from "ibm,query-pe-dma-windows" go from 5 to 6.
This change of output size is meant to expand the address size of
largest_available_block PE TCE from 32-bit to 64-bit, which ends up
shifting page_size and migration_capable.
This ends up requiring the update of
ddw_query_response->largest_available_block from u32 to u64, and manually
assigning the values from the buffer into this struct, according to
output size.
Also, a routine was created for helping reading the ddw extensions as
suggested by LoPAR: First reading the size of the extension array from
index 0, checking if the property exists, and then returning it's value.
Signed-off-by: Leonardo Bras <redacted>
---
arch/powerpc/platforms/pseries/iommu.c | 91 +++++++++++++++++++++++---
1 file changed, 81 insertions(+), 10 deletions(-)
From: Leonardo Bras <hidden> Date: 2020-07-03 06:20:13
Move the window-removing part of remove_ddw into a new function
(remove_dma_window), so it can be used to remove other DMA windows.
It's useful for removing DMA windows that don't create DIRECT64_PROPNAME
property, like the default DMA window from the device, which uses
"ibm,dma-window".
Signed-off-by: Leonardo Bras <redacted>
---
arch/powerpc/platforms/pseries/iommu.c | 45 +++++++++++++++-----------
1 file changed, 27 insertions(+), 18 deletions(-)
@@ -781,25 +781,14 @@ static int __init disable_ddw_setup(char *str)early_param("disable_ddw",disable_ddw_setup);-staticvoidremove_ddw(structdevice_node*np,boolremove_prop)+staticvoidremove_dma_window(structdevice_node*np,u32*ddw_avail,+structproperty*win){structdynamic_dma_window_prop*dwp;-structproperty*win64;-u32ddw_avail[DDW_APPLICABLE_SIZE];u64liobn;-intret=0;--ret=of_property_read_u32_array(np,"ibm,ddw-applicable",-&ddw_avail[0],DDW_APPLICABLE_SIZE);--win64=of_find_property(np,DIRECT64_PROPNAME,NULL);-if(!win64)-return;--if(ret||win64->length<sizeof(*dwp))-gotodelprop;+intret;-dwp=win64->value;+dwp=win->value;liobn=(u64)be32_to_cpu(dwp->liobn);/* clear the whole window, note the arg is in kernel pages */
@@ -821,10 +810,30 @@ static void remove_ddw(struct device_node *np, bool remove_prop)pr_debug("%pOF: successfully removed direct window: rtas returned ""%d to ibm,remove-pe-dma-window(%x) %llx\n",np,ret,ddw_avail[DDW_REMOVE_PE_DMA_WIN],liobn);+}++staticvoidremove_ddw(structdevice_node*np,boolremove_prop)+{+structproperty*win;+u32ddw_avail[DDW_APPLICABLE_SIZE];+intret=0;++ret=of_property_read_u32_array(np,"ibm,ddw-applicable",+&ddw_avail[0],DDW_APPLICABLE_SIZE);+if(ret)+return;++win=of_find_property(np,DIRECT64_PROPNAME,NULL);+if(!win)+return;++if(win->length>=sizeof(structdynamic_dma_window_prop))+remove_dma_window(np,ddw_avail,win);++if(!remove_prop)+return;-delprop:-if(remove_prop)-ret=of_remove_property(np,win64);+ret=of_remove_property(np,win);if(ret)pr_warn("%pOF: failed to remove direct window property: %d\n",np,ret);
From: Leonardo Bras <hidden> Date: 2020-07-03 06:20:17
On LoPAR "DMA Window Manipulation Calls", it's recommended to remove the
default DMA window for the device, before attempting to configure a DDW,
in order to make the maximum resources available for the next DDW to be
created.
This is a requirement for using DDW on devices in which hypervisor
allows only one DMA window.
If setting up a new DDW fails anywhere after the removal of this
default DMA window, it's needed to restore the default DMA window.
For this, an implementation of ibm,reset-pe-dma-windows rtas call is
needed:
Platforms supporting the DDW option starting with LoPAR level 2.7 implement
ibm,ddw-extensions. The first extension available (index 2) carries the
token for ibm,reset-pe-dma-windows rtas call, which is used to restore
the default DMA window for a device, if it has been deleted.
It does so by resetting the TCE table allocation for the PE to it's
boot time value, available in "ibm,dma-window" device tree node.
Signed-off-by: Leonardo Bras <redacted>
---
arch/powerpc/platforms/pseries/iommu.c | 83 +++++++++++++++++++++-----
1 file changed, 69 insertions(+), 14 deletions(-)
@@ -1133,14 +1165,34 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)if(ret!=0)gotoout_failed;+/*+*Ifthereisnowindowavailable,removethedefaultDMAwindow,+*ifit'spresent.Thiswillmakealltheresourcesavailabletothe+*newDDWwindow.+*Ifanythingfailsafterthis,weneedtorestoreit,soalsocheck+*forextensionspresence.+*/if(query.windows_available==0){-/*-*noadditionalwindowsareavailableforthisdevice.-*Wemightbeabletoreallocatetheexistingwindow,-*tradinginforalargerpagesize.-*/-dev_dbg(&dev->dev,"no free dynamic windows");-gotoout_failed;+default_win=of_find_property(pdn,"ibm,dma-window",NULL);+if(!default_win)+gotoout_failed;++reset_win_ext=ddw_read_ext(pdn,DDW_EXT_RESET_DMA_WIN,NULL);+if(reset_win_ext)+gotoout_failed;++remove_dma_window(pdn,ddw_avail,default_win);++/* Query again, to check if the window is available */+ret=query_ddw(dev,ddw_avail,&query,pdn);+if(ret!=0)+gotoout_restore_defwin;++if(query.windows_available==0){+/* no windows are available for this device. */+dev_dbg(&dev->dev,"no free dynamic windows");+gotoout_restore_defwin;+}}if(query.page_size&4){page_shift=24;/* 16MB */
@@ -1151,7 +1203,7 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)}else{dev_dbg(&dev->dev,"no supported direct page size in mask %x",query.page_size);-gotoout_failed;+gotoout_restore_defwin;}/* verify the window * number of ptes will map the partition *//* check largest block * page size > max memory hotplug addr */
@@ -1160,14 +1212,14 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)dev_dbg(&dev->dev,"can't map partition max 0x%llx with %llu ""%llu-sized pages\n",max_addr,query.largest_available_block,1ULL<<page_shift);-gotoout_failed;+gotoout_restore_defwin;}len=order_base_2(max_addr);win64=kzalloc(sizeof(structproperty),GFP_KERNEL);if(!win64){dev_info(&dev->dev,"couldn't allocate property for 64bit dma window\n");-gotoout_failed;+gotoout_restore_defwin;}win64->name=kstrdup(DIRECT64_PROPNAME,GFP_KERNEL);win64->value=ddwprop=kmalloc(sizeof(*ddwprop),GFP_KERNEL);
On LoPAR "DMA Window Manipulation Calls", it's recommended to remove the
default DMA window for the device, before attempting to configure a DDW,
in order to make the maximum resources available for the next DDW to be
created.
This is a requirement for using DDW on devices in which hypervisor
allows only one DMA window.
If setting up a new DDW fails anywhere after the removal of this
default DMA window, it's needed to restore the default DMA window.
For this, an implementation of ibm,reset-pe-dma-windows rtas call is
needed:
Platforms supporting the DDW option starting with LoPAR level 2.7 implement
ibm,ddw-extensions. The first extension available (index 2) carries the
token for ibm,reset-pe-dma-windows rtas call, which is used to restore
the default DMA window for a device, if it has been deleted.
It does so by resetting the TCE table allocation for the PE to it's
boot time value, available in "ibm,dma-window" device tree node.
Signed-off-by: Leonardo Bras <redacted>
---
arch/powerpc/platforms/pseries/iommu.c | 83 +++++++++++++++++++++-----
1 file changed, 69 insertions(+), 14 deletions(-)
@@ -1122,7 +1154,7 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn) if (ret) goto out_failed;- /*+ /* * Query if there is a second window of size to map the * whole partition. Query returns number of windows, largest * block assigned to PE (partition endpoint), and two bitmasks
@@ -1133,14 +1165,34 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn) if (ret != 0) goto out_failed;+ /*+ * If there is no window available, remove the default DMA window,+ * if it's present. This will make all the resources available to the+ * new DDW window.+ * If anything fails after this, we need to restore it, so also check+ * for extensions presence.+ */ if (query.windows_available == 0) {- /*- * no additional windows are available for this device.- * We might be able to reallocate the existing window,- * trading in for a larger page size.- */- dev_dbg(&dev->dev, "no free dynamic windows");- goto out_failed;+ default_win = of_find_property(pdn, "ibm,dma-window", NULL);+ if (!default_win)+ goto out_failed;++ reset_win_ext = ddw_read_ext(pdn, DDW_EXT_RESET_DMA_WIN, NULL);+ if (reset_win_ext)+ goto out_failed;++ remove_dma_window(pdn, ddw_avail, default_win);++ /* Query again, to check if the window is available */+ ret = query_ddw(dev, ddw_avail, &query, pdn);+ if (ret != 0)+ goto out_restore_defwin;++ if (query.windows_available == 0) {+ /* no windows are available for this device. */+ dev_dbg(&dev->dev, "no free dynamic windows");+ goto out_restore_defwin;+ } } if (query.page_size & 4) { page_shift = 24; /* 16MB */
@@ -1151,7 +1203,7 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn) } else { dev_dbg(&dev->dev, "no supported direct page size in mask %x", query.page_size);- goto out_failed;+ goto out_restore_defwin; } /* verify the window * number of ptes will map the partition */ /* check largest block * page size > max memory hotplug addr */
reset_win_ext potentially may be uninitialized here. Yeah I know it is
tied to default_win but still.
After looking at this function for a few minutes, it could use some
refactoring (way too many gotos) such as:
1. move (query.page_size & xx) checks before "if
(query.windows_available == 0)"
2. move "win64 = kzalloc(sizeof(struct property), GFP_KERNEL)" before
"if (query.windows_available == 0)"
3. call "reset_dma_window(dev, pdn)" inside the "if
(query.windows_available == 0)" branch.
Then you can drop all "goto out_restore_defwin" and move default_win and
reset_win_ext inside "if (query.windows_available == 0)".
The rest of the series is good as it is, however it may conflict with
https://patchwork.ozlabs.org/project/linuxppc-dev/patch/20200713062348.100552-1-aik@ozlabs.ru/
and the patchset it is made on top of -
https://patchwork.ozlabs.org/project/linuxppc-dev/list/?series=188385 .
thanks,
From: Leonardo Bras <hidden> Date: 2020-07-14 02:40:39
Thank you for this feedback Alexey!
On Mon, 2020-07-13 at 17:33 +1000, Alexey Kardashevskiy wrote:
[...]
quoted
- int len, ret;
+ int len, ret, reset_win_ext;
Make it "reset_token".
Oh, it's not a token here, it just checks if the reset_win extension
exists. The token would be returned in *value, but since we did not
need it here, it's not copied.
quoted
[...]
-out_failed:
+out_restore_defwin:
+ if (default_win && reset_win_ext == 0)
reset_win_ext potentially may be uninitialized here. Yeah I know it is
tied to default_win but still.
I can't see it being used uninitialized here, as you said it's tied to
default_win.
Could you please tell me how it can be used uninitialized here, or what
is bad by doing this way?
After looking at this function for a few minutes, it could use some
refactoring (way too many gotos) such as:
Moving 'page_size selection' above 'checking windows available' will
need us to duplicate the 'page_size selection' after the new query,
inside the if.
I mean, as query will be done again, it will need to get the (new) page
size.
Then you can drop all "goto out_restore_defwin" and move default_win and
reset_win_ext inside "if (query.windows_available == 0)".
I did all changes suggested locally and did some analysis in the
result:
I did not see a way to put default_win and reset_win_ext inside
"if (query.windows_available == 0)", because if we still need a way to
know if the default window was removed, and if so, restore in case
anything ever fails ahead (like creating the node property).
But from that analysis I noted it's possible to remove all the new
"goto out_restore_defwin", if we do default_win = NULL if
ddw_read_ext() fails.
So testing only default_win should always be enough to say if the
default window was deleted, and reset_win_ext could be moved inside "if
(query.windows_available == 0)".
Also, it would avoid reset_win_ext being 'used uninitialized' and
"out_restore_defwin:" would not be needed.
Against the current patch, we would have something like this:
#####
static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
{
- int len, ret, reset_win_ext;
+ int len, ret;
struct ddw_query_response query;
struct ddw_create_response create;
int page_shift;
struct device_node *pdn)
* for extensions presence.
*/
if (query.windows_available == 0) {
+ int reset_win_ext;
default_win = of_find_property(pdn, "ibm,dma-window",
NULL);
if (!default_win)
goto out_failed;
reset_win_ext = ddw_read_ext(pdn,
DDW_EXT_RESET_DMA_WIN, NULL);
- if (reset_win_ext)
+ if (reset_win_ext){
+ default_win = NULL;
goto out_failed;
+ }
remove_dma_window(pdn, ddw_avail, default_win);
/* Query again, to check if the window is available */
ret = query_ddw(dev, ddw_avail, &query, pdn);
if (ret != 0)
- goto out_restore_defwin;
+ goto out_failed;
if (query.windows_available == 0) {
/* no windows are available for this device. */
dev_dbg(&dev->dev, "no free dynamic windows");
- goto out_restore_defwin;
+ goto out_failed;
}
}
if (query.page_size & 4) {
device_node *pdn)
} else {
dev_dbg(&dev->dev, "no supported direct page size in
mask %x",
query.page_size);
- goto out_restore_defwin;
+ goto out_failed;
}
/* verify the window * number of ptes will map the partition */
/* check largest block * page size > max memory hotplug addr */
Ok, I have no problem rebasing on top of those patchsets, but what
would you suggest to be done?
Would it be ok doing a big multi-author patchset, so we guarantee it
being applied in the correct order?
(You probably want me to rebase my patchset on top of Hellwig + yours,
right?)
Thank you for this feedback Alexey!
On Mon, 2020-07-13 at 17:33 +1000, Alexey Kardashevskiy wrote:
quoted
[...]
quoted
- int len, ret;
+ int len, ret, reset_win_ext;
Make it "reset_token".
Oh, it's not a token here, it just checks if the reset_win extension
exists. The token would be returned in *value, but since we did not
need it here, it's not copied.
ah right, so it is a bool actually.
quoted
quoted
[...]
-out_failed:
+out_restore_defwin:
+ if (default_win && reset_win_ext == 0)
reset_win_ext potentially may be uninitialized here. Yeah I know it is
tied to default_win but still.
I can't see it being used uninitialized here, as you said it's tied to
default_win.
Where it is declared - it is not initialized so in theory it can skip
"if (query.windows_available == 0)".
Could you please tell me how it can be used uninitialized here, or what
is bad by doing this way?
quoted
After looking at this function for a few minutes, it could use some
refactoring (way too many gotos) such as:
Moving 'page_size selection' above 'checking windows available' will
need us to duplicate the 'page_size selection' after the new query,
inside the if.
page_size selection is not going to change, why?
I mean, as query will be done again, it will need to get the (new) page
size.
Then you can drop all "goto out_restore_defwin" and move default_win and
reset_win_ext inside "if (query.windows_available == 0)".
I did all changes suggested locally and did some analysis in the
result:
I did not see a way to put default_win and reset_win_ext inside
"if (query.windows_available == 0)", because if we still need a way to
know if the default window was removed, and if so, restore in case
anything ever fails ahead (like creating the node property).
Ah, I missed that new out_restore_defwin label is between other exit
labels. Sorry :-/
quoted hunk
But from that analysis I noted it's possible to remove all the new
"goto out_restore_defwin", if we do default_win = NULL if
ddw_read_ext() fails.
So testing only default_win should always be enough to say if the
default window was deleted, and reset_win_ext could be moved inside "if
(query.windows_available == 0)".
Also, it would avoid reset_win_ext being 'used uninitialized' and
"out_restore_defwin:" would not be needed.
Against the current patch, we would have something like this:
#####
static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
{
- int len, ret, reset_win_ext;
+ int len, ret;
struct ddw_query_response query;
struct ddw_create_response create;
int page_shift;
struct device_node *pdn)
* for extensions presence.
*/
if (query.windows_available == 0) {
+ int reset_win_ext;
default_win = of_find_property(pdn, "ibm,dma-window",
NULL);
if (!default_win)
goto out_failed;
reset_win_ext = ddw_read_ext(pdn,
DDW_EXT_RESET_DMA_WIN, NULL);
- if (reset_win_ext)
+ if (reset_win_ext){
+ default_win = NULL;
goto out_failed;
+ }
This says "if we can reset, then we fail", no?
remove_dma_window(pdn, ddw_avail, default_win);
I think you can do "default_win=NULL" here and later at
out_restore_defwin check if it is NULL - then call reset.
quoted hunk
/* Query again, to check if the window is available */
ret = query_ddw(dev, ddw_avail, &query, pdn);
if (ret != 0)
- goto out_restore_defwin;
+ goto out_failed;
if (query.windows_available == 0) {
/* no windows are available for this device. */
dev_dbg(&dev->dev, "no free dynamic windows");
- goto out_restore_defwin;
+ goto out_failed;
}
}
if (query.page_size & 4) {
device_node *pdn)
} else {
dev_dbg(&dev->dev, "no supported direct page size in
mask %x",
query.page_size);
- goto out_restore_defwin;
+ goto out_failed;
}
/* verify the window * number of ptes will map the partition */
/* check largest block * page size > max memory hotplug addr */
From: Leonardo Bras <hidden> Date: 2020-07-14 06:30:41
On Tue, 2020-07-14 at 14:52 +1000, Alexey Kardashevskiy wrote:
On 14/07/2020 12:40, Leonardo Bras wrote:
quoted
Thank you for this feedback Alexey!
On Mon, 2020-07-13 at 17:33 +1000, Alexey Kardashevskiy wrote:
quoted
[...]
quoted
- int len, ret;
+ int len, ret, reset_win_ext;
Make it "reset_token".
Oh, it's not a token here, it just checks if the reset_win extension
exists. The token would be returned in *value, but since we did not
need it here, it's not copied.
ah right, so it is a bool actually.
In fact I did it a int, as it's the return value of ddw_read_ext(),
which can return 0 on success and -error otherwise.
quoted
quoted
quoted
[...]
-out_failed:
+out_restore_defwin:
+ if (default_win && reset_win_ext == 0)
reset_win_ext potentially may be uninitialized here. Yeah I know it is
tied to default_win but still.
I can't see it being used uninitialized here, as you said it's tied to
default_win.
Where it is declared - it is not initialized so in theory it can skip
"if (query.windows_available == 0)".
Humm, I thought doing if (default_win && reset_win_ext == 0) would
guarantee default_win to be tested before reset_win_ext is ever tested,
so I could control it using default_win.
quoted
Could you please tell me how it can be used uninitialized here, or what
is bad by doing this way?
quoted
After looking at this function for a few minutes, it could use some
refactoring (way too many gotos) such as:
Moving 'page_size selection' above 'checking windows available' will
need us to duplicate the 'page_size selection' after the new query,
inside the if.
page_size selection is not going to change, why?
In theory, a query after freeing the default DMA window could have a
different (bigger) page size, so we should test again.
quoted
I mean, as query will be done again, it will need to get the (new) page
size.
quoted
2. move "win64 = kzalloc(sizeof(struct property), GFP_KERNEL)" before
"if (query.windows_available == 0)"
3. call "reset_dma_window(dev, pdn)" inside the "if
(query.windows_available == 0)" branch.
Then you can drop all "goto out_restore_defwin" and move default_win and
reset_win_ext inside "if (query.windows_available == 0)".
I did all changes suggested locally and did some analysis in the
result:
I did not see a way to put default_win and reset_win_ext inside
"if (query.windows_available == 0)", because if we still need a way to
know if the default window was removed, and if so, restore in case
anything ever fails ahead (like creating the node property).
Ah, I missed that new out_restore_defwin label is between other exit
labels. Sorry :-/
quoted
reset_win_ext = ddw_read_ext(pdn,
DDW_EXT_RESET_DMA_WIN, NULL);
- if (reset_win_ext)
+ if (reset_win_ext){
+ default_win = NULL;
goto out_failed;
+ }
This says "if we can reset, then we fail", no?
Here ddw_read_ext() should return 0 if extension was found, and
(-EINVAL, -ENODATA or -EOVERFLOW) otherwise.
So it should return nonzero if we can't find the extension, in which
case we should fail.
quoted
remove_dma_window(pdn, ddw_avail, default_win);
I think you can do "default_win=NULL" here and later at
out_restore_defwin check if it is NULL - then call reset.
Currently I initialize 'default_win = NULL', and it only changes when I
read the default DMA window. If reset is not available I restore it to
NULL, so it will be not-NULL only when the have removed the default DMA
window.
If I make it NULL here, we either never reset the default DMA window
(as it is now "if (default_win)" ) or we may always reset it (in case
"if (default_win == NULL)").
If you think it's better, I can create a bool variable like
"default_win_removed", initialized with 'false', which can be assigned
here with 'true' and test in the end if(default_win_removed) reset();
This would allow to move default_win inside this 'if block'.
What do you think?
quoted
[...]
-out_restore_defwin:
- if (default_win && reset_win_ext == 0)
+out_failed:
+ if (default_win)
reset_dma_window(dev, pdn);
-out_failed:
fpdn = kzalloc(sizeof(*fpdn), GFP_KERNEL);
if (!fpdn)
goto out_unlock;
#####
What do you think?
struct device_node *pdn)
if (ret != 0)
goto out_failed;
+ /*
+ * If there is no window available, remove the default DMA
window,
+ * if it's present. This will make all the resources available
to the
+ * new DDW window.
+ * If anything fails after this, we need to restore it, so also
check
+ * for extensions presence.
+ */
if (query.windows_available == 0) {
- /*
- * no additional windows are available for this device.
- * We might be able to reallocate the existing window,
- * trading in for a larger page size.
- */
- dev_dbg(&dev->dev, "no free dynamic windows");
- goto out_failed;
+ int reset_win_ext;
+
+ default_win = of_find_property(pdn, "ibm,dma-window",
NULL);
+ if (!default_win)
+ goto out_failed;
+
+ reset_win_ext = ddw_read_ext(pdn,
DDW_EXT_RESET_DMA_WIN, NULL);
+ if (reset_win_ext) {
+ default_win = NULL;
+ goto out_failed;
+ }
+
+ remove_dma_window(pdn, ddw_avail, default_win);
+
+ /* Query again, to check if the window is available */
+ ret = query_ddw(dev, ddw_avail, &query, pdn);
+ if (ret != 0)
+ goto out_failed;
+
+ if (query.windows_available == 0) {
+ /* no windows are available for this device. */
+ dev_dbg(&dev->dev, "no free dynamic windows");
+ goto out_failed;
+ }
}
if (query.page_size & 4) {
page_shift = 24; /* 16MB */
From: Leonardo Bras <hidden> Date: 2020-07-03 06:20:20
As of today, if the biggest DDW that can be created can't map the whole
partition, it's creation is skipped and the default DMA window
"ibm,dma-window" is used instead.
Usually this DDW is bigger than the default DMA window, and it performs
better, so it would be nice to use it instead.
The ddw created will be used for direct mapping by default.
If it's not available, indirect mapping sill be used instead.
As there will never have both mappings at the same time, the same property
name can be used for the created DDW.
So renaming
define DIRECT64_PROPNAME "linux,direct64-ddr-window-info"
to
define DMA64_PROPNAME "linux,dma64-ddr-window-info"
looks the right thing to do.
Signed-off-by: Leonardo Bras <redacted>
---
arch/powerpc/platforms/pseries/iommu.c | 38 ++++++++++++++++----------
1 file changed, 24 insertions(+), 14 deletions(-)
@@ -704,8 +704,13 @@ static void pci_dma_bus_setup_pSeriesLP(struct pci_bus *bus)break;}+/* If there is a DDW available, use it instead */+alt_dma_window=of_get_property(pdn,DMA64_PROPNAME,NULL);+if(alt_dma_window)+dma_window=alt_dma_window;+if(dma_window==NULL){-pr_debug(" no ibm,dma-window property !\n");+pr_debug(" no ibm,dma-window nor linux,dma64-ddr-window-info property !\n");return;}
@@ -869,8 +874,8 @@ static int find_existing_ddw_windows(void)if(!firmware_has_feature(FW_FEATURE_LPAR))return0;-for_each_node_with_property(pdn,DIRECT64_PROPNAME){-direct64=of_get_property(pdn,DIRECT64_PROPNAME,&len);+for_each_node_with_property(pdn,DMA64_PROPNAME){+direct64=of_get_property(pdn,DMA64_PROPNAME,&len);if(!direct64)continue;
@@ -1205,23 +1210,26 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)query.page_size);gotoout_restore_defwin;}+/* verify the window * number of ptes will map the partition */-/* check largest block * page size > max memory hotplug addr */max_addr=ddw_memory_hotplug_max();if(query.largest_available_block<(max_addr>>page_shift)){-dev_dbg(&dev->dev,"can't map partition max 0x%llx with %llu "-"%llu-sized pages\n",max_addr,query.largest_available_block,-1ULL<<page_shift);-gotoout_restore_defwin;+dev_dbg(&dev->dev,"can't map partition max 0x%llx with %llu %llu-sized pages\n",+max_addr,query.largest_available_block,+1ULL<<page_shift);++len=order_base_2(query.largest_available_block<<page_shift);+}else{+len=order_base_2(max_addr);}-len=order_base_2(max_addr);+win64=kzalloc(sizeof(structproperty),GFP_KERNEL);if(!win64){dev_info(&dev->dev,"couldn't allocate property for 64bit dma window\n");gotoout_restore_defwin;}-win64->name=kstrdup(DIRECT64_PROPNAME,GFP_KERNEL);+win64->name=kstrdup(DMA64_PROPNAME,GFP_KERNEL);win64->value=ddwprop=kmalloc(sizeof(*ddwprop),GFP_KERNEL);win64->length=sizeof(*ddwprop);if(!win64->name||!win64->value){
@@ -1268,7 +1276,9 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)list_add(&window->list,&direct_window_list);spin_unlock(&direct_window_list_lock);-dma_addr=be64_to_cpu(ddwprop->dma_base);+/* Only returns the dma_addr if DDW maps the whole partition */+if(len==order_base_2(max_addr))+dma_addr=be64_to_cpu(ddwprop->dma_base);gotoout_unlock;out_free_window:
From: Leonardo Bras <hidden> Date: 2020-07-03 06:20:24
A previous change introduced the usage of DDW as a bigger indirect DMA
mapping when the DDW available size does not map the whole partition.
As most of the code that manipulates direct mappings was reused for
indirect mappings, it's necessary to rename all names and debug/info
messages to reflect that it can be used for both kinds of mapping.
Also, defines DEFAULT_DMA_WIN as "ibm,dma-window" to document that
it's the name of the default DMA window.
Those changes are not supposed to change how the code works in any
way, just adjust naming.
Signed-off-by: Leonardo Bras <redacted>
---
arch/powerpc/platforms/pseries/iommu.c | 101 +++++++++++++------------
1 file changed, 53 insertions(+), 48 deletions(-)
@@ -359,12 +359,13 @@ struct ddw_create_response {u32addr_lo;};-staticLIST_HEAD(direct_window_list);+staticLIST_HEAD(dma_win_list);/* prevents races between memory on/offline and window creation */-staticDEFINE_SPINLOCK(direct_window_list_lock);+staticDEFINE_SPINLOCK(dma_win_list_lock);/* protects initializing window twice for same device */-staticDEFINE_MUTEX(direct_window_init_mutex);+staticDEFINE_MUTEX(dma_win_init_mutex);#define DMA64_PROPNAME "linux,dma64-ddr-window-info"+#define DEFAULT_DMA_WIN "ibm,dma-window"staticinttce_clearrange_multi_pSeriesLP(unsignedlongstart_pfn,unsignedlongnum_pfn,constvoid*arg)
@@ -697,9 +698,12 @@ static void pci_dma_bus_setup_pSeriesLP(struct pci_bus *bus)pr_debug("pci_dma_bus_setup_pSeriesLP: setting up bus %pOF\n",dn);-/* Find nearest ibm,dma-window, walking up the device tree */+/*+*Findnearestibm,dma-window(defaultDMAwindow),walkingupthe+*devicetree+*/for(pdn=dn;pdn!=NULL;pdn=pdn->parent){-dma_window=of_get_property(pdn,"ibm,dma-window",NULL);+dma_window=of_get_property(pdn,DEFAULT_DMA_WIN,NULL);if(dma_window!=NULL)break;}
@@ -710,7 +714,8 @@ static void pci_dma_bus_setup_pSeriesLP(struct pci_bus *bus)dma_window=alt_dma_window;if(dma_window==NULL){-pr_debug(" no ibm,dma-window nor linux,dma64-ddr-window-info property !\n");+pr_debug(" no %s nor %s property !\n",+DEFAULT_DMA_WIN,DMA64_PROPNAME);return;}
@@ -808,11 +813,11 @@ static void remove_dma_window(struct device_node *np, u32 *ddw_avail,ret=rtas_call(ddw_avail[DDW_REMOVE_PE_DMA_WIN],1,1,NULL,liobn);if(ret)-pr_warn("%pOF: failed to remove direct window: rtas returned "+pr_warn("%pOF: failed to remove dma window: rtas returned ""%d to ibm,remove-pe-dma-window(%x) %llx\n",np,ret,ddw_avail[DDW_REMOVE_PE_DMA_WIN],liobn);else-pr_debug("%pOF: successfully removed direct window: rtas returned "+pr_debug("%pOF: successfully removed dma window: rtas returned ""%d to ibm,remove-pe-dma-window(%x) %llx\n",np,ret,ddw_avail[DDW_REMOVE_PE_DMA_WIN],liobn);}
@@ -840,26 +845,26 @@ static void remove_ddw(struct device_node *np, bool remove_prop)ret=of_remove_property(np,win);if(ret)-pr_warn("%pOF: failed to remove direct window property: %d\n",+pr_warn("%pOF: failed to remove dma window property: %d\n",np,ret);}staticu64find_existing_ddw(structdevice_node*pdn){-structdirect_window*window;-conststructdynamic_dma_window_prop*direct64;+structdma_win*window;+conststructdynamic_dma_window_prop*dma64;u64dma_addr=0;-spin_lock(&direct_window_list_lock);+spin_lock(&dma_win_list_lock);/* check if we already created a window and dupe that config if so */-list_for_each_entry(window,&direct_window_list,list){+list_for_each_entry(window,&dma_win_list,list){if(window->device==pdn){-direct64=window->prop;-dma_addr=be64_to_cpu(direct64->dma_base);+dma64=window->prop;+dma_addr=be64_to_cpu(dma64->dma_base);break;}}-spin_unlock(&direct_window_list_lock);+spin_unlock(&dma_win_list_lock);returndma_addr;}
@@ -868,15 +873,15 @@ static int find_existing_ddw_windows(void){intlen;structdevice_node*pdn;-structdirect_window*window;-conststructdynamic_dma_window_prop*direct64;+structdma_win*window;+conststructdynamic_dma_window_prop*dma64;if(!firmware_has_feature(FW_FEATURE_LPAR))return0;for_each_node_with_property(pdn,DMA64_PROPNAME){-direct64=of_get_property(pdn,DMA64_PROPNAME,&len);-if(!direct64)+dma64=of_get_property(pdn,DMA64_PROPNAME,&len);+if(!dma64)continue;window=kzalloc(sizeof(*window),GFP_KERNEL);
@@ -887,10 +892,10 @@ static int find_existing_ddw_windows(void)}window->device=pdn;-window->prop=direct64;-spin_lock(&direct_window_list_lock);-list_add(&window->list,&direct_window_list);-spin_unlock(&direct_window_list_lock);+window->prop=dma64;+spin_lock(&dma_win_list_lock);+list_add(&window->list,&dma_win_list);+spin_unlock(&dma_win_list_lock);}return0;