First verify system_heap exporter has exclusive dmabuf access.
Build bio_vec from sgtable, then invoke target file's r/w callbacks for IO.
Outperforms buffer IO mmap/read by 250%, beats direct I/O udmabuf
copy_file_range by over 30% with initialization time significantly lower
than udmabuf.

Test data:
|    32x32MB Read 1024MB  |Creat-ms|Close-ms|  I/O-ms|I/O-MB/s| I/O%
|-------------------------|--------|--------|--------|--------|-----
| 1)Beg  dmabuf buffer R/W|     47 |      5 |   1125 |    954 | 100%
| 2)    udmabuf buffer R/W|    576 |    323 |   1228 |    874 |  91%
| 3) udma+memfd buffer R/W|    596 |    340 |   2166 |    495 |  51%
| 4) udma+memfd direct R/W|    570 |    338 |    711 |   1510 | 158%
| 5)  udmabuf buffer c_f_r|    578 |    329 |   1128 |    952 |  99%
| 6)  udmabuf direct c_f_r|    570 |    324 |    405 |   2651 | 277%
| 7)   dmabuf buffer c_f_r|     47 |      5 |   1035 |   1037 | 108%
| 8)   dmabuf direct c_f_r|     51 |      5 |    309 |   3480 | 364%
| 9)End  dmabuf buffer R/W|     48 |      5 |   1153 |    931 |  97%

|    32x32MB Write 1024MB |Creat-ms|Close-ms|  I/O-ms|I/O-MB/s| I/O%
|-------------------------|--------|--------|--------|--------|-----
| 1)Beg  dmabuf buffer R/W|     50 |      5 |   1405 |    764 | 100%
| 2)    udmabuf buffer R/W|    580 |    341 |   1337 |    803 | 105%
| 3) udma+memfd buffer R/W|    588 |    331 |   1820 |    590 |  77%
| 4) udma+memfd direct R/W|    585 |    333 |    662 |   1622 | 212%
| 5)  udmabuf buffer c_f_r|    577 |    329 |   1326 |    810 | 106%
| 6)  udmabuf direct c_f_r|    580 |    330 |    602 |   1784 | 233%
| 7)   dmabuf buffer c_f_r|     49 |      5 |   1330 |    807 | 105%
| 8)   dmabuf direct c_f_r|     49 |      5 |    344 |   3127 | 409%
| 9)End  dmabuf buffer R/W|     50 |      5 |   1442 |    745 |  97%

Signed-off-by: wangtao <tao.wang...@honor.com>
---
 drivers/dma-buf/heaps/system_heap.c | 69 +++++++++++++++++++++++++++++
 1 file changed, 69 insertions(+)

diff --git a/drivers/dma-buf/heaps/system_heap.c 
b/drivers/dma-buf/heaps/system_heap.c
index 26d5dc89ea16..85ffff7ef855 100644
--- a/drivers/dma-buf/heaps/system_heap.c
+++ b/drivers/dma-buf/heaps/system_heap.c
@@ -20,6 +20,8 @@
 #include <linux/scatterlist.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
+#include <linux/bvec.h>
+#include <linux/uio.h>
 
 static struct dma_heap *sys_heap;
 
@@ -281,6 +283,70 @@ static void system_heap_vunmap(struct dma_buf *dmabuf, 
struct iosys_map *map)
        iosys_map_clear(map);
 }
 
+static ssize_t system_heap_buffer_rw_other(struct system_heap_buffer *buffer,
+                       loff_t my_pos, struct file *other, loff_t pos,
+                       size_t count, bool is_write)
+{
+       struct sg_table *sgt = &buffer->sg_table;
+       struct scatterlist *sg;
+       loff_t my_end = my_pos + count, bv_beg, bv_end = 0;
+       size_t i, bv_off, bv_len, bv_idx = 0;
+       struct bio_vec *bvec;
+       struct kiocb kiocb;
+       struct iov_iter iter;
+       unsigned int direction = is_write ? ITER_SOURCE : ITER_DEST;
+       ssize_t ret = 0;
+
+       bvec = kvcalloc(sgt->orig_nents, sizeof(*bvec), GFP_KERNEL);
+       if (!bvec)
+               return -ENOMEM;
+
+       init_sync_kiocb(&kiocb, other);
+       kiocb.ki_pos = pos;
+
+       for_each_sgtable_sg(sgt, sg, i) {
+               bv_beg = bv_end;
+               if (bv_beg >= my_end)
+                       break;
+               bv_end += sg->offset + sg->length;
+               if (bv_end <= my_pos)
+                       continue;
+
+               bv_len = min(bv_end, my_end) - max(my_pos, bv_beg);
+               bv_off = sg->offset + (my_pos > bv_beg ? my_pos - bv_beg : 0);
+               bvec_set_page(&bvec[bv_idx], sg_page(sg), bv_len, bv_off);
+               ++bv_idx;
+       }
+
+       if (bv_idx > 0) {
+               /* start R/W. */
+               iov_iter_bvec(&iter, direction, bvec, bv_idx, count);
+               if (is_write)
+                       ret = other->f_op->write_iter(&kiocb, &iter);
+               else
+                       ret = other->f_op->read_iter(&kiocb, &iter);
+       }
+       kvfree(bvec);
+
+       return ret;
+}
+
+static ssize_t system_heap_dma_buf_rw_file(struct dma_buf *dmabuf,
+                       loff_t my_pos, struct file *file, loff_t pos,
+                       size_t count, bool is_write)
+{
+       struct system_heap_buffer *buffer = dmabuf->priv;
+       ssize_t ret = -EBUSY;
+
+       mutex_lock(&buffer->lock);
+       if (list_empty(&buffer->attachments) && !buffer->vmap_cnt)
+               ret = system_heap_buffer_rw_other(buffer, my_pos,
+                       file, pos, count, is_write);
+       mutex_unlock(&buffer->lock);
+
+       return ret;
+}
+
 static void system_heap_dma_buf_release(struct dma_buf *dmabuf)
 {
        struct system_heap_buffer *buffer = dmabuf->priv;
@@ -308,6 +374,7 @@ static const struct dma_buf_ops system_heap_buf_ops = {
        .mmap = system_heap_mmap,
        .vmap = system_heap_vmap,
        .vunmap = system_heap_vunmap,
+       .rw_file = system_heap_dma_buf_rw_file,
        .release = system_heap_dma_buf_release,
 };
 
@@ -400,6 +467,8 @@ static struct dma_buf *system_heap_allocate(struct dma_heap 
*heap,
                ret = PTR_ERR(dmabuf);
                goto free_pages;
        }
+       /* Support direct I/O */
+       dmabuf->file->f_mode |= FMODE_CAN_ODIRECT;
        return dmabuf;
 
 free_pages:
-- 
2.17.1

Reply via email to