Dear Laura, Shuah, Request you to please review the PATCHv4. If you have more comments please let me know.
Thanks, Pintu On Tue, Oct 24, 2017 at 4:56 AM, Pintu Agarwal <pintu.p...@gmail.com> wrote: > This is a test utility to verify ION buffer sharing in user space > between 2 independent processes. > It uses unix domain socket (with SCM_RIGHTS) as IPC to transfer an FD to > another process to share the same buffer. > This utility demonstrates how ION buffer sharing can be implemented between > two user space processes, using various heap types. > > This utility is made to be run as part of kselftest framework in kernel. > The utility is verified on Ubuntu-32 bit system with Linux Kernel 4.14, > using ION system heap and CMA heap. > > For more information about the utility please check the README file. > > Signed-off-by: Pintu Agarwal <pintu.p...@gmail.com> > --- > tools/testing/selftests/Makefile | 3 +- > tools/testing/selftests/android/Makefile | 46 ++++ > tools/testing/selftests/android/ion/.gitignore | 2 + > tools/testing/selftests/android/ion/Makefile | 16 ++ > tools/testing/selftests/android/ion/README | 101 ++++++++ > tools/testing/selftests/android/ion/config | 4 + > tools/testing/selftests/android/ion/ion_test.sh | 61 +++++ > .../testing/selftests/android/ion/ionapp_export.c | 144 ++++++++++++ > .../testing/selftests/android/ion/ionapp_import.c | 88 +++++++ > tools/testing/selftests/android/ion/ionutils.c | 259 > +++++++++++++++++++++ > tools/testing/selftests/android/ion/ionutils.h | 55 +++++ > tools/testing/selftests/android/ion/ipcsocket.c | 227 ++++++++++++++++++ > tools/testing/selftests/android/ion/ipcsocket.h | 35 +++ > tools/testing/selftests/android/run.sh | 3 + > 14 files changed, 1043 insertions(+), 1 deletion(-) > create mode 100644 tools/testing/selftests/android/Makefile > create mode 100644 tools/testing/selftests/android/ion/.gitignore > create mode 100644 tools/testing/selftests/android/ion/Makefile > create mode 100644 tools/testing/selftests/android/ion/README > create mode 100644 tools/testing/selftests/android/ion/config > create mode 100755 tools/testing/selftests/android/ion/ion_test.sh > create mode 100644 tools/testing/selftests/android/ion/ionapp_export.c > create mode 100644 tools/testing/selftests/android/ion/ionapp_import.c > create mode 100644 tools/testing/selftests/android/ion/ionutils.c > create mode 100644 tools/testing/selftests/android/ion/ionutils.h > create mode 100644 tools/testing/selftests/android/ion/ipcsocket.c > create mode 100644 tools/testing/selftests/android/ion/ipcsocket.h > create mode 100755 tools/testing/selftests/android/run.sh > > diff --git a/tools/testing/selftests/Makefile > b/tools/testing/selftests/Makefile > index ff80564..61bc77b 100644 > --- a/tools/testing/selftests/Makefile > +++ b/tools/testing/selftests/Makefile > @@ -1,4 +1,5 @@ > -TARGETS = bpf > +TARGETS = android > +TARGETS += bpf > TARGETS += breakpoints > TARGETS += capabilities > TARGETS += cpufreq > diff --git a/tools/testing/selftests/android/Makefile > b/tools/testing/selftests/android/Makefile > new file mode 100644 > index 0000000..1a74922 > --- /dev/null > +++ b/tools/testing/selftests/android/Makefile > @@ -0,0 +1,46 @@ > +SUBDIRS := ion > + > +TEST_PROGS := run.sh > + > +.PHONY: all clean > + > +include ../lib.mk > + > +all: > + @for DIR in $(SUBDIRS); do \ > + BUILD_TARGET=$(OUTPUT)/$$DIR; \ > + mkdir $$BUILD_TARGET -p; \ > + make OUTPUT=$$BUILD_TARGET -C $$DIR $@;\ > + #SUBDIR test prog name should be in the form: SUBDIR_test.sh > + TEST=$$DIR"_test.sh"; \ > + if [ -e $$DIR/$$TEST ]; then > + rsync -a $$DIR/$$TEST $$BUILD_TARGET/; > + fi > + done > + > +override define RUN_TESTS > + @cd $(OUTPUT); ./run.sh > +endef > + > +override define INSTALL_RULE > + mkdir -p $(INSTALL_PATH) > + install -t $(INSTALL_PATH) $(TEST_PROGS) $(TEST_PROGS_EXTENDED) > $(TEST_FILES) > + > + @for SUBDIR in $(SUBDIRS); do \ > + BUILD_TARGET=$(OUTPUT)/$$SUBDIR; \ > + mkdir $$BUILD_TARGET -p; \ > + $(MAKE) OUTPUT=$$BUILD_TARGET -C $$SUBDIR > INSTALL_PATH=$(INSTALL_PATH)/$$SUBDIR install; \ > + done; > +endef > + > +override define EMIT_TESTS > + echo "./run.sh" > +endef > + > +override define CLEAN > + @for DIR in $(SUBDIRS); do \ > + BUILD_TARGET=$(OUTPUT)/$$DIR; \ > + mkdir $$BUILD_TARGET -p; \ > + make OUTPUT=$$BUILD_TARGET -C $$DIR $@;\ > + done > +endef > diff --git a/tools/testing/selftests/android/ion/.gitignore > b/tools/testing/selftests/android/ion/.gitignore > new file mode 100644 > index 0000000..67e6f39 > --- /dev/null > +++ b/tools/testing/selftests/android/ion/.gitignore > @@ -0,0 +1,2 @@ > +ionapp_export > +ionapp_import > diff --git a/tools/testing/selftests/android/ion/Makefile > b/tools/testing/selftests/android/ion/Makefile > new file mode 100644 > index 0000000..1399a5e > --- /dev/null > +++ b/tools/testing/selftests/android/ion/Makefile > @@ -0,0 +1,16 @@ > + > +INCLUDEDIR := -I../../../../../drivers/staging/android/uapi/ > +CFLAGS := $(CFLAGS) $(INCLUDEDIR) -Wall -O2 -g > + > +TEST_GEN_FILES := ionapp_export ionapp_import > + > +all: $(TEST_GEN_FILES) > + > +$(TEST_GEN_FILES): ipcsocket.c ionutils.c > + > +TEST_PROGS := ion_test.sh > + > +include ../../lib.mk > + > +$(OUTPUT)/ionapp_export: ionapp_export.c ipcsocket.c ionutils.c > +$(OUTPUT)/ionapp_import: ionapp_import.c ipcsocket.c ionutils.c > diff --git a/tools/testing/selftests/android/ion/README > b/tools/testing/selftests/android/ion/README > new file mode 100644 > index 0000000..21783e9 > --- /dev/null > +++ b/tools/testing/selftests/android/ion/README > @@ -0,0 +1,101 @@ > +ION BUFFER SHARING UTILITY > +========================== > +File: ion_test.sh : Utility to test ION driver buffer sharing mechanism. > +Author: Pintu Kumar <pintu.p...@gmail.com> > + > +Introduction: > +------------- > +This is a test utility to verify ION buffer sharing in user space > +between 2 independent processes. > +It uses unix domain socket (with SCM_RIGHTS) as IPC to transfer an FD to > +another process to share the same buffer. > +This utility demonstrates how ION buffer sharing can be implemented between > +two user space processes, using various heap types. > +The following heap types are supported by ION driver. > +ION_HEAP_TYPE_SYSTEM (0) > +ION_HEAP_TYPE_SYSTEM_CONTIG (1) > +ION_HEAP_TYPE_CARVEOUT (2) > +ION_HEAP_TYPE_CHUNK (3) > +ION_HEAP_TYPE_DMA (4) > + > +By default only the SYSTEM and SYSTEM_CONTIG heaps are supported. > +Each heap is associated with the respective heap id. > +This utility is designed in the form of client/server program. > +The server part (ionapp_export) is the exporter of the buffer. > +It is responsible for creating an ION client, allocating the buffer based on > +the heap id, writing some data to this buffer and then exporting the FD > +(associated with this buffer) to another process using socket IPC. > +This FD is called as buffer FD (which is different than the ION client FD). > + > +The client part (ionapp_import) is the importer of the buffer. > +It retrives the FD from the socket data and installs into its address space. > +This new FD internally points to the same kernel buffer. > +So first it reads the data that is stored in this buffer and prints it. > +Then it writes the different size of data (it could be different data) to the > +same buffer. > +Finally the buffer FD must be closed by both the exporter and importer. > +Thus the same kernel buffer is shared among two user space processes using > +ION driver and only one time allocation. > + > +Prerequisite: > +------------- > +This utility works only if /dev/ion interface is present. > +The following configs needs to be enabled in kernel to include ion driver. > +CONFIG_ANDROID=y > +CONFIG_STAGING=y > +CONFIG_ION=y > +CONFIG_ION_SYSTEM_HEAP=y > + > +This utility requires to be run as root user. > + > + > +Compile and test: > +----------------- > +This utility is made to be run as part of kselftest framework in kernel. > +To compile and run using kselftest you can simply do the following from the > +kernel top directory. > +linux$ make TARGETS=android kselftest > +Or you can also use: > +linux$ make -C tools/testing/selftests TARGETS=android run_tests > +Using the selftest it can directly execute the ion_test.sh script to test the > +buffer sharing using ion system heap. > +Currently the heap size is hard coded as just 10 bytes inside this script. > +You need to be a root user to run under selftest. > + > +You can also compile and test manually using the following steps: > +ion$ make > +These will generate 2 executable: ionapp_export, ionapp_import > +Now you can run the export and import manually by specifying the heap type > +and the heap size. > +You can also directly execute the shell script to run the test automatically. > +Simply use the following command to run the test. > +ion$ sudo ./ion_test.sh > + > +Test Results: > +------------- > +The utility is verified on Ubuntu-32 bit system with Linux Kernel 4.14. > +Here is the snapshot of the test result using kselftest. > + > +linux# make TARGETS=android kselftest > +heap_type: 0, heap_size: 10 > +-------------------------------------- > +heap type: 0 > + heap id: 1 > +heap name: ion_system_heap > +-------------------------------------- > +Fill buffer content: > +0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd > +Sharing fd: 6, Client fd: 5 > +<ion_close_buffer_fd>: buffer release successfully.... > +Received buffer fd: 4 > +Read buffer content: > +0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0x0 0x0 0x0 0x0 0x0 0x0 > +0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 > +Fill buffer content: > +0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd > +0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd > +0xfd 0xfd > +<ion_close_buffer_fd>: buffer release successfully.... > +ion_test.sh: heap_type: 0 - [PASS] > + > +ion_test.sh: done > diff --git a/tools/testing/selftests/android/ion/config > b/tools/testing/selftests/android/ion/config > new file mode 100644 > index 0000000..19db6ca > --- /dev/null > +++ b/tools/testing/selftests/android/ion/config > @@ -0,0 +1,4 @@ > +CONFIG_ANDROID=y > +CONFIG_STAGING=y > +CONFIG_ION=y > +CONFIG_ION_SYSTEM_HEAP=y > diff --git a/tools/testing/selftests/android/ion/ion_test.sh > b/tools/testing/selftests/android/ion/ion_test.sh > new file mode 100755 > index 0000000..65bb28f > --- /dev/null > +++ b/tools/testing/selftests/android/ion/ion_test.sh > @@ -0,0 +1,61 @@ > +#!/bin/bash > + > +heapsize=4096 > +TCID="ion_test.sh" > +errcode=0 > + > +run_test() > +{ > + heaptype=$1 > + ./ionapp_export -i $heaptype -s $heapsize & > + sleep 1 > + ./ionapp_import > + if [ $? -ne 0 ]; then > + echo "$TCID: heap_type: $heaptype - [FAIL]" > + errcode=1 > + else > + echo "$TCID: heap_type: $heaptype - [PASS]" > + fi > + sleep 1 > + echo "" > +} > + > +check_root() > +{ > + uid=$(id -u) > + if [ $uid -ne 0 ]; then > + echo $TCID: must be run as root >&2 > + exit 0 > + fi > +} > + > +check_device() > +{ > + DEVICE=/dev/ion > + if [ ! -e $DEVICE ]; then > + echo $TCID: No $DEVICE device found >&2 > + echo $TCID: May be CONFIG_ION is not set >&2 > + exit 0 > + fi > +} > + > +main_function() > +{ > + check_device > + check_root > + > + # ION_SYSTEM_HEAP TEST > + run_test 0 > + # ION_SYSTEM_CONTIG_HEAP TEST > + run_test 1 > + # ION_CARVEOUT HEAP TEST > + #run_test 2 > + # ION_CHUNK_HEAP TEST > + #run_test 3 > + # ION_CMA_HEAP TEST > + #run_test 4 > +} > + > +main_function > +echo "$TCID: done" > +exit $errcode > diff --git a/tools/testing/selftests/android/ion/ionapp_export.c > b/tools/testing/selftests/android/ion/ionapp_export.c > new file mode 100644 > index 0000000..6eaea10 > --- /dev/null > +++ b/tools/testing/selftests/android/ion/ionapp_export.c > @@ -0,0 +1,144 @@ > +/* > + * ionapp_export.c > + * > + * It is a user space utility to create and export android > + * ion memory buffer fd to another process using unix domain socket as IPC. > + * This acts like a server for ionapp_import(client). > + * So, this server has to be started first before the client. > + * > + * Copyright (C) 2017 Pintu Kumar <pintu.p...@gmail.com> > + * > + * This software is licensed under the terms of the GNU General Public > + * License version 2, as published by the Free Software Foundation, and > + * may be copied, distributed, and modified under those terms. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + */ > + > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > +#include <unistd.h> > +#include <errno.h> > +#include <sys/time.h> > +#include "ionutils.h" > +#include "ipcsocket.h" > + > + > +void print_usage(int argc, char *argv[]) > +{ > + printf("Usage: %s [-h <help>] [-i <heap id>] [-s <size in bytes>]\n", > + argv[0]); > +} > + > +int main(int argc, char *argv[]) > +{ > + int opt, ret, status, heapid; > + int sockfd, client_fd, shared_fd; > + unsigned char *map_buf; > + unsigned long map_len, heap_type, heap_size, flags; > + struct ion_buffer_info info; > + struct socket_info skinfo; > + > + if (argc < 2) { > + print_usage(argc, argv); > + return -1; > + } > + > + heap_size = 0; > + flags = 0; > + > + while ((opt = getopt(argc, argv, "hi:s:")) != -1) { > + switch (opt) { > + case 'h': > + print_usage(argc, argv); > + exit(0); > + break; > + case 'i': > + heapid = atoi(optarg); > + switch (heapid) { > + case 0: > + heap_type = ION_HEAP_TYPE_SYSTEM; > + break; > + case 1: > + heap_type = ION_HEAP_TYPE_SYSTEM_CONTIG; > + break; > + case 2: > + heap_type = ION_HEAP_TYPE_CARVEOUT; > + break; > + case 3: > + heap_type = ION_HEAP_TYPE_CHUNK; > + break; > + case 4: > + heap_type = ION_HEAP_TYPE_DMA; > + break; > + default: > + printf("ERROR: Wrong - heap type\n"); > + exit(1); > + } > + break; > + case 's': > + heap_size = atoi(optarg); > + break; > + default: > + print_usage(argc, argv); > + exit(1); > + break; > + } > + } > + > + if (heap_size <= 0) { > + printf("heap_size cannot be 0\n"); > + print_usage(argc, argv); > + exit(1); > + } > + > + printf("heap_type: %ld, heap_size: %ld\n", heap_type, heap_size); > + info.heap_type = heap_type; > + info.heap_size = heap_size; > + info.flag_type = flags; > + > + /* This is server: open the socket connection first */ > + /* Here; 1 indicates server or exporter */ > + status = opensocket(&sockfd, SOCKET_NAME, 1); > + if (status < 0) { > + fprintf(stderr, "<%s>: Failed opensocket.\n", __func__); > + goto err_socket; > + } > + skinfo.sockfd = sockfd; > + > + ret = ion_export_buffer_fd(&info); > + if (ret < 0) { > + fprintf(stderr, "FAILED: ion_get_buffer_fd\n"); > + goto err_export; > + } > + client_fd = info.ionfd; > + shared_fd = info.buffd; > + map_buf = info.buffer; > + map_len = info.buflen; > + write_buffer(map_buf, map_len); > + > + /* share ion buf fd with other user process */ > + printf("Sharing fd: %d, Client fd: %d\n", shared_fd, client_fd); > + skinfo.datafd = shared_fd; > + skinfo.buflen = map_len; > + > + ret = socket_send_fd(&skinfo); > + if (ret < 0) { > + fprintf(stderr, "FAILED: socket_send_fd\n"); > + goto err_send; > + } > + > +err_send: > +err_export: > + ion_close_buffer_fd(&info); > + > +err_socket: > + closesocket(sockfd, SOCKET_NAME); > + > + return 0; > +} > diff --git a/tools/testing/selftests/android/ion/ionapp_import.c > b/tools/testing/selftests/android/ion/ionapp_import.c > new file mode 100644 > index 0000000..ae2d704 > --- /dev/null > +++ b/tools/testing/selftests/android/ion/ionapp_import.c > @@ -0,0 +1,88 @@ > +/* > + * ionapp_import.c > + * > + * It is a user space utility to receive android ion memory buffer fd > + * over unix domain socket IPC that can be exported by ionapp_export. > + * This acts like a client for ionapp_export. > + * > + * Copyright (C) 2017 Pintu Kumar <pintu.p...@gmail.com> > + * > + * This software is licensed under the terms of the GNU General Public > + * License version 2, as published by the Free Software Foundation, and > + * may be copied, distributed, and modified under those terms. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + */ > + > +#include <stdio.h> > +#include <stdlib.h> > +#include <unistd.h> > +#include <string.h> > +#include "ionutils.h" > +#include "ipcsocket.h" > + > + > +int main(void) > +{ > + int ret, status; > + int sockfd, shared_fd; > + unsigned char *map_buf; > + unsigned long map_len; > + struct ion_buffer_info info; > + struct socket_info skinfo; > + > + /* This is the client part. Here 0 means client or importer */ > + status = opensocket(&sockfd, SOCKET_NAME, 0); > + if (status < 0) { > + fprintf(stderr, "No exporter exists...\n"); > + ret = status; > + goto err_socket; > + } > + > + skinfo.sockfd = sockfd; > + > + ret = socket_receive_fd(&skinfo); > + if (ret < 0) { > + fprintf(stderr, "Failed: socket_receive_fd\n"); > + goto err_recv; > + } > + > + shared_fd = skinfo.datafd; > + printf("Received buffer fd: %d\n", shared_fd); > + if (shared_fd <= 0) { > + fprintf(stderr, "ERROR: improper buf fd\n"); > + ret = -1; > + goto err_fd; > + } > + > + memset(&info, 0, sizeof(info)); > + info.buffd = shared_fd; > + info.buflen = ION_BUFFER_LEN; > + > + ret = ion_import_buffer_fd(&info); > + if (ret < 0) { > + fprintf(stderr, "Failed: ion_use_buffer_fd\n"); > + goto err_import; > + } > + > + map_buf = info.buffer; > + map_len = info.buflen; > + read_buffer(map_buf, map_len); > + > + /* Write probably new data to the same buffer again */ > + map_len = ION_BUFFER_LEN; > + write_buffer(map_buf, map_len); > + > +err_import: > + ion_close_buffer_fd(&info); > +err_fd: > +err_recv: > +err_socket: > + closesocket(sockfd, SOCKET_NAME); > + > + return ret; > +} > diff --git a/tools/testing/selftests/android/ion/ionutils.c > b/tools/testing/selftests/android/ion/ionutils.c > new file mode 100644 > index 0000000..ce69c14 > --- /dev/null > +++ b/tools/testing/selftests/android/ion/ionutils.c > @@ -0,0 +1,259 @@ > +#include <stdio.h> > +#include <string.h> > +#include <unistd.h> > +#include <fcntl.h> > +#include <errno.h> > +//#include <stdint.h> > +#include <sys/ioctl.h> > +#include <sys/mman.h> > +#include "ionutils.h" > +#include "ipcsocket.h" > + > + > +void write_buffer(void *buffer, unsigned long len) > +{ > + int i; > + unsigned char *ptr = (unsigned char *)buffer; > + > + if (!ptr) { > + fprintf(stderr, "<%s>: Invalid buffer...\n", __func__); > + return; > + } > + > + printf("Fill buffer content:\n"); > + memset(ptr, 0xfd, len); > + for (i = 0; i < len; i++) > + printf("0x%x ", ptr[i]); > + printf("\n"); > +} > + > +void read_buffer(void *buffer, unsigned long len) > +{ > + int i; > + unsigned char *ptr = (unsigned char *)buffer; > + > + if (!ptr) { > + fprintf(stderr, "<%s>: Invalid buffer...\n", __func__); > + return; > + } > + > + printf("Read buffer content:\n"); > + for (i = 0; i < len; i++) > + printf("0x%x ", ptr[i]); > + printf("\n"); > +} > + > +int ion_export_buffer_fd(struct ion_buffer_info *ion_info) > +{ > + int i, ret, ionfd, buffer_fd; > + unsigned int heap_id; > + unsigned long maplen; > + unsigned char *map_buffer; > + struct ion_allocation_data alloc_data; > + struct ion_heap_query query; > + struct ion_heap_data heap_data[MAX_HEAP_COUNT]; > + > + if (!ion_info) { > + fprintf(stderr, "<%s>: Invalid ion info\n", __func__); > + return -1; > + } > + > + /* Create an ION client */ > + ionfd = open(ION_DEVICE, O_RDWR); > + if (ionfd < 0) { > + fprintf(stderr, "<%s>: Failed to open ion client: %s\n", > + __func__, strerror(errno)); > + return -1; > + } > + > + memset(&query, 0, sizeof(query)); > + query.cnt = MAX_HEAP_COUNT; > + query.heaps = (unsigned long int)&heap_data[0]; > + /* Query ION heap_id_mask from ION heap */ > + ret = ioctl(ionfd, ION_IOC_HEAP_QUERY, &query); > + if (ret < 0) { > + fprintf(stderr, "<%s>: Failed: ION_IOC_HEAP_QUERY: %s\n", > + __func__, strerror(errno)); > + goto err_query; > + } > + > + heap_id = MAX_HEAP_COUNT + 1; > + for (i = 0; i < query.cnt; i++) { > + if (heap_data[i].type == ion_info->heap_type) { > + printf("--------------------------------------\n"); > + printf("heap type: %d\n", heap_data[i].type); > + printf(" heap id: %d\n", heap_data[i].heap_id); > + printf("heap name: %s\n", heap_data[i].name); > + printf("--------------------------------------\n"); > + heap_id = heap_data[i].heap_id; > + break; > + } > + } > + > + if (heap_id > MAX_HEAP_COUNT) { > + fprintf(stderr, "<%s>: ERROR: heap type does not exists\n", > + __func__); > + goto err_heap; > + } > + > + alloc_data.len = ion_info->heap_size; > + alloc_data.heap_id_mask = 1 << heap_id; > + alloc_data.flags = ion_info->flag_type; > + > + /* Allocate memory for this ION client as per heap_type */ > + ret = ioctl(ionfd, ION_IOC_ALLOC, &alloc_data); > + if (ret < 0) { > + fprintf(stderr, "<%s>: Failed: ION_IOC_ALLOC: %s\n", > + __func__, strerror(errno)); > + goto err_alloc; > + } > + > + /* This will return a valid buffer fd */ > + buffer_fd = alloc_data.fd; > + maplen = alloc_data.len; > + > + if (buffer_fd < 0 || maplen <= 0) { > + fprintf(stderr, "<%s>: Invalid map data, fd: %d, len: %ld\n", > + __func__, buffer_fd, maplen); > + goto err_fd_data; > + } > + > + /* Create memory mapped buffer for the buffer fd */ > + map_buffer = (unsigned char *)mmap(NULL, maplen, PROT_READ|PROT_WRITE, > + MAP_SHARED, buffer_fd, 0); > + if (map_buffer == MAP_FAILED) { > + fprintf(stderr, "<%s>: Failed: mmap: %s\n", > + __func__, strerror(errno)); > + goto err_mmap; > + } > + > + ion_info->ionfd = ionfd; > + ion_info->buffd = buffer_fd; > + ion_info->buffer = map_buffer; > + ion_info->buflen = maplen; > + > + return 0; > + > + munmap(map_buffer, maplen); > + > +err_fd_data: > +err_mmap: > + /* in case of error: close the buffer fd */ > + if (buffer_fd) > + close(buffer_fd); > + > +err_query: > +err_heap: > +err_alloc: > + /* In case of error: close the ion client fd */ > + if (ionfd) > + close(ionfd); > + > + return -1; > +} > + > +int ion_import_buffer_fd(struct ion_buffer_info *ion_info) > +{ > + int buffd; > + unsigned char *map_buf; > + unsigned long map_len; > + > + if (!ion_info) { > + fprintf(stderr, "<%s>: Invalid ion info\n", __func__); > + return -1; > + } > + > + map_len = ion_info->buflen; > + buffd = ion_info->buffd; > + > + if (buffd < 0 || map_len <= 0) { > + fprintf(stderr, "<%s>: Invalid map data, fd: %d, len: %ld\n", > + __func__, buffd, map_len); > + goto err_buffd; > + } > + > + map_buf = (unsigned char *)mmap(NULL, map_len, PROT_READ|PROT_WRITE, > + MAP_SHARED, buffd, 0); > + if (map_buf == MAP_FAILED) { > + printf("<%s>: Failed - mmap: %s\n", > + __func__, strerror(errno)); > + goto err_mmap; > + } > + > + ion_info->buffer = map_buf; > + ion_info->buflen = map_len; > + > + return 0; > + > +err_mmap: > + if (buffd) > + close(buffd); > + > +err_buffd: > + return -1; > +} > + > +void ion_close_buffer_fd(struct ion_buffer_info *ion_info) > +{ > + if (ion_info) { > + /* unmap the buffer properly in the end */ > + munmap(ion_info->buffer, ion_info->buflen); > + /* close the buffer fd */ > + if (ion_info->buffd > 0) > + close(ion_info->buffd); > + /* Finally, close the client fd */ > + if (ion_info->ionfd > 0) > + close(ion_info->ionfd); > + printf("<%s>: buffer release successfully....\n", __func__); > + } > +} > + > +int socket_send_fd(struct socket_info *info) > +{ > + int status; > + int fd, sockfd; > + struct socketdata skdata; > + > + if (!info) { > + fprintf(stderr, "<%s>: Invalid socket info\n", __func__); > + return -1; > + } > + > + sockfd = info->sockfd; > + fd = info->datafd; > + memset(&skdata, 0, sizeof(skdata)); > + skdata.data = fd; > + skdata.len = sizeof(skdata.data); > + status = sendtosocket(sockfd, &skdata); > + if (status < 0) { > + fprintf(stderr, "<%s>: Failed: sendtosocket\n", __func__); > + return -1; > + } > + > + return 0; > +} > + > +int socket_receive_fd(struct socket_info *info) > +{ > + int status; > + int fd, sockfd; > + struct socketdata skdata; > + > + if (!info) { > + fprintf(stderr, "<%s>: Invalid socket info\n", __func__); > + return -1; > + } > + > + sockfd = info->sockfd; > + memset(&skdata, 0, sizeof(skdata)); > + status = receivefromsocket(sockfd, &skdata); > + if (status < 0) { > + fprintf(stderr, "<%s>: Failed: receivefromsocket\n", > __func__); > + return -1; > + } > + > + fd = (int)skdata.data; > + info->datafd = fd; > + > + return status; > +} > diff --git a/tools/testing/selftests/android/ion/ionutils.h > b/tools/testing/selftests/android/ion/ionutils.h > new file mode 100644 > index 0000000..9941eb8 > --- /dev/null > +++ b/tools/testing/selftests/android/ion/ionutils.h > @@ -0,0 +1,55 @@ > +#ifndef __ION_UTILS_H > +#define __ION_UTILS_H > + > +#include "ion.h" > + > +#define SOCKET_NAME "ion_socket" > +#define ION_DEVICE "/dev/ion" > + > +#define ION_BUFFER_LEN 4096 > +#define MAX_HEAP_COUNT ION_HEAP_TYPE_CUSTOM > + > +struct socket_info { > + int sockfd; > + int datafd; > + unsigned long buflen; > +}; > + > +struct ion_buffer_info { > + int ionfd; > + int buffd; > + unsigned int heap_type; > + unsigned int flag_type; > + unsigned long heap_size; > + unsigned long buflen; > + unsigned char *buffer; > +}; > + > + > +/* This is used to fill the data into the mapped buffer */ > +void write_buffer(void *buffer, unsigned long len); > + > +/* This is used to read the data from the exported buffer */ > +void read_buffer(void *buffer, unsigned long len); > + > +/* This is used to create an ION buffer FD for the kernel buffer > + * So you can export this same buffer to others in the form of FD > + */ > +int ion_export_buffer_fd(struct ion_buffer_info *ion_info); > + > +/* This is used to import or map an exported FD. > + * So we point to same buffer without making a copy. Hence zero-copy. > + */ > +int ion_import_buffer_fd(struct ion_buffer_info *ion_info); > + > +/* This is used to close all references for the ION client */ > +void ion_close_buffer_fd(struct ion_buffer_info *ion_info); > + > +/* This is used to send FD to another process using socket IPC */ > +int socket_send_fd(struct socket_info *skinfo); > + > +/* This is used to receive FD from another process using socket IPC */ > +int socket_receive_fd(struct socket_info *skinfo); > + > + > +#endif > diff --git a/tools/testing/selftests/android/ion/ipcsocket.c > b/tools/testing/selftests/android/ion/ipcsocket.c > new file mode 100644 > index 0000000..7dc5210 > --- /dev/null > +++ b/tools/testing/selftests/android/ion/ipcsocket.c > @@ -0,0 +1,227 @@ > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > +#include <unistd.h> > +#include <sys/types.h> > +#include <sys/socket.h> > +#include <sys/time.h> > +#include <sys/un.h> > +#include <errno.h> > + > +#include "ipcsocket.h" > + > + > +int opensocket(int *sockfd, const char *name, int connecttype) > +{ > + int ret, temp = 1; > + > + if (!name || strlen(name) > MAX_SOCK_NAME_LEN) { > + fprintf(stderr, "<%s>: Invalid socket name.\n", __func__); > + return -1; > + } > + > + ret = socket(PF_LOCAL, SOCK_STREAM, 0); > + if (ret < 0) { > + fprintf(stderr, "<%s>: Failed socket: <%s>\n", > + __func__, strerror(errno)); > + return ret; > + } > + > + *sockfd = ret; > + if (setsockopt(*sockfd, SOL_SOCKET, SO_REUSEADDR, > + (char *)&temp, sizeof(int)) < 0) { > + fprintf(stderr, "<%s>: Failed setsockopt: <%s>\n", > + __func__, strerror(errno)); > + goto err; > + } > + > + sprintf(sock_name, "/tmp/%s", name); > + > + if (connecttype == 1) { > + /* This is for Server connection */ > + struct sockaddr_un skaddr; > + int clientfd; > + socklen_t sklen; > + > + unlink(sock_name); > + memset(&skaddr, 0, sizeof(skaddr)); > + skaddr.sun_family = AF_LOCAL; > + strcpy(skaddr.sun_path, sock_name); > + > + ret = bind(*sockfd, (struct sockaddr *)&skaddr, > + SUN_LEN(&skaddr)); > + if (ret < 0) { > + fprintf(stderr, "<%s>: Failed bind: <%s>\n", > + __func__, strerror(errno)); > + goto err; > + } > + > + ret = listen(*sockfd, 5); > + if (ret < 0) { > + fprintf(stderr, "<%s>: Failed listen: <%s>\n", > + __func__, strerror(errno)); > + goto err; > + } > + > + memset(&skaddr, 0, sizeof(skaddr)); > + sklen = sizeof(skaddr); > + > + ret = accept(*sockfd, (struct sockaddr *)&skaddr, > + (socklen_t *)&sklen); > + if (ret < 0) { > + fprintf(stderr, "<%s>: Failed accept: <%s>\n", > + __func__, strerror(errno)); > + goto err; > + } > + > + clientfd = ret; > + *sockfd = clientfd; > + } else { > + /* This is for client connection */ > + struct sockaddr_un skaddr; > + > + memset(&skaddr, 0, sizeof(skaddr)); > + skaddr.sun_family = AF_LOCAL; > + strcpy(skaddr.sun_path, sock_name); > + > + ret = connect(*sockfd, (struct sockaddr *)&skaddr, > + SUN_LEN(&skaddr)); > + if (ret < 0) { > + fprintf(stderr, "<%s>: Failed connect: <%s>\n", > + __func__, strerror(errno)); > + goto err; > + } > + } > + > + return 0; > + > +err: > + if (*sockfd) > + close(*sockfd); > + > + return ret; > +} > + > +int sendtosocket(int sockfd, struct socketdata *skdata) > +{ > + int ret, buffd; > + unsigned int len; > + char cmsg_b[CMSG_SPACE(sizeof(int))]; > + struct cmsghdr *cmsg; > + struct msghdr msgh; > + struct iovec iov; > + struct timeval timeout; > + fd_set selFDs; > + > + if (!skdata) { > + fprintf(stderr, "<%s>: socketdata is NULL\n", __func__); > + return -1; > + } > + > + FD_ZERO(&selFDs); > + FD_SET(0, &selFDs); > + FD_SET(sockfd, &selFDs); > + timeout.tv_sec = 20; > + timeout.tv_usec = 0; > + > + ret = select(sockfd+1, NULL, &selFDs, NULL, &timeout); > + if (ret < 0) { > + fprintf(stderr, "<%s>: Failed select: <%s>\n", > + __func__, strerror(errno)); > + return -1; > + } > + > + if (FD_ISSET(sockfd, &selFDs)) { > + buffd = skdata->data; > + len = skdata->len; > + memset(&msgh, 0, sizeof(msgh)); > + msgh.msg_control = &cmsg_b; > + msgh.msg_controllen = CMSG_LEN(len); > + iov.iov_base = "OK"; > + iov.iov_len = 2; > + msgh.msg_iov = &iov; > + msgh.msg_iovlen = 1; > + cmsg = CMSG_FIRSTHDR(&msgh); > + cmsg->cmsg_level = SOL_SOCKET; > + cmsg->cmsg_type = SCM_RIGHTS; > + cmsg->cmsg_len = CMSG_LEN(len); > + memcpy(CMSG_DATA(cmsg), &buffd, len); > + > + ret = sendmsg(sockfd, &msgh, MSG_DONTWAIT); > + if (ret < 0) { > + fprintf(stderr, "<%s>: Failed sendmsg: <%s>\n", > + __func__, strerror(errno)); > + return -1; > + } > + } > + > + return 0; > +} > + > +int receivefromsocket(int sockfd, struct socketdata *skdata) > +{ > + int ret, buffd; > + unsigned int len = 0; > + char cmsg_b[CMSG_SPACE(sizeof(int))]; > + struct cmsghdr *cmsg; > + struct msghdr msgh; > + struct iovec iov; > + fd_set recvFDs; > + char data[32]; > + > + if (!skdata) { > + fprintf(stderr, "<%s>: socketdata is NULL\n", __func__); > + return -1; > + } > + > + FD_ZERO(&recvFDs); > + FD_SET(0, &recvFDs); > + FD_SET(sockfd, &recvFDs); > + > + ret = select(sockfd+1, &recvFDs, NULL, NULL, NULL); > + if (ret < 0) { > + fprintf(stderr, "<%s>: Failed select: <%s>\n", > + __func__, strerror(errno)); > + return -1; > + } > + > + if (FD_ISSET(sockfd, &recvFDs)) { > + len = sizeof(buffd); > + memset(&msgh, 0, sizeof(msgh)); > + msgh.msg_control = &cmsg_b; > + msgh.msg_controllen = CMSG_LEN(len); > + iov.iov_base = data; > + iov.iov_len = sizeof(data)-1; > + msgh.msg_iov = &iov; > + msgh.msg_iovlen = 1; > + cmsg = CMSG_FIRSTHDR(&msgh); > + cmsg->cmsg_level = SOL_SOCKET; > + cmsg->cmsg_type = SCM_RIGHTS; > + cmsg->cmsg_len = CMSG_LEN(len); > + > + ret = recvmsg(sockfd, &msgh, MSG_DONTWAIT); > + if (ret < 0) { > + fprintf(stderr, "<%s>: Failed recvmsg: <%s>\n", > + __func__, strerror(errno)); > + return -1; > + } > + > + memcpy(&buffd, CMSG_DATA(cmsg), len); > + skdata->data = buffd; > + skdata->len = len; > + } > + return 0; > +} > + > +int closesocket(int sockfd, char *name) > +{ > + char sockname[MAX_SOCK_NAME_LEN]; > + > + if (sockfd) > + close(sockfd); > + sprintf(sockname, "/tmp/%s", name); > + unlink(sockname); > + shutdown(sockfd, 2); > + > + return 0; > +} > diff --git a/tools/testing/selftests/android/ion/ipcsocket.h > b/tools/testing/selftests/android/ion/ipcsocket.h > new file mode 100644 > index 0000000..b3e8449 > --- /dev/null > +++ b/tools/testing/selftests/android/ion/ipcsocket.h > @@ -0,0 +1,35 @@ > + > +#ifndef _IPCSOCKET_H > +#define _IPCSOCKET_H > + > + > +#define MAX_SOCK_NAME_LEN 64 > + > +char sock_name[MAX_SOCK_NAME_LEN]; > + > +/* This structure is responsible for holding the IPC data > + * data: hold the buffer fd > + * len: just the length of 32-bit integer fd > + */ > +struct socketdata { > + int data; > + unsigned int len; > +}; > + > +/* This API is used to open the IPC socket connection > + * name: implies a unique socket name in the system > + * connecttype: implies server(0) or client(1) > + */ > +int opensocket(int *sockfd, const char *name, int connecttype); > + > +/* This is the API to send socket data over IPC socket */ > +int sendtosocket(int sockfd, struct socketdata *data); > + > +/* This is the API to receive socket data over IPC socket */ > +int receivefromsocket(int sockfd, struct socketdata *data); > + > +/* This is the API to close the socket connection */ > +int closesocket(int sockfd, char *name); > + > + > +#endif > diff --git a/tools/testing/selftests/android/run.sh > b/tools/testing/selftests/android/run.sh > new file mode 100755 > index 0000000..dd8edf2 > --- /dev/null > +++ b/tools/testing/selftests/android/run.sh > @@ -0,0 +1,3 @@ > +#!/bin/sh > + > +(cd ion; ./ion_test.sh) > -- > 2.7.4 >