2019-12-03 20:26:39 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* DMABUF System heap exporter
*
* Copyright ( C ) 2011 Google , Inc .
2020-11-22 02:49:58 +03:00
* Copyright ( C ) 2019 , 2020 Linaro Ltd .
*
* Portions based off of Andrew Davis ' SRAM heap :
* Copyright ( C ) 2019 Texas Instruments Incorporated - http : //www.ti.com/
* Andrew F . Davis < afd @ ti . com >
2019-12-03 20:26:39 +03:00
*/
# include <linux/dma-buf.h>
# include <linux/dma-mapping.h>
# include <linux/dma-heap.h>
# include <linux/err.h>
# include <linux/highmem.h>
# include <linux/mm.h>
# include <linux/module.h>
# include <linux/scatterlist.h>
# include <linux/slab.h>
2020-11-22 02:49:58 +03:00
# include <linux/vmalloc.h>
static struct dma_heap * sys_heap ;
2019-12-03 20:26:39 +03:00
2020-11-22 02:49:58 +03:00
struct system_heap_buffer {
struct dma_heap * heap ;
struct list_head attachments ;
struct mutex lock ;
unsigned long len ;
struct sg_table sg_table ;
int vmap_cnt ;
void * vaddr ;
} ;
2019-12-03 20:26:39 +03:00
2020-11-22 02:49:58 +03:00
struct dma_heap_attachment {
struct device * dev ;
struct sg_table * table ;
struct list_head list ;
dma-buf: heaps: Skip sync if not mapped
This patch is basically a port of Ørjan Eide's similar patch for ION
https://lore.kernel.org/lkml/20200414134629.54567-1-orjan.eide@arm.com/
Only sync the sg-list of dma-buf heap attachment when the attachment
is actually mapped on the device.
dma-bufs may be synced at any time. It can be reached from user space
via DMA_BUF_IOCTL_SYNC, so there are no guarantees from callers on when
syncs may be attempted, and dma_buf_end_cpu_access() and
dma_buf_begin_cpu_access() may not be paired.
Since the sg_list's dma_address isn't set up until the buffer is used
on the device, and dma_map_sg() is called on it, the dma_address will be
NULL if sync is attempted on the dma-buf before it's mapped on a device.
Before v5.0 (commit 55897af63091 ("dma-direct: merge swiotlb_dma_ops
into the dma_direct code")) this was a problem as the dma-api (at least
the swiotlb_dma_ops on arm64) would use the potentially invalid
dma_address. How that failed depended on how the device handled physical
address 0. If 0 was a valid address to physical ram, that page would get
flushed a lot, while the actual pages in the buffer would not get synced
correctly. While if 0 is an invalid physical address it may cause a
fault and trigger a crash.
In v5.0 this was incidentally fixed by commit 55897af63091 ("dma-direct:
merge swiotlb_dma_ops into the dma_direct code"), as this moved the
dma-api to use the page pointer in the sg_list, and (for Ion buffers at
least) this will always be valid if the sg_list exists at all.
But, this issue is re-introduced in v5.3 with
commit 449fa54d6815 ("dma-direct: correct the physical addr in
dma_direct_sync_sg_for_cpu/device") moves the dma-api back to the old
behaviour and picks the dma_address that may be invalid.
dma-buf core doesn't ensure that the buffer is mapped on the device, and
thus have a valid sg_list, before calling the exporter's
begin_cpu_access.
Logic and commit message originally by: Ørjan Eide <orjan.eide@arm.com>
Cc: Sumit Semwal <sumit.semwal@linaro.org>
Cc: Liam Mark <lmark@codeaurora.org>
Cc: Laura Abbott <labbott@kernel.org>
Cc: Brian Starkey <Brian.Starkey@arm.com>
Cc: Hridya Valsaraju <hridya@google.com>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Sandeep Patil <sspatil@google.com>
Cc: Daniel Mentz <danielmentz@google.com>
Cc: Chris Goldsworthy <cgoldswo@codeaurora.org>
Cc: Ørjan Eide <orjan.eide@arm.com>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: Ezequiel Garcia <ezequiel@collabora.com>
Cc: Simon Ser <contact@emersion.fr>
Cc: James Jones <jajones@nvidia.com>
Cc: linux-media@vger.kernel.org
Cc: dri-devel@lists.freedesktop.org
Reviewed-by: Brian Starkey <brian.starkey@arm.com>
Signed-off-by: John Stultz <john.stultz@linaro.org>
Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20201121235002.69945-5-john.stultz@linaro.org
2020-11-22 02:50:01 +03:00
bool mapped ;
2020-11-22 02:49:58 +03:00
} ;
2019-12-03 20:26:39 +03:00
dma-buf: system_heap: avoid reclaim for order 4
Using order 4 pages would be helpful for IOMMUs mapping, but trying to get
order 4 pages could spend quite much time in the page allocation. From
the perspective of responsiveness, the deterministic memory allocation
speed, I think, is quite important.
The order 4 allocation with __GFP_RECLAIM may spend much time in reclaim
and compation logic. __GFP_NORETRY also may affect. These cause
unpredictable delay.
To get reasonable allocation speed from dma-buf system heap, use
HIGH_ORDER_GFP for order 4 to avoid reclaim. And let me remove
meaningless __GFP_COMP for order 0.
According to my tests, order 4 with MID_ORDER_GFP could get more number
of order 4 pages but the elapsed times could be very slow.
time order 8 order 4 order 0
584 usec 0 160 0
28,428 usec 0 160 0
100,701 usec 0 160 0
76,645 usec 0 160 0
25,522 usec 0 160 0
38,798 usec 0 160 0
89,012 usec 0 160 0
23,015 usec 0 160 0
73,360 usec 0 160 0
76,953 usec 0 160 0
31,492 usec 0 160 0
75,889 usec 0 160 0
84,551 usec 0 160 0
84,352 usec 0 160 0
57,103 usec 0 160 0
93,452 usec 0 160 0
If HIGH_ORDER_GFP is used for order 4, the number of order 4 could be
decreased but the elapsed time results were quite stable and fast enough.
time order 8 order 4 order 0
1,356 usec 0 155 80
1,901 usec 0 11 2384
1,912 usec 0 0 2560
1,911 usec 0 0 2560
1,884 usec 0 0 2560
1,577 usec 0 0 2560
1,366 usec 0 0 2560
1,711 usec 0 0 2560
1,635 usec 0 28 2112
544 usec 10 0 0
633 usec 2 128 0
848 usec 0 160 0
729 usec 0 160 0
1,000 usec 0 160 0
1,358 usec 0 160 0
2,638 usec 0 31 2064
Link: https://lkml.kernel.org/r/20230303050332.10138-1-jaewon31.kim@samsung.com
Signed-off-by: Jaewon Kim <jaewon31.kim@samsung.com>
Reviewed-by: John Stultz <jstultz@google.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Sumit Semwal <sumit.semwal@linaro.org>
Cc: T.J. Mercier <tjmercier@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2023-03-03 08:03:32 +03:00
# define LOW_ORDER_GFP (GFP_HIGHUSER | __GFP_ZERO)
2020-11-22 02:50:02 +03:00
# define HIGH_ORDER_GFP (((GFP_HIGHUSER | __GFP_ZERO | __GFP_NOWARN \
| __GFP_NORETRY ) & ~ __GFP_RECLAIM ) \
| __GFP_COMP )
dma-buf: system_heap: avoid reclaim for order 4
Using order 4 pages would be helpful for IOMMUs mapping, but trying to get
order 4 pages could spend quite much time in the page allocation. From
the perspective of responsiveness, the deterministic memory allocation
speed, I think, is quite important.
The order 4 allocation with __GFP_RECLAIM may spend much time in reclaim
and compation logic. __GFP_NORETRY also may affect. These cause
unpredictable delay.
To get reasonable allocation speed from dma-buf system heap, use
HIGH_ORDER_GFP for order 4 to avoid reclaim. And let me remove
meaningless __GFP_COMP for order 0.
According to my tests, order 4 with MID_ORDER_GFP could get more number
of order 4 pages but the elapsed times could be very slow.
time order 8 order 4 order 0
584 usec 0 160 0
28,428 usec 0 160 0
100,701 usec 0 160 0
76,645 usec 0 160 0
25,522 usec 0 160 0
38,798 usec 0 160 0
89,012 usec 0 160 0
23,015 usec 0 160 0
73,360 usec 0 160 0
76,953 usec 0 160 0
31,492 usec 0 160 0
75,889 usec 0 160 0
84,551 usec 0 160 0
84,352 usec 0 160 0
57,103 usec 0 160 0
93,452 usec 0 160 0
If HIGH_ORDER_GFP is used for order 4, the number of order 4 could be
decreased but the elapsed time results were quite stable and fast enough.
time order 8 order 4 order 0
1,356 usec 0 155 80
1,901 usec 0 11 2384
1,912 usec 0 0 2560
1,911 usec 0 0 2560
1,884 usec 0 0 2560
1,577 usec 0 0 2560
1,366 usec 0 0 2560
1,711 usec 0 0 2560
1,635 usec 0 28 2112
544 usec 10 0 0
633 usec 2 128 0
848 usec 0 160 0
729 usec 0 160 0
1,000 usec 0 160 0
1,358 usec 0 160 0
2,638 usec 0 31 2064
Link: https://lkml.kernel.org/r/20230303050332.10138-1-jaewon31.kim@samsung.com
Signed-off-by: Jaewon Kim <jaewon31.kim@samsung.com>
Reviewed-by: John Stultz <jstultz@google.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Sumit Semwal <sumit.semwal@linaro.org>
Cc: T.J. Mercier <tjmercier@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2023-03-03 08:03:32 +03:00
static gfp_t order_flags [ ] = { HIGH_ORDER_GFP , HIGH_ORDER_GFP , LOW_ORDER_GFP } ;
2020-11-22 02:50:02 +03:00
/*
* The selection of the orders used for allocation ( 1 MB , 64 K , 4 K ) is designed
* to match with the sizes often found in IOMMUs . Using order 4 pages instead
* of order 0 pages can significantly improve the performance of many IOMMUs
* by reducing TLB pressure and time spent updating page tables .
*/
static const unsigned int orders [ ] = { 8 , 4 , 0 } ;
# define NUM_ORDERS ARRAY_SIZE(orders)
2020-11-22 02:49:58 +03:00
static struct sg_table * dup_sg_table ( struct sg_table * table )
2019-12-03 20:26:39 +03:00
{
2020-11-22 02:49:58 +03:00
struct sg_table * new_table ;
int ret , i ;
struct scatterlist * sg , * new_sg ;
new_table = kzalloc ( sizeof ( * new_table ) , GFP_KERNEL ) ;
if ( ! new_table )
return ERR_PTR ( - ENOMEM ) ;
ret = sg_alloc_table ( new_table , table - > orig_nents , GFP_KERNEL ) ;
if ( ret ) {
kfree ( new_table ) ;
return ERR_PTR ( - ENOMEM ) ;
}
new_sg = new_table - > sgl ;
for_each_sgtable_sg ( table , sg , i ) {
sg_set_page ( new_sg , sg_page ( sg ) , sg - > length , sg - > offset ) ;
new_sg = sg_next ( new_sg ) ;
}
return new_table ;
}
static int system_heap_attach ( struct dma_buf * dmabuf ,
struct dma_buf_attachment * attachment )
{
struct system_heap_buffer * buffer = dmabuf - > priv ;
struct dma_heap_attachment * a ;
struct sg_table * table ;
a = kzalloc ( sizeof ( * a ) , GFP_KERNEL ) ;
if ( ! a )
return - ENOMEM ;
table = dup_sg_table ( & buffer - > sg_table ) ;
if ( IS_ERR ( table ) ) {
kfree ( a ) ;
return - ENOMEM ;
}
a - > table = table ;
a - > dev = attachment - > dev ;
INIT_LIST_HEAD ( & a - > list ) ;
dma-buf: heaps: Skip sync if not mapped
This patch is basically a port of Ørjan Eide's similar patch for ION
https://lore.kernel.org/lkml/20200414134629.54567-1-orjan.eide@arm.com/
Only sync the sg-list of dma-buf heap attachment when the attachment
is actually mapped on the device.
dma-bufs may be synced at any time. It can be reached from user space
via DMA_BUF_IOCTL_SYNC, so there are no guarantees from callers on when
syncs may be attempted, and dma_buf_end_cpu_access() and
dma_buf_begin_cpu_access() may not be paired.
Since the sg_list's dma_address isn't set up until the buffer is used
on the device, and dma_map_sg() is called on it, the dma_address will be
NULL if sync is attempted on the dma-buf before it's mapped on a device.
Before v5.0 (commit 55897af63091 ("dma-direct: merge swiotlb_dma_ops
into the dma_direct code")) this was a problem as the dma-api (at least
the swiotlb_dma_ops on arm64) would use the potentially invalid
dma_address. How that failed depended on how the device handled physical
address 0. If 0 was a valid address to physical ram, that page would get
flushed a lot, while the actual pages in the buffer would not get synced
correctly. While if 0 is an invalid physical address it may cause a
fault and trigger a crash.
In v5.0 this was incidentally fixed by commit 55897af63091 ("dma-direct:
merge swiotlb_dma_ops into the dma_direct code"), as this moved the
dma-api to use the page pointer in the sg_list, and (for Ion buffers at
least) this will always be valid if the sg_list exists at all.
But, this issue is re-introduced in v5.3 with
commit 449fa54d6815 ("dma-direct: correct the physical addr in
dma_direct_sync_sg_for_cpu/device") moves the dma-api back to the old
behaviour and picks the dma_address that may be invalid.
dma-buf core doesn't ensure that the buffer is mapped on the device, and
thus have a valid sg_list, before calling the exporter's
begin_cpu_access.
Logic and commit message originally by: Ørjan Eide <orjan.eide@arm.com>
Cc: Sumit Semwal <sumit.semwal@linaro.org>
Cc: Liam Mark <lmark@codeaurora.org>
Cc: Laura Abbott <labbott@kernel.org>
Cc: Brian Starkey <Brian.Starkey@arm.com>
Cc: Hridya Valsaraju <hridya@google.com>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Sandeep Patil <sspatil@google.com>
Cc: Daniel Mentz <danielmentz@google.com>
Cc: Chris Goldsworthy <cgoldswo@codeaurora.org>
Cc: Ørjan Eide <orjan.eide@arm.com>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: Ezequiel Garcia <ezequiel@collabora.com>
Cc: Simon Ser <contact@emersion.fr>
Cc: James Jones <jajones@nvidia.com>
Cc: linux-media@vger.kernel.org
Cc: dri-devel@lists.freedesktop.org
Reviewed-by: Brian Starkey <brian.starkey@arm.com>
Signed-off-by: John Stultz <john.stultz@linaro.org>
Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20201121235002.69945-5-john.stultz@linaro.org
2020-11-22 02:50:01 +03:00
a - > mapped = false ;
2020-11-22 02:49:58 +03:00
attachment - > priv = a ;
mutex_lock ( & buffer - > lock ) ;
list_add ( & a - > list , & buffer - > attachments ) ;
mutex_unlock ( & buffer - > lock ) ;
return 0 ;
}
static void system_heap_detach ( struct dma_buf * dmabuf ,
struct dma_buf_attachment * attachment )
{
struct system_heap_buffer * buffer = dmabuf - > priv ;
struct dma_heap_attachment * a = attachment - > priv ;
mutex_lock ( & buffer - > lock ) ;
list_del ( & a - > list ) ;
mutex_unlock ( & buffer - > lock ) ;
sg_free_table ( a - > table ) ;
kfree ( a - > table ) ;
kfree ( a ) ;
}
static struct sg_table * system_heap_map_dma_buf ( struct dma_buf_attachment * attachment ,
enum dma_data_direction direction )
{
struct dma_heap_attachment * a = attachment - > priv ;
struct sg_table * table = a - > table ;
int ret ;
ret = dma_map_sgtable ( attachment - > dev , table , direction , 0 ) ;
if ( ret )
return ERR_PTR ( ret ) ;
dma-buf: heaps: Skip sync if not mapped
This patch is basically a port of Ørjan Eide's similar patch for ION
https://lore.kernel.org/lkml/20200414134629.54567-1-orjan.eide@arm.com/
Only sync the sg-list of dma-buf heap attachment when the attachment
is actually mapped on the device.
dma-bufs may be synced at any time. It can be reached from user space
via DMA_BUF_IOCTL_SYNC, so there are no guarantees from callers on when
syncs may be attempted, and dma_buf_end_cpu_access() and
dma_buf_begin_cpu_access() may not be paired.
Since the sg_list's dma_address isn't set up until the buffer is used
on the device, and dma_map_sg() is called on it, the dma_address will be
NULL if sync is attempted on the dma-buf before it's mapped on a device.
Before v5.0 (commit 55897af63091 ("dma-direct: merge swiotlb_dma_ops
into the dma_direct code")) this was a problem as the dma-api (at least
the swiotlb_dma_ops on arm64) would use the potentially invalid
dma_address. How that failed depended on how the device handled physical
address 0. If 0 was a valid address to physical ram, that page would get
flushed a lot, while the actual pages in the buffer would not get synced
correctly. While if 0 is an invalid physical address it may cause a
fault and trigger a crash.
In v5.0 this was incidentally fixed by commit 55897af63091 ("dma-direct:
merge swiotlb_dma_ops into the dma_direct code"), as this moved the
dma-api to use the page pointer in the sg_list, and (for Ion buffers at
least) this will always be valid if the sg_list exists at all.
But, this issue is re-introduced in v5.3 with
commit 449fa54d6815 ("dma-direct: correct the physical addr in
dma_direct_sync_sg_for_cpu/device") moves the dma-api back to the old
behaviour and picks the dma_address that may be invalid.
dma-buf core doesn't ensure that the buffer is mapped on the device, and
thus have a valid sg_list, before calling the exporter's
begin_cpu_access.
Logic and commit message originally by: Ørjan Eide <orjan.eide@arm.com>
Cc: Sumit Semwal <sumit.semwal@linaro.org>
Cc: Liam Mark <lmark@codeaurora.org>
Cc: Laura Abbott <labbott@kernel.org>
Cc: Brian Starkey <Brian.Starkey@arm.com>
Cc: Hridya Valsaraju <hridya@google.com>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Sandeep Patil <sspatil@google.com>
Cc: Daniel Mentz <danielmentz@google.com>
Cc: Chris Goldsworthy <cgoldswo@codeaurora.org>
Cc: Ørjan Eide <orjan.eide@arm.com>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: Ezequiel Garcia <ezequiel@collabora.com>
Cc: Simon Ser <contact@emersion.fr>
Cc: James Jones <jajones@nvidia.com>
Cc: linux-media@vger.kernel.org
Cc: dri-devel@lists.freedesktop.org
Reviewed-by: Brian Starkey <brian.starkey@arm.com>
Signed-off-by: John Stultz <john.stultz@linaro.org>
Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20201121235002.69945-5-john.stultz@linaro.org
2020-11-22 02:50:01 +03:00
a - > mapped = true ;
2020-11-22 02:49:58 +03:00
return table ;
}
static void system_heap_unmap_dma_buf ( struct dma_buf_attachment * attachment ,
struct sg_table * table ,
enum dma_data_direction direction )
{
dma-buf: heaps: Skip sync if not mapped
This patch is basically a port of Ørjan Eide's similar patch for ION
https://lore.kernel.org/lkml/20200414134629.54567-1-orjan.eide@arm.com/
Only sync the sg-list of dma-buf heap attachment when the attachment
is actually mapped on the device.
dma-bufs may be synced at any time. It can be reached from user space
via DMA_BUF_IOCTL_SYNC, so there are no guarantees from callers on when
syncs may be attempted, and dma_buf_end_cpu_access() and
dma_buf_begin_cpu_access() may not be paired.
Since the sg_list's dma_address isn't set up until the buffer is used
on the device, and dma_map_sg() is called on it, the dma_address will be
NULL if sync is attempted on the dma-buf before it's mapped on a device.
Before v5.0 (commit 55897af63091 ("dma-direct: merge swiotlb_dma_ops
into the dma_direct code")) this was a problem as the dma-api (at least
the swiotlb_dma_ops on arm64) would use the potentially invalid
dma_address. How that failed depended on how the device handled physical
address 0. If 0 was a valid address to physical ram, that page would get
flushed a lot, while the actual pages in the buffer would not get synced
correctly. While if 0 is an invalid physical address it may cause a
fault and trigger a crash.
In v5.0 this was incidentally fixed by commit 55897af63091 ("dma-direct:
merge swiotlb_dma_ops into the dma_direct code"), as this moved the
dma-api to use the page pointer in the sg_list, and (for Ion buffers at
least) this will always be valid if the sg_list exists at all.
But, this issue is re-introduced in v5.3 with
commit 449fa54d6815 ("dma-direct: correct the physical addr in
dma_direct_sync_sg_for_cpu/device") moves the dma-api back to the old
behaviour and picks the dma_address that may be invalid.
dma-buf core doesn't ensure that the buffer is mapped on the device, and
thus have a valid sg_list, before calling the exporter's
begin_cpu_access.
Logic and commit message originally by: Ørjan Eide <orjan.eide@arm.com>
Cc: Sumit Semwal <sumit.semwal@linaro.org>
Cc: Liam Mark <lmark@codeaurora.org>
Cc: Laura Abbott <labbott@kernel.org>
Cc: Brian Starkey <Brian.Starkey@arm.com>
Cc: Hridya Valsaraju <hridya@google.com>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Sandeep Patil <sspatil@google.com>
Cc: Daniel Mentz <danielmentz@google.com>
Cc: Chris Goldsworthy <cgoldswo@codeaurora.org>
Cc: Ørjan Eide <orjan.eide@arm.com>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: Ezequiel Garcia <ezequiel@collabora.com>
Cc: Simon Ser <contact@emersion.fr>
Cc: James Jones <jajones@nvidia.com>
Cc: linux-media@vger.kernel.org
Cc: dri-devel@lists.freedesktop.org
Reviewed-by: Brian Starkey <brian.starkey@arm.com>
Signed-off-by: John Stultz <john.stultz@linaro.org>
Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20201121235002.69945-5-john.stultz@linaro.org
2020-11-22 02:50:01 +03:00
struct dma_heap_attachment * a = attachment - > priv ;
a - > mapped = false ;
2020-11-22 02:49:58 +03:00
dma_unmap_sgtable ( attachment - > dev , table , direction , 0 ) ;
}
static int system_heap_dma_buf_begin_cpu_access ( struct dma_buf * dmabuf ,
enum dma_data_direction direction )
{
struct system_heap_buffer * buffer = dmabuf - > priv ;
struct dma_heap_attachment * a ;
mutex_lock ( & buffer - > lock ) ;
if ( buffer - > vmap_cnt )
invalidate_kernel_vmap_range ( buffer - > vaddr , buffer - > len ) ;
list_for_each_entry ( a , & buffer - > attachments , list ) {
dma-buf: heaps: Skip sync if not mapped
This patch is basically a port of Ørjan Eide's similar patch for ION
https://lore.kernel.org/lkml/20200414134629.54567-1-orjan.eide@arm.com/
Only sync the sg-list of dma-buf heap attachment when the attachment
is actually mapped on the device.
dma-bufs may be synced at any time. It can be reached from user space
via DMA_BUF_IOCTL_SYNC, so there are no guarantees from callers on when
syncs may be attempted, and dma_buf_end_cpu_access() and
dma_buf_begin_cpu_access() may not be paired.
Since the sg_list's dma_address isn't set up until the buffer is used
on the device, and dma_map_sg() is called on it, the dma_address will be
NULL if sync is attempted on the dma-buf before it's mapped on a device.
Before v5.0 (commit 55897af63091 ("dma-direct: merge swiotlb_dma_ops
into the dma_direct code")) this was a problem as the dma-api (at least
the swiotlb_dma_ops on arm64) would use the potentially invalid
dma_address. How that failed depended on how the device handled physical
address 0. If 0 was a valid address to physical ram, that page would get
flushed a lot, while the actual pages in the buffer would not get synced
correctly. While if 0 is an invalid physical address it may cause a
fault and trigger a crash.
In v5.0 this was incidentally fixed by commit 55897af63091 ("dma-direct:
merge swiotlb_dma_ops into the dma_direct code"), as this moved the
dma-api to use the page pointer in the sg_list, and (for Ion buffers at
least) this will always be valid if the sg_list exists at all.
But, this issue is re-introduced in v5.3 with
commit 449fa54d6815 ("dma-direct: correct the physical addr in
dma_direct_sync_sg_for_cpu/device") moves the dma-api back to the old
behaviour and picks the dma_address that may be invalid.
dma-buf core doesn't ensure that the buffer is mapped on the device, and
thus have a valid sg_list, before calling the exporter's
begin_cpu_access.
Logic and commit message originally by: Ørjan Eide <orjan.eide@arm.com>
Cc: Sumit Semwal <sumit.semwal@linaro.org>
Cc: Liam Mark <lmark@codeaurora.org>
Cc: Laura Abbott <labbott@kernel.org>
Cc: Brian Starkey <Brian.Starkey@arm.com>
Cc: Hridya Valsaraju <hridya@google.com>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Sandeep Patil <sspatil@google.com>
Cc: Daniel Mentz <danielmentz@google.com>
Cc: Chris Goldsworthy <cgoldswo@codeaurora.org>
Cc: Ørjan Eide <orjan.eide@arm.com>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: Ezequiel Garcia <ezequiel@collabora.com>
Cc: Simon Ser <contact@emersion.fr>
Cc: James Jones <jajones@nvidia.com>
Cc: linux-media@vger.kernel.org
Cc: dri-devel@lists.freedesktop.org
Reviewed-by: Brian Starkey <brian.starkey@arm.com>
Signed-off-by: John Stultz <john.stultz@linaro.org>
Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20201121235002.69945-5-john.stultz@linaro.org
2020-11-22 02:50:01 +03:00
if ( ! a - > mapped )
continue ;
2020-11-22 02:49:58 +03:00
dma_sync_sgtable_for_cpu ( a - > dev , a - > table , direction ) ;
}
mutex_unlock ( & buffer - > lock ) ;
return 0 ;
}
static int system_heap_dma_buf_end_cpu_access ( struct dma_buf * dmabuf ,
enum dma_data_direction direction )
{
struct system_heap_buffer * buffer = dmabuf - > priv ;
struct dma_heap_attachment * a ;
mutex_lock ( & buffer - > lock ) ;
2019-12-03 20:26:39 +03:00
2020-11-22 02:49:58 +03:00
if ( buffer - > vmap_cnt )
flush_kernel_vmap_range ( buffer - > vaddr , buffer - > len ) ;
list_for_each_entry ( a , & buffer - > attachments , list ) {
dma-buf: heaps: Skip sync if not mapped
This patch is basically a port of Ørjan Eide's similar patch for ION
https://lore.kernel.org/lkml/20200414134629.54567-1-orjan.eide@arm.com/
Only sync the sg-list of dma-buf heap attachment when the attachment
is actually mapped on the device.
dma-bufs may be synced at any time. It can be reached from user space
via DMA_BUF_IOCTL_SYNC, so there are no guarantees from callers on when
syncs may be attempted, and dma_buf_end_cpu_access() and
dma_buf_begin_cpu_access() may not be paired.
Since the sg_list's dma_address isn't set up until the buffer is used
on the device, and dma_map_sg() is called on it, the dma_address will be
NULL if sync is attempted on the dma-buf before it's mapped on a device.
Before v5.0 (commit 55897af63091 ("dma-direct: merge swiotlb_dma_ops
into the dma_direct code")) this was a problem as the dma-api (at least
the swiotlb_dma_ops on arm64) would use the potentially invalid
dma_address. How that failed depended on how the device handled physical
address 0. If 0 was a valid address to physical ram, that page would get
flushed a lot, while the actual pages in the buffer would not get synced
correctly. While if 0 is an invalid physical address it may cause a
fault and trigger a crash.
In v5.0 this was incidentally fixed by commit 55897af63091 ("dma-direct:
merge swiotlb_dma_ops into the dma_direct code"), as this moved the
dma-api to use the page pointer in the sg_list, and (for Ion buffers at
least) this will always be valid if the sg_list exists at all.
But, this issue is re-introduced in v5.3 with
commit 449fa54d6815 ("dma-direct: correct the physical addr in
dma_direct_sync_sg_for_cpu/device") moves the dma-api back to the old
behaviour and picks the dma_address that may be invalid.
dma-buf core doesn't ensure that the buffer is mapped on the device, and
thus have a valid sg_list, before calling the exporter's
begin_cpu_access.
Logic and commit message originally by: Ørjan Eide <orjan.eide@arm.com>
Cc: Sumit Semwal <sumit.semwal@linaro.org>
Cc: Liam Mark <lmark@codeaurora.org>
Cc: Laura Abbott <labbott@kernel.org>
Cc: Brian Starkey <Brian.Starkey@arm.com>
Cc: Hridya Valsaraju <hridya@google.com>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Sandeep Patil <sspatil@google.com>
Cc: Daniel Mentz <danielmentz@google.com>
Cc: Chris Goldsworthy <cgoldswo@codeaurora.org>
Cc: Ørjan Eide <orjan.eide@arm.com>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: Ezequiel Garcia <ezequiel@collabora.com>
Cc: Simon Ser <contact@emersion.fr>
Cc: James Jones <jajones@nvidia.com>
Cc: linux-media@vger.kernel.org
Cc: dri-devel@lists.freedesktop.org
Reviewed-by: Brian Starkey <brian.starkey@arm.com>
Signed-off-by: John Stultz <john.stultz@linaro.org>
Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20201121235002.69945-5-john.stultz@linaro.org
2020-11-22 02:50:01 +03:00
if ( ! a - > mapped )
continue ;
2020-11-22 02:49:58 +03:00
dma_sync_sgtable_for_device ( a - > dev , a - > table , direction ) ;
}
mutex_unlock ( & buffer - > lock ) ;
return 0 ;
}
static int system_heap_mmap ( struct dma_buf * dmabuf , struct vm_area_struct * vma )
{
struct system_heap_buffer * buffer = dmabuf - > priv ;
struct sg_table * table = & buffer - > sg_table ;
unsigned long addr = vma - > vm_start ;
struct sg_page_iter piter ;
int ret ;
for_each_sgtable_page ( table , & piter , vma - > vm_pgoff ) {
struct page * page = sg_page_iter_page ( & piter ) ;
ret = remap_pfn_range ( vma , addr , page_to_pfn ( page ) , PAGE_SIZE ,
vma - > vm_page_prot ) ;
if ( ret )
return ret ;
addr + = PAGE_SIZE ;
if ( addr > = vma - > vm_end )
return 0 ;
}
return 0 ;
}
static void * system_heap_do_vmap ( struct system_heap_buffer * buffer )
{
struct sg_table * table = & buffer - > sg_table ;
int npages = PAGE_ALIGN ( buffer - > len ) / PAGE_SIZE ;
struct page * * pages = vmalloc ( sizeof ( struct page * ) * npages ) ;
struct page * * tmp = pages ;
struct sg_page_iter piter ;
void * vaddr ;
if ( ! pages )
return ERR_PTR ( - ENOMEM ) ;
for_each_sgtable_page ( table , & piter , 0 ) {
WARN_ON ( tmp - pages > = npages ) ;
* tmp + + = sg_page_iter_page ( & piter ) ;
}
vaddr = vmap ( pages , npages , VM_MAP , PAGE_KERNEL ) ;
vfree ( pages ) ;
if ( ! vaddr )
return ERR_PTR ( - ENOMEM ) ;
return vaddr ;
}
2022-02-04 20:05:41 +03:00
static int system_heap_vmap ( struct dma_buf * dmabuf , struct iosys_map * map )
2020-11-22 02:49:58 +03:00
{
struct system_heap_buffer * buffer = dmabuf - > priv ;
void * vaddr ;
int ret = 0 ;
mutex_lock ( & buffer - > lock ) ;
if ( buffer - > vmap_cnt ) {
buffer - > vmap_cnt + + ;
2022-02-04 20:05:41 +03:00
iosys_map_set_vaddr ( map , buffer - > vaddr ) ;
2020-11-22 02:49:58 +03:00
goto out ;
}
vaddr = system_heap_do_vmap ( buffer ) ;
if ( IS_ERR ( vaddr ) ) {
ret = PTR_ERR ( vaddr ) ;
goto out ;
}
buffer - > vaddr = vaddr ;
buffer - > vmap_cnt + + ;
2022-02-04 20:05:41 +03:00
iosys_map_set_vaddr ( map , buffer - > vaddr ) ;
2020-11-22 02:49:58 +03:00
out :
mutex_unlock ( & buffer - > lock ) ;
return ret ;
}
2022-02-04 20:05:41 +03:00
static void system_heap_vunmap ( struct dma_buf * dmabuf , struct iosys_map * map )
2020-11-22 02:49:58 +03:00
{
struct system_heap_buffer * buffer = dmabuf - > priv ;
mutex_lock ( & buffer - > lock ) ;
if ( ! - - buffer - > vmap_cnt ) {
vunmap ( buffer - > vaddr ) ;
buffer - > vaddr = NULL ;
}
mutex_unlock ( & buffer - > lock ) ;
2022-02-04 20:05:41 +03:00
iosys_map_clear ( map ) ;
2020-11-22 02:49:58 +03:00
}
static void system_heap_dma_buf_release ( struct dma_buf * dmabuf )
{
struct system_heap_buffer * buffer = dmabuf - > priv ;
struct sg_table * table ;
struct scatterlist * sg ;
int i ;
table = & buffer - > sg_table ;
2021-11-26 10:49:04 +03:00
for_each_sgtable_sg ( table , sg , i ) {
2020-11-22 02:50:02 +03:00
struct page * page = sg_page ( sg ) ;
__free_pages ( page , compound_order ( page ) ) ;
}
2020-11-22 02:49:58 +03:00
sg_free_table ( table ) ;
2019-12-03 20:26:39 +03:00
kfree ( buffer ) ;
}
2020-11-22 02:49:58 +03:00
static const struct dma_buf_ops system_heap_buf_ops = {
. attach = system_heap_attach ,
. detach = system_heap_detach ,
. map_dma_buf = system_heap_map_dma_buf ,
. unmap_dma_buf = system_heap_unmap_dma_buf ,
. begin_cpu_access = system_heap_dma_buf_begin_cpu_access ,
. end_cpu_access = system_heap_dma_buf_end_cpu_access ,
. mmap = system_heap_mmap ,
. vmap = system_heap_vmap ,
. vunmap = system_heap_vunmap ,
. release = system_heap_dma_buf_release ,
} ;
2020-11-22 02:50:02 +03:00
static struct page * alloc_largest_available ( unsigned long size ,
unsigned int max_order )
{
struct page * page ;
int i ;
for ( i = 0 ; i < NUM_ORDERS ; i + + ) {
if ( size < ( PAGE_SIZE < < orders [ i ] ) )
continue ;
if ( max_order < orders [ i ] )
continue ;
page = alloc_pages ( order_flags [ i ] , orders [ i ] ) ;
if ( ! page )
continue ;
return page ;
}
return NULL ;
}
2021-01-19 23:45:08 +03:00
static struct dma_buf * system_heap_allocate ( struct dma_heap * heap ,
unsigned long len ,
unsigned long fd_flags ,
unsigned long heap_flags )
2019-12-03 20:26:39 +03:00
{
2020-11-22 02:49:58 +03:00
struct system_heap_buffer * buffer ;
DEFINE_DMA_BUF_EXPORT_INFO ( exp_info ) ;
2020-11-22 02:50:02 +03:00
unsigned long size_remaining = len ;
unsigned int max_order = orders [ 0 ] ;
2019-12-03 20:26:39 +03:00
struct dma_buf * dmabuf ;
2020-11-22 02:49:58 +03:00
struct sg_table * table ;
struct scatterlist * sg ;
2020-11-22 02:50:02 +03:00
struct list_head pages ;
struct page * page , * tmp_page ;
2020-11-22 02:49:58 +03:00
int i , ret = - ENOMEM ;
2019-12-03 20:26:39 +03:00
2020-11-22 02:49:58 +03:00
buffer = kzalloc ( sizeof ( * buffer ) , GFP_KERNEL ) ;
if ( ! buffer )
2021-01-19 23:45:08 +03:00
return ERR_PTR ( - ENOMEM ) ;
2019-12-03 20:26:39 +03:00
2020-11-22 02:49:58 +03:00
INIT_LIST_HEAD ( & buffer - > attachments ) ;
mutex_init ( & buffer - > lock ) ;
buffer - > heap = heap ;
buffer - > len = len ;
2020-11-22 02:50:02 +03:00
INIT_LIST_HEAD ( & pages ) ;
i = 0 ;
while ( size_remaining > 0 ) {
2019-12-03 20:26:39 +03:00
/*
* Avoid trying to allocate memory if the process
2020-11-22 02:49:58 +03:00
* has been killed by SIGKILL
2019-12-03 20:26:39 +03:00
*/
2021-01-19 23:45:06 +03:00
if ( fatal_signal_pending ( current ) ) {
ret = - EINTR ;
2020-11-22 02:50:02 +03:00
goto free_buffer ;
2021-01-19 23:45:06 +03:00
}
2020-11-22 02:50:02 +03:00
page = alloc_largest_available ( size_remaining , max_order ) ;
2020-11-22 02:49:58 +03:00
if ( ! page )
2020-11-22 02:50:02 +03:00
goto free_buffer ;
list_add_tail ( & page - > lru , & pages ) ;
size_remaining - = page_size ( page ) ;
max_order = compound_order ( page ) ;
i + + ;
}
table = & buffer - > sg_table ;
if ( sg_alloc_table ( table , i , GFP_KERNEL ) )
goto free_buffer ;
sg = table - > sgl ;
list_for_each_entry_safe ( page , tmp_page , & pages , lru ) {
2020-11-22 02:49:58 +03:00
sg_set_page ( sg , page , page_size ( page ) , 0 ) ;
sg = sg_next ( sg ) ;
2020-11-22 02:50:02 +03:00
list_del ( & page - > lru ) ;
2019-12-03 20:26:39 +03:00
}
/* create the dmabuf */
2021-02-09 22:48:18 +03:00
exp_info . exp_name = dma_heap_get_name ( heap ) ;
2020-11-22 02:49:58 +03:00
exp_info . ops = & system_heap_buf_ops ;
exp_info . size = buffer - > len ;
exp_info . flags = fd_flags ;
exp_info . priv = buffer ;
dmabuf = dma_buf_export ( & exp_info ) ;
2019-12-03 20:26:39 +03:00
if ( IS_ERR ( dmabuf ) ) {
ret = PTR_ERR ( dmabuf ) ;
2020-11-22 02:49:58 +03:00
goto free_pages ;
2019-12-03 20:26:39 +03:00
}
2021-01-19 23:45:08 +03:00
return dmabuf ;
2019-12-03 20:26:39 +03:00
2020-11-22 02:49:58 +03:00
free_pages :
2020-11-22 02:50:02 +03:00
for_each_sgtable_sg ( table , sg , i ) {
struct page * p = sg_page ( sg ) ;
__free_pages ( p , compound_order ( p ) ) ;
}
2020-11-22 02:49:58 +03:00
sg_free_table ( table ) ;
free_buffer :
2020-11-22 02:50:02 +03:00
list_for_each_entry_safe ( page , tmp_page , & pages , lru )
__free_pages ( page , compound_order ( page ) ) ;
2020-11-22 02:49:58 +03:00
kfree ( buffer ) ;
2019-12-03 20:26:39 +03:00
2021-01-19 23:45:08 +03:00
return ERR_PTR ( ret ) ;
2019-12-03 20:26:39 +03:00
}
static const struct dma_heap_ops system_heap_ops = {
. allocate = system_heap_allocate ,
} ;
static int system_heap_create ( void )
{
struct dma_heap_export_info exp_info ;
2019-12-16 16:34:05 +03:00
exp_info . name = " system " ;
2019-12-03 20:26:39 +03:00
exp_info . ops = & system_heap_ops ;
exp_info . priv = NULL ;
sys_heap = dma_heap_add ( & exp_info ) ;
if ( IS_ERR ( sys_heap ) )
2020-11-22 02:49:58 +03:00
return PTR_ERR ( sys_heap ) ;
2019-12-03 20:26:39 +03:00
2020-11-22 02:49:58 +03:00
return 0 ;
2019-12-03 20:26:39 +03:00
}
module_init ( system_heap_create ) ;