linux-kselftest-4.15-rc1
This update to Kselftest consists of cleanup patches, fixes, and a new test for ion buffer sharing. Fixes include changes to skip firmware tests on systems that aren't configured to support them, as opposed to failing them. -----BEGIN PGP SIGNATURE----- iQIcBAABCAAGBQJaDwTMAAoJEAsCRMQNDUMcxoIQAOhQqMKWBg+LGvxqpKsdG+wy zNXSh8WjLJsqyuqnuYef1Aytnrl7sGohnZYS5VLH9awQ8BDr18SyX73mLvxhKtkl XL89Ek8zB5g2Wo3bG7aSX3yDLzLHyXaqRy/eKjPR7uMpPY3muEHHs/PYOGbMxiKj BvwvOaALt9dDnYPgus4hmfzjiZnEvc+jl5vo5vUce68NYvtUdw7Bh8fc4BEtFiI+ JMDOmV40+3t3xs4c+ibsWuBSUOcvQTi/FSGkdYreBBUDM/n8m7lmH/msdZ5TYQcW jPO7DsEPerlbscaI741ZE8ZuB9/MFe8eh34vHfYJEUtsUEsp5zNGXS6ASpQWCopX D0ECH+ec5roWjX9xcp3HE0z63jhY0VtEa9JTSRIFEqqSuR1/fwwg5rt7ksArGJoW UI+3MtGcEDJDUUZjzlAuA/iZg7k5hSP7Hoc/evlrRgczev1v0OBieNezd7wyv4qT WfxoldvIIX+0dGZ1HDcW2vNnrq9dVc7Jk0Vc1JqHfdwJ92WRBx0M2QBtvt1br9Gd FDoMQRbGMWT0iMnewzkVCJbIb6/BkRZENlZ1MQFvspqpKxEGN6tP9tpqT4fp2wAK IKP6f1lU/PlvpTiwkA6p4i/qIdk2wGbofn3Lv9XoGrXYYla5wp75Acp6kodqUmJG KXNV5OtGCCTmCc7+ZNcZ =pE67 -----END PGP SIGNATURE----- Merge tag 'linux-kselftest-4.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest Pull kselftest updates from Shuah Khan: "This update to Kselftest consists of cleanup patches, fixes, and a new test for ion buffer sharing. Fixes include changes to skip firmware tests on systems that aren't configured to support them, as opposed to failing them" * tag 'linux-kselftest-4.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest: selftests: firmware: skip unsupported custom firmware fallback tests selftests: firmware: skip unsupported async loading tests selftests: memfd_test.c: fix compilation warning. selftests/ftrace: Introduce exit_pass and exit_fail selftests: ftrace: add more config fragments android/ion: userspace test utility for ion buffer sharing selftests: remove obsolete kconfig fragment for cpu-hotplug selftests: vdso_test: support ARM64 targets selftests/ftrace: Do not use arch dependent do_IRQ as a target function selftests: breakpoints: fix compile error on breakpoint_test_arm64 selftests: add missing test result status in memory-hotplug test selftests/exec: include cwd in long path calculation selftests: seccomp: update .gitignore with newly added tests selftests: vm: Update .gitignore with newly added tests selftests: timers: Update .gitignore with newly added tests
This commit is contained in:
commit
b1c2a344cc
|
@ -1,5 +1,6 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
TARGETS = bpf
|
||||
TARGETS = android
|
||||
TARGETS += bpf
|
||||
TARGETS += breakpoints
|
||||
TARGETS += capabilities
|
||||
TARGETS += cpufreq
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1,2 @@
|
|||
ionapp_export
|
||||
ionapp_import
|
|
@ -0,0 +1,16 @@
|
|||
|
||||
INCLUDEDIR := -I. -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
|
|
@ -0,0 +1,101 @@
|
|||
ION BUFFER SHARING UTILITY
|
||||
==========================
|
||||
File: ion_test.sh : Utility to test ION driver buffer sharing mechanism.
|
||||
Author: Pintu Kumar <pintu.ping@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
|
|
@ -0,0 +1,4 @@
|
|||
CONFIG_ANDROID=y
|
||||
CONFIG_STAGING=y
|
||||
CONFIG_ION=y
|
||||
CONFIG_ION_SYSTEM_HEAP=y
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* ion.h
|
||||
*
|
||||
* Copyright (C) 2011 Google, Inc.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/* This file is copied from drivers/staging/android/uapi/ion.h
|
||||
* This local copy is required for the selftest to pass, when build
|
||||
* outside the kernel source tree.
|
||||
* Please keep this file in sync with its original file until the
|
||||
* ion driver is moved outside the staging tree.
|
||||
*/
|
||||
|
||||
#ifndef _UAPI_LINUX_ION_H
|
||||
#define _UAPI_LINUX_ION_H
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/**
|
||||
* enum ion_heap_types - list of all possible types of heaps
|
||||
* @ION_HEAP_TYPE_SYSTEM: memory allocated via vmalloc
|
||||
* @ION_HEAP_TYPE_SYSTEM_CONTIG: memory allocated via kmalloc
|
||||
* @ION_HEAP_TYPE_CARVEOUT: memory allocated from a prereserved
|
||||
* carveout heap, allocations are physically
|
||||
* contiguous
|
||||
* @ION_HEAP_TYPE_DMA: memory allocated via DMA API
|
||||
* @ION_NUM_HEAPS: helper for iterating over heaps, a bit mask
|
||||
* is used to identify the heaps, so only 32
|
||||
* total heap types are supported
|
||||
*/
|
||||
enum ion_heap_type {
|
||||
ION_HEAP_TYPE_SYSTEM,
|
||||
ION_HEAP_TYPE_SYSTEM_CONTIG,
|
||||
ION_HEAP_TYPE_CARVEOUT,
|
||||
ION_HEAP_TYPE_CHUNK,
|
||||
ION_HEAP_TYPE_DMA,
|
||||
ION_HEAP_TYPE_CUSTOM, /*
|
||||
* must be last so device specific heaps always
|
||||
* are at the end of this enum
|
||||
*/
|
||||
};
|
||||
|
||||
#define ION_NUM_HEAP_IDS (sizeof(unsigned int) * 8)
|
||||
|
||||
/**
|
||||
* allocation flags - the lower 16 bits are used by core ion, the upper 16
|
||||
* bits are reserved for use by the heaps themselves.
|
||||
*/
|
||||
|
||||
/*
|
||||
* mappings of this buffer should be cached, ion will do cache maintenance
|
||||
* when the buffer is mapped for dma
|
||||
*/
|
||||
#define ION_FLAG_CACHED 1
|
||||
|
||||
/**
|
||||
* DOC: Ion Userspace API
|
||||
*
|
||||
* create a client by opening /dev/ion
|
||||
* most operations handled via following ioctls
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* struct ion_allocation_data - metadata passed from userspace for allocations
|
||||
* @len: size of the allocation
|
||||
* @heap_id_mask: mask of heap ids to allocate from
|
||||
* @flags: flags passed to heap
|
||||
* @handle: pointer that will be populated with a cookie to use to
|
||||
* refer to this allocation
|
||||
*
|
||||
* Provided by userspace as an argument to the ioctl
|
||||
*/
|
||||
struct ion_allocation_data {
|
||||
__u64 len;
|
||||
__u32 heap_id_mask;
|
||||
__u32 flags;
|
||||
__u32 fd;
|
||||
__u32 unused;
|
||||
};
|
||||
|
||||
#define MAX_HEAP_NAME 32
|
||||
|
||||
/**
|
||||
* struct ion_heap_data - data about a heap
|
||||
* @name - first 32 characters of the heap name
|
||||
* @type - heap type
|
||||
* @heap_id - heap id for the heap
|
||||
*/
|
||||
struct ion_heap_data {
|
||||
char name[MAX_HEAP_NAME];
|
||||
__u32 type;
|
||||
__u32 heap_id;
|
||||
__u32 reserved0;
|
||||
__u32 reserved1;
|
||||
__u32 reserved2;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ion_heap_query - collection of data about all heaps
|
||||
* @cnt - total number of heaps to be copied
|
||||
* @heaps - buffer to copy heap data
|
||||
*/
|
||||
struct ion_heap_query {
|
||||
__u32 cnt; /* Total number of heaps to be copied */
|
||||
__u32 reserved0; /* align to 64bits */
|
||||
__u64 heaps; /* buffer to be populated */
|
||||
__u32 reserved1;
|
||||
__u32 reserved2;
|
||||
};
|
||||
|
||||
#define ION_IOC_MAGIC 'I'
|
||||
|
||||
/**
|
||||
* DOC: ION_IOC_ALLOC - allocate memory
|
||||
*
|
||||
* Takes an ion_allocation_data struct and returns it with the handle field
|
||||
* populated with the opaque handle for the allocation.
|
||||
*/
|
||||
#define ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, \
|
||||
struct ion_allocation_data)
|
||||
|
||||
/**
|
||||
* DOC: ION_IOC_HEAP_QUERY - information about available heaps
|
||||
*
|
||||
* Takes an ion_heap_query structure and populates information about
|
||||
* available Ion heaps.
|
||||
*/
|
||||
#define ION_IOC_HEAP_QUERY _IOWR(ION_IOC_MAGIC, 8, \
|
||||
struct ion_heap_query)
|
||||
|
||||
#endif /* _UAPI_LINUX_ION_H */
|
|
@ -0,0 +1,55 @@
|
|||
#!/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
|
||||
}
|
||||
|
||||
main_function
|
||||
echo "$TCID: done"
|
||||
exit $errcode
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* 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.ping@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;
|
||||
default:
|
||||
printf("ERROR: heap type not supported\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;
|
||||
}
|
|
@ -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.ping@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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
(cd ion; ./ion_test.sh)
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <asm/ptrace.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/ptrace.h>
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
CONFIG_NOTIFIER_ERROR_INJECTION=y
|
||||
CONFIG_CPU_NOTIFIER_ERROR_INJECT=m
|
||||
|
|
|
@ -147,7 +147,7 @@ static void exe_cp(const char *src, const char *dest)
|
|||
}
|
||||
|
||||
#define XX_DIR_LEN 200
|
||||
static int check_execveat_pathmax(int dot_dfd, const char *src, int is_script)
|
||||
static int check_execveat_pathmax(int root_dfd, const char *src, int is_script)
|
||||
{
|
||||
int fail = 0;
|
||||
int ii, count, len;
|
||||
|
@ -156,20 +156,30 @@ static int check_execveat_pathmax(int dot_dfd, const char *src, int is_script)
|
|||
|
||||
if (*longpath == '\0') {
|
||||
/* Create a filename close to PATH_MAX in length */
|
||||
char *cwd = getcwd(NULL, 0);
|
||||
|
||||
if (!cwd) {
|
||||
printf("Failed to getcwd(), errno=%d (%s)\n",
|
||||
errno, strerror(errno));
|
||||
return 2;
|
||||
}
|
||||
strcpy(longpath, cwd);
|
||||
strcat(longpath, "/");
|
||||
memset(longname, 'x', XX_DIR_LEN - 1);
|
||||
longname[XX_DIR_LEN - 1] = '/';
|
||||
longname[XX_DIR_LEN] = '\0';
|
||||
count = (PATH_MAX - 3) / XX_DIR_LEN;
|
||||
count = (PATH_MAX - 3 - strlen(cwd)) / XX_DIR_LEN;
|
||||
for (ii = 0; ii < count; ii++) {
|
||||
strcat(longpath, longname);
|
||||
mkdir(longpath, 0755);
|
||||
}
|
||||
len = (PATH_MAX - 3) - (count * XX_DIR_LEN);
|
||||
len = (PATH_MAX - 3 - strlen(cwd)) - (count * XX_DIR_LEN);
|
||||
if (len <= 0)
|
||||
len = 1;
|
||||
memset(longname, 'y', len);
|
||||
longname[len] = '\0';
|
||||
strcat(longpath, longname);
|
||||
free(cwd);
|
||||
}
|
||||
exe_cp(src, longpath);
|
||||
|
||||
|
@ -190,7 +200,7 @@ static int check_execveat_pathmax(int dot_dfd, const char *src, int is_script)
|
|||
}
|
||||
|
||||
/*
|
||||
* Execute as a long pathname relative to ".". If this is a script,
|
||||
* Execute as a long pathname relative to "/". If this is a script,
|
||||
* the interpreter will launch but fail to open the script because its
|
||||
* name ("/dev/fd/5/xxx....") is bigger than PATH_MAX.
|
||||
*
|
||||
|
@ -200,10 +210,10 @@ static int check_execveat_pathmax(int dot_dfd, const char *src, int is_script)
|
|||
* the exit status shall be 126."), so allow either.
|
||||
*/
|
||||
if (is_script)
|
||||
fail += check_execveat_invoked_rc(dot_dfd, longpath, 0,
|
||||
fail += check_execveat_invoked_rc(root_dfd, longpath + 1, 0,
|
||||
127, 126);
|
||||
else
|
||||
fail += check_execveat(dot_dfd, longpath, 0);
|
||||
fail += check_execveat(root_dfd, longpath + 1, 0);
|
||||
|
||||
return fail;
|
||||
}
|
||||
|
@ -218,6 +228,7 @@ static int run_tests(void)
|
|||
int subdir_dfd_ephemeral = open_or_die("subdir.ephemeral",
|
||||
O_DIRECTORY|O_RDONLY);
|
||||
int dot_dfd = open_or_die(".", O_DIRECTORY|O_RDONLY);
|
||||
int root_dfd = open_or_die("/", O_DIRECTORY|O_RDONLY);
|
||||
int dot_dfd_path = open_or_die(".", O_DIRECTORY|O_RDONLY|O_PATH);
|
||||
int dot_dfd_cloexec = open_or_die(".", O_DIRECTORY|O_RDONLY|O_CLOEXEC);
|
||||
int fd = open_or_die("execveat", O_RDONLY);
|
||||
|
@ -353,8 +364,8 @@ static int run_tests(void)
|
|||
/* Attempt to execute relative to non-directory => ENOTDIR */
|
||||
fail += check_execveat_fail(fd, "execveat", 0, ENOTDIR);
|
||||
|
||||
fail += check_execveat_pathmax(dot_dfd, "execveat", 0);
|
||||
fail += check_execveat_pathmax(dot_dfd, "script", 1);
|
||||
fail += check_execveat_pathmax(root_dfd, "execveat", 0);
|
||||
fail += check_execveat_pathmax(root_dfd, "script", 1);
|
||||
return fail;
|
||||
}
|
||||
|
||||
|
|
|
@ -86,6 +86,11 @@ load_fw_cancel()
|
|||
|
||||
load_fw_custom()
|
||||
{
|
||||
if [ ! -e "$DIR"/trigger_custom_fallback ]; then
|
||||
echo "$0: custom fallback trigger not present, ignoring test" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local name="$1"
|
||||
local file="$2"
|
||||
|
||||
|
@ -108,11 +113,17 @@ load_fw_custom()
|
|||
|
||||
# Wait for request to finish.
|
||||
wait
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
load_fw_custom_cancel()
|
||||
{
|
||||
if [ ! -e "$DIR"/trigger_custom_fallback ]; then
|
||||
echo "$0: canceling custom fallback trigger not present, ignoring test" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local name="$1"
|
||||
local file="$2"
|
||||
|
||||
|
@ -133,6 +144,7 @@ load_fw_custom_cancel()
|
|||
|
||||
# Wait for request to finish.
|
||||
wait
|
||||
return 0
|
||||
}
|
||||
|
||||
load_fw_fallback_with_child()
|
||||
|
@ -227,20 +239,22 @@ else
|
|||
echo "$0: cancelling fallback mechanism works"
|
||||
fi
|
||||
|
||||
load_fw_custom "$NAME" "$FW"
|
||||
if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then
|
||||
echo "$0: firmware was not loaded" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "$0: custom fallback loading mechanism works"
|
||||
if load_fw_custom "$NAME" "$FW" ; then
|
||||
if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then
|
||||
echo "$0: firmware was not loaded" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "$0: custom fallback loading mechanism works"
|
||||
fi
|
||||
fi
|
||||
|
||||
load_fw_custom_cancel "nope-$NAME" "$FW"
|
||||
if diff -q "$FW" /dev/test_firmware >/dev/null ; then
|
||||
echo "$0: firmware was expected to be cancelled" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "$0: cancelling custom fallback mechanism works"
|
||||
if load_fw_custom_cancel "nope-$NAME" "$FW" ; then
|
||||
if diff -q "$FW" /dev/test_firmware >/dev/null ; then
|
||||
echo "$0: firmware was expected to be cancelled" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "$0: cancelling custom fallback mechanism works"
|
||||
fi
|
||||
fi
|
||||
|
||||
set +e
|
||||
|
|
|
@ -70,9 +70,13 @@ if printf '\000' >"$DIR"/trigger_request 2> /dev/null; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
if printf '\000' >"$DIR"/trigger_async_request 2> /dev/null; then
|
||||
echo "$0: empty filename should not succeed (async)" >&2
|
||||
exit 1
|
||||
if [ ! -e "$DIR"/trigger_async_request ]; then
|
||||
echo "$0: empty filename: async trigger not present, ignoring test" >&2
|
||||
else
|
||||
if printf '\000' >"$DIR"/trigger_async_request 2> /dev/null; then
|
||||
echo "$0: empty filename should not succeed (async)" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Request a firmware that doesn't exist, it should fail.
|
||||
|
@ -105,17 +109,21 @@ else
|
|||
fi
|
||||
|
||||
# Try the asynchronous version too
|
||||
if ! echo -n "$NAME" >"$DIR"/trigger_async_request ; then
|
||||
echo "$0: could not trigger async request" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify the contents are what we expect.
|
||||
if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then
|
||||
echo "$0: firmware was not loaded (async)" >&2
|
||||
exit 1
|
||||
if [ ! -e "$DIR"/trigger_async_request ]; then
|
||||
echo "$0: firmware loading: async trigger not present, ignoring test" >&2
|
||||
else
|
||||
echo "$0: async filesystem loading works"
|
||||
if ! echo -n "$NAME" >"$DIR"/trigger_async_request ; then
|
||||
echo "$0: could not trigger async request" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify the contents are what we expect.
|
||||
if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then
|
||||
echo "$0: firmware was not loaded (async)" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "$0: async filesystem loading works"
|
||||
fi
|
||||
fi
|
||||
|
||||
### Batched requests tests
|
||||
|
|
|
@ -1,2 +1,6 @@
|
|||
CONFIG_KPROBES=y
|
||||
CONFIG_FTRACE=y
|
||||
CONFIG_FUNCTION_PROFILER=y
|
||||
CONFIG_TRACER_SNAPSHOT=y
|
||||
CONFIG_STACK_TRACER=y
|
||||
CONFIG_HIST_TRIGGERS=y
|
||||
|
|
|
@ -222,7 +222,14 @@ SIG_RESULT=
|
|||
SIG_BASE=36 # Use realtime signals
|
||||
SIG_PID=$$
|
||||
|
||||
exit_pass () {
|
||||
exit 0
|
||||
}
|
||||
|
||||
SIG_FAIL=$((SIG_BASE + FAIL))
|
||||
exit_fail () {
|
||||
exit 1
|
||||
}
|
||||
trap 'SIG_RESULT=$FAIL' $SIG_FAIL
|
||||
|
||||
SIG_UNRESOLVED=$((SIG_BASE + UNRESOLVED))
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
# description: Basic event tracing check
|
||||
test -f available_events -a -f set_event -a -d events
|
||||
# check scheduler events are available
|
||||
grep -q sched available_events && exit 0 || exit $FAIL
|
||||
grep -q sched available_events && exit_pass || exit_fail
|
||||
|
|
|
@ -11,7 +11,7 @@ do_reset() {
|
|||
fail() { #msg
|
||||
do_reset
|
||||
echo $1
|
||||
exit $FAIL
|
||||
exit_fail
|
||||
}
|
||||
|
||||
yield() {
|
||||
|
|
|
@ -13,7 +13,7 @@ do_reset() {
|
|||
fail() { #msg
|
||||
do_reset
|
||||
echo $1
|
||||
exit $FAIL
|
||||
exit_fail
|
||||
}
|
||||
|
||||
yield() {
|
||||
|
|
|
@ -11,7 +11,7 @@ do_reset() {
|
|||
fail() { #msg
|
||||
do_reset
|
||||
echo $1
|
||||
exit $FAIL
|
||||
exit_fail
|
||||
}
|
||||
|
||||
yield() {
|
||||
|
|
|
@ -10,7 +10,7 @@ do_reset() {
|
|||
fail() { #msg
|
||||
do_reset
|
||||
echo $1
|
||||
exit $FAIL
|
||||
exit_fail
|
||||
}
|
||||
|
||||
yield() {
|
||||
|
|
|
@ -28,7 +28,7 @@ do_reset() {
|
|||
fail() { # msg
|
||||
do_reset
|
||||
echo $1
|
||||
exit $FAIL
|
||||
exit_fail
|
||||
}
|
||||
|
||||
disable_tracing
|
||||
|
|
|
@ -18,7 +18,7 @@ do_reset() {
|
|||
fail() { # msg
|
||||
do_reset
|
||||
echo $1
|
||||
exit $FAIL
|
||||
exit_fail
|
||||
}
|
||||
|
||||
disable_tracing
|
||||
|
|
|
@ -51,7 +51,7 @@ do_reset() {
|
|||
fail() { # msg
|
||||
do_reset
|
||||
echo $1
|
||||
exit $FAIL
|
||||
exit_fail
|
||||
}
|
||||
|
||||
yield() {
|
||||
|
|
|
@ -27,7 +27,7 @@ do_reset() {
|
|||
fail() { # mesg
|
||||
do_reset
|
||||
echo $1
|
||||
exit $FAIL
|
||||
exit_fail
|
||||
}
|
||||
|
||||
SLEEP_TIME=".1"
|
||||
|
@ -48,8 +48,7 @@ test_event_enabled() {
|
|||
|
||||
e=`cat $EVENT_ENABLE`
|
||||
if [ "$e" != $val ]; then
|
||||
echo "Expected $val but found $e"
|
||||
exit 1
|
||||
fail "Expected $val but found $e"
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ fail() { # mesg
|
|||
reset_tracer
|
||||
echo > set_ftrace_filter
|
||||
echo $1
|
||||
exit $FAIL
|
||||
exit_fail
|
||||
}
|
||||
|
||||
echo "Testing function tracer with profiler:"
|
||||
|
|
|
@ -26,14 +26,14 @@ do_reset() {
|
|||
fail() { # mesg
|
||||
do_reset
|
||||
echo $1
|
||||
exit $FAIL
|
||||
exit_fail
|
||||
}
|
||||
|
||||
do_reset
|
||||
|
||||
FILTER=set_ftrace_filter
|
||||
FUNC1="schedule"
|
||||
FUNC2="do_IRQ"
|
||||
FUNC2="do_softirq"
|
||||
|
||||
ALL_FUNCS="#### all functions enabled ####"
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ do_reset() {
|
|||
fail() { # mesg
|
||||
do_reset
|
||||
echo $1
|
||||
exit $FAIL
|
||||
exit_fail
|
||||
}
|
||||
|
||||
SLEEP_TIME=".1"
|
||||
|
|
|
@ -11,7 +11,7 @@ fail() { # mesg
|
|||
rmdir foo 2>/dev/null
|
||||
echo $1
|
||||
set -e
|
||||
exit $FAIL
|
||||
exit_fail
|
||||
}
|
||||
|
||||
cd instances
|
||||
|
|
|
@ -11,7 +11,7 @@ fail() { # mesg
|
|||
rmdir x y z 2>/dev/null
|
||||
echo $1
|
||||
set -e
|
||||
exit $FAIL
|
||||
exit_fail
|
||||
}
|
||||
|
||||
cd instances
|
||||
|
|
|
@ -9,7 +9,7 @@ echo > kprobe_events
|
|||
echo p:myevent _do_fork > kprobe_events
|
||||
test -d events/kprobes/myevent
|
||||
echo 1 > events/kprobes/myevent/enable
|
||||
echo > kprobe_events && exit 1 # this must fail
|
||||
echo > kprobe_events && exit_fail # this must fail
|
||||
echo 0 > events/kprobes/myevent/enable
|
||||
echo > kprobe_events # this must succeed
|
||||
clear_trace
|
||||
|
|
|
@ -14,5 +14,5 @@ echo 1 > events/kprobes/testprobe/enable
|
|||
echo 0 > events/kprobes/testprobe/enable
|
||||
echo "-:testprobe" >> kprobe_events
|
||||
clear_trace
|
||||
test -d events/kprobes/testprobe && exit 1 || exit 0
|
||||
test -d events/kprobes/testprobe && exit_fail || exit_pass
|
||||
|
||||
|
|
|
@ -35,4 +35,4 @@ check_types $ARGS
|
|||
|
||||
echo "-:testprobe" >> kprobe_events
|
||||
clear_trace
|
||||
test -d events/kprobes/testprobe && exit 1 || exit 0
|
||||
test -d events/kprobes/testprobe && exit_fail || exit_pass
|
||||
|
|
|
@ -14,4 +14,4 @@ echo 1 > events/kprobes/testprobe2/enable
|
|||
echo 0 > events/kprobes/testprobe2/enable
|
||||
echo '-:testprobe2' >> kprobe_events
|
||||
clear_trace
|
||||
test -d events/kprobes/testprobe2 && exit 1 || exit 0
|
||||
test -d events/kprobes/testprobe2 && exit_fail || exit_pass
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
# Note that all tests are run with "errexit" option.
|
||||
|
||||
exit 0 # Return 0 if the test is passed, otherwise return !0
|
||||
# Or you can call exit_pass for passed test, and exit_fail for failed test.
|
||||
# If the test could not run because of lack of feature, call exit_unsupported
|
||||
# If the test returned unclear results, call exit_unresolved
|
||||
# If the test is a dummy, or a placeholder, call exit_untested
|
||||
|
|
|
@ -12,7 +12,7 @@ do_reset() {
|
|||
fail() { #msg
|
||||
do_reset
|
||||
echo $1
|
||||
exit $FAIL
|
||||
exit_fail
|
||||
}
|
||||
|
||||
if [ ! -f set_event -o ! -d events/sched ]; then
|
||||
|
|
|
@ -12,7 +12,7 @@ do_reset() {
|
|||
fail() { #msg
|
||||
do_reset
|
||||
echo $1
|
||||
exit $FAIL
|
||||
exit_fail
|
||||
}
|
||||
|
||||
if [ ! -f set_event -o ! -d events/sched ]; then
|
||||
|
|
|
@ -12,7 +12,7 @@ do_reset() {
|
|||
fail() { #msg
|
||||
do_reset
|
||||
echo $1
|
||||
exit $FAIL
|
||||
exit_fail
|
||||
}
|
||||
|
||||
if [ ! -f set_event -o ! -d events/sched ]; then
|
||||
|
|
|
@ -12,7 +12,7 @@ do_reset() {
|
|||
fail() { #msg
|
||||
do_reset
|
||||
echo $1
|
||||
exit $FAIL
|
||||
exit_fail
|
||||
}
|
||||
|
||||
if [ ! -f set_event -o ! -d events/sched ]; then
|
||||
|
|
|
@ -12,7 +12,7 @@ do_reset() {
|
|||
fail() { #msg
|
||||
do_reset
|
||||
echo $1
|
||||
exit $FAIL
|
||||
exit_fail
|
||||
}
|
||||
|
||||
if [ ! -f set_event -o ! -d events/sched ]; then
|
||||
|
|
|
@ -11,7 +11,7 @@ do_reset() {
|
|||
fail() { #msg
|
||||
do_reset
|
||||
echo $1
|
||||
exit $FAIL
|
||||
exit_fail
|
||||
}
|
||||
|
||||
if [ ! -f set_event -o ! -d events/sched ]; then
|
||||
|
|
|
@ -11,7 +11,7 @@ do_reset() {
|
|||
fail() { #msg
|
||||
do_reset
|
||||
echo $1
|
||||
exit $FAIL
|
||||
exit_fail
|
||||
}
|
||||
|
||||
if [ ! -f set_event -o ! -d events/sched ]; then
|
||||
|
|
|
@ -11,7 +11,7 @@ do_reset() {
|
|||
fail() { #msg
|
||||
do_reset
|
||||
echo $1
|
||||
exit $FAIL
|
||||
exit_fail
|
||||
}
|
||||
|
||||
if [ ! -f set_event -o ! -d events/sched ]; then
|
||||
|
|
|
@ -515,7 +515,7 @@ static void mfd_assert_grow_write(int fd)
|
|||
|
||||
buf = malloc(mfd_def_size * 8);
|
||||
if (!buf) {
|
||||
printf("malloc(%d) failed: %m\n", mfd_def_size * 8);
|
||||
printf("malloc(%zu) failed: %m\n", mfd_def_size * 8);
|
||||
abort();
|
||||
}
|
||||
|
||||
|
@ -535,7 +535,7 @@ static void mfd_fail_grow_write(int fd)
|
|||
|
||||
buf = malloc(mfd_def_size * 8);
|
||||
if (!buf) {
|
||||
printf("malloc(%d) failed: %m\n", mfd_def_size * 8);
|
||||
printf("malloc(%zu) failed: %m\n", mfd_def_size * 8);
|
||||
abort();
|
||||
}
|
||||
|
||||
|
|
|
@ -4,10 +4,10 @@ all:
|
|||
include ../lib.mk
|
||||
|
||||
TEST_PROGS := mem-on-off-test.sh
|
||||
override RUN_TESTS := ./mem-on-off-test.sh -r 2 || echo "selftests: memory-hotplug [FAIL]"
|
||||
override RUN_TESTS := ./mem-on-off-test.sh -r 2 && echo "selftests: memory-hotplug [PASS]" || echo "selftests: memory-hotplug [FAIL]"
|
||||
override EMIT_TESTS := echo "$(RUN_TESTS)"
|
||||
|
||||
run_full_test:
|
||||
@/bin/bash ./mem-on-off-test.sh || echo "memory-hotplug selftests: [FAIL]"
|
||||
@/bin/bash ./mem-on-off-test.sh && echo "memory-hotplug selftests: [PASS]" || echo "memory-hotplug selftests: [FAIL]"
|
||||
|
||||
clean:
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
seccomp_bpf
|
||||
seccomp_benchmark
|
||||
|
|
|
@ -18,3 +18,5 @@ threadtest
|
|||
valid-adjtimex
|
||||
adjtick
|
||||
set-tz
|
||||
freq-step
|
||||
rtctest_setdate
|
||||
|
|
|
@ -19,6 +19,19 @@ extern void *vdso_sym(const char *version, const char *name);
|
|||
extern void vdso_init_from_sysinfo_ehdr(uintptr_t base);
|
||||
extern void vdso_init_from_auxv(void *auxv);
|
||||
|
||||
/*
|
||||
* ARM64's vDSO exports its gettimeofday() implementation with a different
|
||||
* name and version from other architectures, so we need to handle it as
|
||||
* a special case.
|
||||
*/
|
||||
#if defined(__aarch64__)
|
||||
const char *version = "LINUX_2.6.39";
|
||||
const char *name = "__kernel_gettimeofday";
|
||||
#else
|
||||
const char *version = "LINUX_2.6";
|
||||
const char *name = "__vdso_gettimeofday";
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR);
|
||||
|
@ -31,10 +44,10 @@ int main(int argc, char **argv)
|
|||
|
||||
/* Find gettimeofday. */
|
||||
typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz);
|
||||
gtod_t gtod = (gtod_t)vdso_sym("LINUX_2.6", "__vdso_gettimeofday");
|
||||
gtod_t gtod = (gtod_t)vdso_sym(version, name);
|
||||
|
||||
if (!gtod) {
|
||||
printf("Could not find __vdso_gettimeofday\n");
|
||||
printf("Could not find %s\n", name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -45,7 +58,7 @@ int main(int argc, char **argv)
|
|||
printf("The time is %lld.%06lld\n",
|
||||
(long long)tv.tv_sec, (long long)tv.tv_usec);
|
||||
} else {
|
||||
printf("__vdso_gettimeofday failed\n");
|
||||
printf("%s failed\n", name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -8,3 +8,5 @@ on-fault-limit
|
|||
transhuge-stress
|
||||
userfaultfd
|
||||
mlock-intersect-test
|
||||
mlock-random-test
|
||||
virtual_address_range
|
||||
|
|
Loading…
Reference in New Issue