18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2016 Red Hat, Inc.
48c2ecf20Sopenharmony_ci * Author: Michael S. Tsirkin <mst@redhat.com>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Command line processing and common functions for ring benchmarking.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci#define _GNU_SOURCE
98c2ecf20Sopenharmony_ci#include <getopt.h>
108c2ecf20Sopenharmony_ci#include <pthread.h>
118c2ecf20Sopenharmony_ci#include <assert.h>
128c2ecf20Sopenharmony_ci#include <sched.h>
138c2ecf20Sopenharmony_ci#include "main.h"
148c2ecf20Sopenharmony_ci#include <sys/eventfd.h>
158c2ecf20Sopenharmony_ci#include <stdlib.h>
168c2ecf20Sopenharmony_ci#include <stdio.h>
178c2ecf20Sopenharmony_ci#include <unistd.h>
188c2ecf20Sopenharmony_ci#include <limits.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ciint runcycles = 10000000;
218c2ecf20Sopenharmony_ciint max_outstanding = INT_MAX;
228c2ecf20Sopenharmony_ciint batch = 1;
238c2ecf20Sopenharmony_ciint param = 0;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cibool do_sleep = false;
268c2ecf20Sopenharmony_cibool do_relax = false;
278c2ecf20Sopenharmony_cibool do_exit = true;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ciunsigned ring_size = 256;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic int kickfd = -1;
328c2ecf20Sopenharmony_cistatic int callfd = -1;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_civoid notify(int fd)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	unsigned long long v = 1;
378c2ecf20Sopenharmony_ci	int r;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	vmexit();
408c2ecf20Sopenharmony_ci	r = write(fd, &v, sizeof v);
418c2ecf20Sopenharmony_ci	assert(r == sizeof v);
428c2ecf20Sopenharmony_ci	vmentry();
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_civoid wait_for_notify(int fd)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	unsigned long long v = 1;
488c2ecf20Sopenharmony_ci	int r;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	vmexit();
518c2ecf20Sopenharmony_ci	r = read(fd, &v, sizeof v);
528c2ecf20Sopenharmony_ci	assert(r == sizeof v);
538c2ecf20Sopenharmony_ci	vmentry();
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_civoid kick(void)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	notify(kickfd);
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_civoid wait_for_kick(void)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	wait_for_notify(kickfd);
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_civoid call(void)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	notify(callfd);
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_civoid wait_for_call(void)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	wait_for_notify(callfd);
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_civoid set_affinity(const char *arg)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	cpu_set_t cpuset;
798c2ecf20Sopenharmony_ci	int ret;
808c2ecf20Sopenharmony_ci	pthread_t self;
818c2ecf20Sopenharmony_ci	long int cpu;
828c2ecf20Sopenharmony_ci	char *endptr;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	if (!arg)
858c2ecf20Sopenharmony_ci		return;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	cpu = strtol(arg, &endptr, 0);
888c2ecf20Sopenharmony_ci	assert(!*endptr);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	assert(cpu >= 0 && cpu < CPU_SETSIZE);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	self = pthread_self();
938c2ecf20Sopenharmony_ci	CPU_ZERO(&cpuset);
948c2ecf20Sopenharmony_ci	CPU_SET(cpu, &cpuset);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	ret = pthread_setaffinity_np(self, sizeof(cpu_set_t), &cpuset);
978c2ecf20Sopenharmony_ci	assert(!ret);
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_civoid poll_used(void)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	while (used_empty())
1038c2ecf20Sopenharmony_ci		busy_wait();
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic void __attribute__((__flatten__)) run_guest(void)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	int completed_before;
1098c2ecf20Sopenharmony_ci	int completed = 0;
1108c2ecf20Sopenharmony_ci	int started = 0;
1118c2ecf20Sopenharmony_ci	int bufs = runcycles;
1128c2ecf20Sopenharmony_ci	int spurious = 0;
1138c2ecf20Sopenharmony_ci	int r;
1148c2ecf20Sopenharmony_ci	unsigned len;
1158c2ecf20Sopenharmony_ci	void *buf;
1168c2ecf20Sopenharmony_ci	int tokick = batch;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	for (;;) {
1198c2ecf20Sopenharmony_ci		if (do_sleep)
1208c2ecf20Sopenharmony_ci			disable_call();
1218c2ecf20Sopenharmony_ci		completed_before = completed;
1228c2ecf20Sopenharmony_ci		do {
1238c2ecf20Sopenharmony_ci			if (started < bufs &&
1248c2ecf20Sopenharmony_ci			    started - completed < max_outstanding) {
1258c2ecf20Sopenharmony_ci				r = add_inbuf(0, "Buffer\n", "Hello, world!");
1268c2ecf20Sopenharmony_ci				if (__builtin_expect(r == 0, true)) {
1278c2ecf20Sopenharmony_ci					++started;
1288c2ecf20Sopenharmony_ci					if (!--tokick) {
1298c2ecf20Sopenharmony_ci						tokick = batch;
1308c2ecf20Sopenharmony_ci						if (do_sleep)
1318c2ecf20Sopenharmony_ci							kick_available();
1328c2ecf20Sopenharmony_ci					}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci				}
1358c2ecf20Sopenharmony_ci			} else
1368c2ecf20Sopenharmony_ci				r = -1;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci			/* Flush out completed bufs if any */
1398c2ecf20Sopenharmony_ci			if (get_buf(&len, &buf)) {
1408c2ecf20Sopenharmony_ci				++completed;
1418c2ecf20Sopenharmony_ci				if (__builtin_expect(completed == bufs, false))
1428c2ecf20Sopenharmony_ci					return;
1438c2ecf20Sopenharmony_ci				r = 0;
1448c2ecf20Sopenharmony_ci			}
1458c2ecf20Sopenharmony_ci		} while (r == 0);
1468c2ecf20Sopenharmony_ci		if (completed == completed_before)
1478c2ecf20Sopenharmony_ci			++spurious;
1488c2ecf20Sopenharmony_ci		assert(completed <= bufs);
1498c2ecf20Sopenharmony_ci		assert(started <= bufs);
1508c2ecf20Sopenharmony_ci		if (do_sleep) {
1518c2ecf20Sopenharmony_ci			if (used_empty() && enable_call())
1528c2ecf20Sopenharmony_ci				wait_for_call();
1538c2ecf20Sopenharmony_ci		} else {
1548c2ecf20Sopenharmony_ci			poll_used();
1558c2ecf20Sopenharmony_ci		}
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_civoid poll_avail(void)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	while (avail_empty())
1628c2ecf20Sopenharmony_ci		busy_wait();
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic void __attribute__((__flatten__)) run_host(void)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	int completed_before;
1688c2ecf20Sopenharmony_ci	int completed = 0;
1698c2ecf20Sopenharmony_ci	int spurious = 0;
1708c2ecf20Sopenharmony_ci	int bufs = runcycles;
1718c2ecf20Sopenharmony_ci	unsigned len;
1728c2ecf20Sopenharmony_ci	void *buf;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	for (;;) {
1758c2ecf20Sopenharmony_ci		if (do_sleep) {
1768c2ecf20Sopenharmony_ci			if (avail_empty() && enable_kick())
1778c2ecf20Sopenharmony_ci				wait_for_kick();
1788c2ecf20Sopenharmony_ci		} else {
1798c2ecf20Sopenharmony_ci			poll_avail();
1808c2ecf20Sopenharmony_ci		}
1818c2ecf20Sopenharmony_ci		if (do_sleep)
1828c2ecf20Sopenharmony_ci			disable_kick();
1838c2ecf20Sopenharmony_ci		completed_before = completed;
1848c2ecf20Sopenharmony_ci		while (__builtin_expect(use_buf(&len, &buf), true)) {
1858c2ecf20Sopenharmony_ci			if (do_sleep)
1868c2ecf20Sopenharmony_ci				call_used();
1878c2ecf20Sopenharmony_ci			++completed;
1888c2ecf20Sopenharmony_ci			if (__builtin_expect(completed == bufs, false))
1898c2ecf20Sopenharmony_ci				return;
1908c2ecf20Sopenharmony_ci		}
1918c2ecf20Sopenharmony_ci		if (completed == completed_before)
1928c2ecf20Sopenharmony_ci			++spurious;
1938c2ecf20Sopenharmony_ci		assert(completed <= bufs);
1948c2ecf20Sopenharmony_ci		if (completed == bufs)
1958c2ecf20Sopenharmony_ci			break;
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_civoid *start_guest(void *arg)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	set_affinity(arg);
2028c2ecf20Sopenharmony_ci	run_guest();
2038c2ecf20Sopenharmony_ci	pthread_exit(NULL);
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_civoid *start_host(void *arg)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	set_affinity(arg);
2098c2ecf20Sopenharmony_ci	run_host();
2108c2ecf20Sopenharmony_ci	pthread_exit(NULL);
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistatic const char optstring[] = "";
2148c2ecf20Sopenharmony_cistatic const struct option longopts[] = {
2158c2ecf20Sopenharmony_ci	{
2168c2ecf20Sopenharmony_ci		.name = "help",
2178c2ecf20Sopenharmony_ci		.has_arg = no_argument,
2188c2ecf20Sopenharmony_ci		.val = 'h',
2198c2ecf20Sopenharmony_ci	},
2208c2ecf20Sopenharmony_ci	{
2218c2ecf20Sopenharmony_ci		.name = "host-affinity",
2228c2ecf20Sopenharmony_ci		.has_arg = required_argument,
2238c2ecf20Sopenharmony_ci		.val = 'H',
2248c2ecf20Sopenharmony_ci	},
2258c2ecf20Sopenharmony_ci	{
2268c2ecf20Sopenharmony_ci		.name = "guest-affinity",
2278c2ecf20Sopenharmony_ci		.has_arg = required_argument,
2288c2ecf20Sopenharmony_ci		.val = 'G',
2298c2ecf20Sopenharmony_ci	},
2308c2ecf20Sopenharmony_ci	{
2318c2ecf20Sopenharmony_ci		.name = "ring-size",
2328c2ecf20Sopenharmony_ci		.has_arg = required_argument,
2338c2ecf20Sopenharmony_ci		.val = 'R',
2348c2ecf20Sopenharmony_ci	},
2358c2ecf20Sopenharmony_ci	{
2368c2ecf20Sopenharmony_ci		.name = "run-cycles",
2378c2ecf20Sopenharmony_ci		.has_arg = required_argument,
2388c2ecf20Sopenharmony_ci		.val = 'C',
2398c2ecf20Sopenharmony_ci	},
2408c2ecf20Sopenharmony_ci	{
2418c2ecf20Sopenharmony_ci		.name = "outstanding",
2428c2ecf20Sopenharmony_ci		.has_arg = required_argument,
2438c2ecf20Sopenharmony_ci		.val = 'o',
2448c2ecf20Sopenharmony_ci	},
2458c2ecf20Sopenharmony_ci	{
2468c2ecf20Sopenharmony_ci		.name = "batch",
2478c2ecf20Sopenharmony_ci		.has_arg = required_argument,
2488c2ecf20Sopenharmony_ci		.val = 'b',
2498c2ecf20Sopenharmony_ci	},
2508c2ecf20Sopenharmony_ci	{
2518c2ecf20Sopenharmony_ci		.name = "param",
2528c2ecf20Sopenharmony_ci		.has_arg = required_argument,
2538c2ecf20Sopenharmony_ci		.val = 'p',
2548c2ecf20Sopenharmony_ci	},
2558c2ecf20Sopenharmony_ci	{
2568c2ecf20Sopenharmony_ci		.name = "sleep",
2578c2ecf20Sopenharmony_ci		.has_arg = no_argument,
2588c2ecf20Sopenharmony_ci		.val = 's',
2598c2ecf20Sopenharmony_ci	},
2608c2ecf20Sopenharmony_ci	{
2618c2ecf20Sopenharmony_ci		.name = "relax",
2628c2ecf20Sopenharmony_ci		.has_arg = no_argument,
2638c2ecf20Sopenharmony_ci		.val = 'x',
2648c2ecf20Sopenharmony_ci	},
2658c2ecf20Sopenharmony_ci	{
2668c2ecf20Sopenharmony_ci		.name = "exit",
2678c2ecf20Sopenharmony_ci		.has_arg = no_argument,
2688c2ecf20Sopenharmony_ci		.val = 'e',
2698c2ecf20Sopenharmony_ci	},
2708c2ecf20Sopenharmony_ci	{
2718c2ecf20Sopenharmony_ci	}
2728c2ecf20Sopenharmony_ci};
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic void help(void)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	fprintf(stderr, "Usage: <test> [--help]"
2778c2ecf20Sopenharmony_ci		" [--host-affinity H]"
2788c2ecf20Sopenharmony_ci		" [--guest-affinity G]"
2798c2ecf20Sopenharmony_ci		" [--ring-size R (default: %d)]"
2808c2ecf20Sopenharmony_ci		" [--run-cycles C (default: %d)]"
2818c2ecf20Sopenharmony_ci		" [--batch b]"
2828c2ecf20Sopenharmony_ci		" [--outstanding o]"
2838c2ecf20Sopenharmony_ci		" [--param p]"
2848c2ecf20Sopenharmony_ci		" [--sleep]"
2858c2ecf20Sopenharmony_ci		" [--relax]"
2868c2ecf20Sopenharmony_ci		" [--exit]"
2878c2ecf20Sopenharmony_ci		"\n",
2888c2ecf20Sopenharmony_ci		ring_size,
2898c2ecf20Sopenharmony_ci		runcycles);
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ciint main(int argc, char **argv)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	int ret;
2958c2ecf20Sopenharmony_ci	pthread_t host, guest;
2968c2ecf20Sopenharmony_ci	void *tret;
2978c2ecf20Sopenharmony_ci	char *host_arg = NULL;
2988c2ecf20Sopenharmony_ci	char *guest_arg = NULL;
2998c2ecf20Sopenharmony_ci	char *endptr;
3008c2ecf20Sopenharmony_ci	long int c;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	kickfd = eventfd(0, 0);
3038c2ecf20Sopenharmony_ci	assert(kickfd >= 0);
3048c2ecf20Sopenharmony_ci	callfd = eventfd(0, 0);
3058c2ecf20Sopenharmony_ci	assert(callfd >= 0);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	for (;;) {
3088c2ecf20Sopenharmony_ci		int o = getopt_long(argc, argv, optstring, longopts, NULL);
3098c2ecf20Sopenharmony_ci		switch (o) {
3108c2ecf20Sopenharmony_ci		case -1:
3118c2ecf20Sopenharmony_ci			goto done;
3128c2ecf20Sopenharmony_ci		case '?':
3138c2ecf20Sopenharmony_ci			help();
3148c2ecf20Sopenharmony_ci			exit(2);
3158c2ecf20Sopenharmony_ci		case 'H':
3168c2ecf20Sopenharmony_ci			host_arg = optarg;
3178c2ecf20Sopenharmony_ci			break;
3188c2ecf20Sopenharmony_ci		case 'G':
3198c2ecf20Sopenharmony_ci			guest_arg = optarg;
3208c2ecf20Sopenharmony_ci			break;
3218c2ecf20Sopenharmony_ci		case 'R':
3228c2ecf20Sopenharmony_ci			ring_size = strtol(optarg, &endptr, 0);
3238c2ecf20Sopenharmony_ci			assert(ring_size && !(ring_size & (ring_size - 1)));
3248c2ecf20Sopenharmony_ci			assert(!*endptr);
3258c2ecf20Sopenharmony_ci			break;
3268c2ecf20Sopenharmony_ci		case 'C':
3278c2ecf20Sopenharmony_ci			c = strtol(optarg, &endptr, 0);
3288c2ecf20Sopenharmony_ci			assert(!*endptr);
3298c2ecf20Sopenharmony_ci			assert(c > 0 && c < INT_MAX);
3308c2ecf20Sopenharmony_ci			runcycles = c;
3318c2ecf20Sopenharmony_ci			break;
3328c2ecf20Sopenharmony_ci		case 'o':
3338c2ecf20Sopenharmony_ci			c = strtol(optarg, &endptr, 0);
3348c2ecf20Sopenharmony_ci			assert(!*endptr);
3358c2ecf20Sopenharmony_ci			assert(c > 0 && c < INT_MAX);
3368c2ecf20Sopenharmony_ci			max_outstanding = c;
3378c2ecf20Sopenharmony_ci			break;
3388c2ecf20Sopenharmony_ci		case 'p':
3398c2ecf20Sopenharmony_ci			c = strtol(optarg, &endptr, 0);
3408c2ecf20Sopenharmony_ci			assert(!*endptr);
3418c2ecf20Sopenharmony_ci			assert(c > 0 && c < INT_MAX);
3428c2ecf20Sopenharmony_ci			param = c;
3438c2ecf20Sopenharmony_ci			break;
3448c2ecf20Sopenharmony_ci		case 'b':
3458c2ecf20Sopenharmony_ci			c = strtol(optarg, &endptr, 0);
3468c2ecf20Sopenharmony_ci			assert(!*endptr);
3478c2ecf20Sopenharmony_ci			assert(c > 0 && c < INT_MAX);
3488c2ecf20Sopenharmony_ci			batch = c;
3498c2ecf20Sopenharmony_ci			break;
3508c2ecf20Sopenharmony_ci		case 's':
3518c2ecf20Sopenharmony_ci			do_sleep = true;
3528c2ecf20Sopenharmony_ci			break;
3538c2ecf20Sopenharmony_ci		case 'x':
3548c2ecf20Sopenharmony_ci			do_relax = true;
3558c2ecf20Sopenharmony_ci			break;
3568c2ecf20Sopenharmony_ci		case 'e':
3578c2ecf20Sopenharmony_ci			do_exit = true;
3588c2ecf20Sopenharmony_ci			break;
3598c2ecf20Sopenharmony_ci		default:
3608c2ecf20Sopenharmony_ci			help();
3618c2ecf20Sopenharmony_ci			exit(4);
3628c2ecf20Sopenharmony_ci			break;
3638c2ecf20Sopenharmony_ci		}
3648c2ecf20Sopenharmony_ci	}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	/* does nothing here, used to make sure all smp APIs compile */
3678c2ecf20Sopenharmony_ci	smp_acquire();
3688c2ecf20Sopenharmony_ci	smp_release();
3698c2ecf20Sopenharmony_ci	smp_mb();
3708c2ecf20Sopenharmony_cidone:
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	if (batch > max_outstanding)
3738c2ecf20Sopenharmony_ci		batch = max_outstanding;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	if (optind < argc) {
3768c2ecf20Sopenharmony_ci		help();
3778c2ecf20Sopenharmony_ci		exit(4);
3788c2ecf20Sopenharmony_ci	}
3798c2ecf20Sopenharmony_ci	alloc_ring();
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	ret = pthread_create(&host, NULL, start_host, host_arg);
3828c2ecf20Sopenharmony_ci	assert(!ret);
3838c2ecf20Sopenharmony_ci	ret = pthread_create(&guest, NULL, start_guest, guest_arg);
3848c2ecf20Sopenharmony_ci	assert(!ret);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	ret = pthread_join(guest, &tret);
3878c2ecf20Sopenharmony_ci	assert(!ret);
3888c2ecf20Sopenharmony_ci	ret = pthread_join(host, &tret);
3898c2ecf20Sopenharmony_ci	assert(!ret);
3908c2ecf20Sopenharmony_ci	return 0;
3918c2ecf20Sopenharmony_ci}
392