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
>

Reply via email to