1 /*
2  *   Copyright 2020, 2021 Linaro Ltd.
3  *
4  *  Licensed under the Apache License, Version 2.0 (the "License");
5  *  you may not use this file except in compliance with the License.
6  *  You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *  Unless required by applicable law or agreed to in writing, software
11  *  distributed under the License is distributed on an "AS IS" BASIS,
12  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *  See the License for the specific language governing permissions and
14  *  limitations under the License.
15  */
16 
17 #include <dirent.h>
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <stdint.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/ioctl.h>
25 #include <sys/mman.h>
26 #include <sys/types.h>
27 #include <time.h>
28 #include <unistd.h>
29 
30 #include <drm/drm.h>
31 #include <linux/dma-buf.h>
32 #include <linux/dma-heap.h>
33 #include <linux/ion.h>
34 #include <linux/ion_4.19.h>
35 
36 #define HEAP_DEVPATH "/dev/dma_heap"
37 #define ION_DEVPATH "/dev/ion"
38 
39 #define ONE_MEG (1024 * 1024)
40 #define NUM_SIZES 4
41 int sizes[NUM_SIZES] = {4 * 1024, ONE_MEG, 8 * ONE_MEG, 32 * ONE_MEG};
42 
43 #define NUM_ITERS 5000
44 #define NSEC_PER_SEC 1000000000LL
45 #define MAX_HEAP_COUNT ION_HEAP_TYPE_CUSTOM
46 
ion_heap_open(void)47 int ion_heap_open(void) {
48     int ret, fd;
49     char buf[256];
50 
51     ret = sprintf(buf, "%s", ION_DEVPATH);
52     if (ret < 0) {
53         printf("sprintf failed!\n");
54         return ret;
55     }
56 
57     fd = open(buf, O_RDWR);
58     if (fd < 0) printf("open %s failed!\n", buf);
59     return fd;
60 }
61 
ion_heap_alloc(int ionfd,int heap_id,size_t len,unsigned int flags,int * dmabuf_fd)62 int ion_heap_alloc(int ionfd, int heap_id, size_t len, unsigned int flags, int* dmabuf_fd) {
63     struct ion_new_allocation_data alloc_data;
64     int ret;
65 
66     memset(&alloc_data, 0, sizeof(alloc_data));
67     alloc_data.heap_id_mask = 1 << heap_id;
68     alloc_data.flags = flags;
69     alloc_data.len = len;
70 
71     /* Allocate memory for this ION client as per heap_type */
72     ret = ioctl(ionfd, ION_IOC_NEW_ALLOC, &alloc_data);
73 
74     *dmabuf_fd = alloc_data.fd;
75 
76     return ret;
77 }
78 
dmabuf_heap_open(char * name)79 int dmabuf_heap_open(char* name) {
80     int ret, fd;
81     char buf[256];
82 
83     ret = sprintf(buf, "%s/%s", HEAP_DEVPATH, name);
84     if (ret < 0) {
85         printf("sprintf failed!\n");
86         return ret;
87     }
88 
89     fd = open(buf, O_RDWR);
90     if (fd < 0) printf("open %s failed!\n", buf);
91     return fd;
92 }
93 
dmabuf_heap_alloc(int fd,size_t len,unsigned int flags,int * dmabuf_fd)94 int dmabuf_heap_alloc(int fd, size_t len, unsigned int flags, int* dmabuf_fd) {
95     struct dma_heap_allocation_data data = {
96             .len = len,
97             .fd_flags = O_RDWR | O_CLOEXEC,
98             .heap_flags = flags,
99     };
100     int ret;
101 
102     if (dmabuf_fd == NULL) return -EINVAL;
103 
104     ret = ioctl(fd, DMA_HEAP_IOCTL_ALLOC, &data);
105     if (ret < 0) return ret;
106     *dmabuf_fd = (int)data.fd;
107     return ret;
108 }
109 
dmabuf_sync(int fd,int start_stop)110 void dmabuf_sync(int fd, int start_stop) {
111     struct dma_buf_sync sync = {0};
112     int ret;
113 
114     sync.flags = start_stop | DMA_BUF_SYNC_RW;
115     ret = ioctl(fd, DMA_BUF_IOCTL_SYNC, &sync);
116     if (ret) printf("sync failed %d\n", errno);
117 }
118 
ion_heap_bench(unsigned int heap_type,int size,int flags)119 void ion_heap_bench(unsigned int heap_type, int size, int flags) {
120     int heap_id;
121     int ionfd = -1, dmabuf_fd = -1;
122     struct ion_heap_query query;
123     struct ion_heap_data heap_data[MAX_HEAP_COUNT];
124     struct timespec ts_start, ts_end;
125     long long start, end;
126     int ret;
127     unsigned int i;
128 
129     ionfd = ion_heap_open();
130     if (ionfd < 0) return;
131 
132     memset(&query, 0, sizeof(query));
133     query.cnt = MAX_HEAP_COUNT;
134     query.heaps = (unsigned long int)&heap_data[0];
135     /* Query ION heap_id_mask from ION heap */
136     ret = ioctl(ionfd, ION_IOC_HEAP_QUERY, &query);
137     if (ret < 0) {
138         printf("<%s>: Failed: ION_IOC_HEAP_QUERY: %s\n", __func__, strerror(errno));
139         goto out;
140     }
141     heap_id = MAX_HEAP_COUNT + 1;
142     for (i = 0; i < query.cnt; i++) {
143         if (heap_data[i].type == heap_type) {
144             heap_id = heap_data[i].heap_id;
145             break;
146         }
147     }
148     if (heap_id > MAX_HEAP_COUNT) {
149         printf("<%s>: ERROR: heap type does not exists\n", __func__);
150         goto out;
151     }
152 
153     clock_gettime(CLOCK_MONOTONIC, &ts_start);
154     for (i = 0; i < NUM_ITERS; i++) {
155         ret = ion_heap_alloc(ionfd, heap_id, size, flags, &dmabuf_fd);
156         if (ret) goto out;
157         close(dmabuf_fd);
158     }
159     clock_gettime(CLOCK_MONOTONIC, &ts_end);
160 
161     start = ts_start.tv_sec * NSEC_PER_SEC + ts_start.tv_nsec;
162     end = ts_end.tv_sec * NSEC_PER_SEC + ts_end.tv_nsec;
163 
164     printf("ion heap:    alloc %d bytes %i times in %lld ns \t %lld ns/call\n", size, NUM_ITERS,
165            end - start, (end - start) / NUM_ITERS);
166 out:
167     if (ionfd >= 0) close(ionfd);
168 }
169 
dmabuf_heap_bench(char * heap_name,int size)170 void dmabuf_heap_bench(char* heap_name, int size) {
171     int heap_fd = -1, dmabuf_fd = -1;
172     struct timespec ts_start, ts_end;
173     long long start, end;
174     int ret;
175     int i;
176 
177     heap_fd = dmabuf_heap_open(heap_name);
178     if (heap_fd < 0) return;
179 
180     clock_gettime(CLOCK_MONOTONIC, &ts_start);
181     for (i = 0; i < NUM_ITERS; i++) {
182         ret = dmabuf_heap_alloc(heap_fd, size, 0, &dmabuf_fd);
183         if (ret) goto out;
184         close(dmabuf_fd);
185     }
186     clock_gettime(CLOCK_MONOTONIC, &ts_end);
187 
188     start = ts_start.tv_sec * NSEC_PER_SEC + ts_start.tv_nsec;
189     end = ts_end.tv_sec * NSEC_PER_SEC + ts_end.tv_nsec;
190 
191     printf("dmabuf heap: alloc %d bytes %i times in %lld ns \t %lld ns/call\n", size, NUM_ITERS,
192            end - start, (end - start) / NUM_ITERS);
193 out:
194     if (heap_fd >= 0) close(heap_fd);
195 }
196 
main(int argc,char * argv[])197 int main(int argc, char* argv[]) {
198     char* dmabuf_heap_name;
199     unsigned int ion_heap_type;
200     int ion_flags = 0;
201     int testing_ion = 0;
202     int i;
203     if (argc < 2) {
204         printf("Usage %s [-i <ion heap type> <ion heap flags>] <dmabuf heap name>\n", argv[0]);
205         return -1;
206     }
207 
208     if (argv[1][0] == '-' && argv[1][1] == 'i') {
209         testing_ion = 1;
210         ion_heap_type = strtol(argv[2], NULL, 0);
211         ion_flags = strtol(argv[3], NULL, 0);
212         dmabuf_heap_name = argv[4];
213     } else {
214         dmabuf_heap_name = argv[1];
215     }
216 
217     printf("Testing dmabuf %s", dmabuf_heap_name);
218     if (testing_ion) printf(" vs ion heaptype %d (flags: 0x%x)", ion_heap_type, ion_flags);
219     printf("\n---------------------------------------------\n");
220     for (i = 0; i < NUM_SIZES; i++) {
221         dmabuf_heap_bench(dmabuf_heap_name, sizes[i]);
222         if (testing_ion) ion_heap_bench(ion_heap_type, sizes[i], ion_flags);
223     }
224 
225     return 0;
226 }
227