| /* | 
 |  * Copyright (C) 2016 Red Hat, Inc. | 
 |  * Author: Michael S. Tsirkin <mst@redhat.com> | 
 |  * This work is licensed under the terms of the GNU GPL, version 2. | 
 |  * | 
 |  * Command line processing and common functions for ring benchmarking. | 
 |  */ | 
 | #define _GNU_SOURCE | 
 | #include <getopt.h> | 
 | #include <pthread.h> | 
 | #include <assert.h> | 
 | #include <sched.h> | 
 | #include "main.h" | 
 | #include <sys/eventfd.h> | 
 | #include <stdlib.h> | 
 | #include <stdio.h> | 
 | #include <unistd.h> | 
 | #include <limits.h> | 
 |  | 
 | int runcycles = 10000000; | 
 | int max_outstanding = INT_MAX; | 
 | int batch = 1; | 
 | int param = 0; | 
 |  | 
 | bool do_sleep = false; | 
 | bool do_relax = false; | 
 | bool do_exit = true; | 
 |  | 
 | unsigned ring_size = 256; | 
 |  | 
 | static int kickfd = -1; | 
 | static int callfd = -1; | 
 |  | 
 | void notify(int fd) | 
 | { | 
 | 	unsigned long long v = 1; | 
 | 	int r; | 
 |  | 
 | 	vmexit(); | 
 | 	r = write(fd, &v, sizeof v); | 
 | 	assert(r == sizeof v); | 
 | 	vmentry(); | 
 | } | 
 |  | 
 | void wait_for_notify(int fd) | 
 | { | 
 | 	unsigned long long v = 1; | 
 | 	int r; | 
 |  | 
 | 	vmexit(); | 
 | 	r = read(fd, &v, sizeof v); | 
 | 	assert(r == sizeof v); | 
 | 	vmentry(); | 
 | } | 
 |  | 
 | void kick(void) | 
 | { | 
 | 	notify(kickfd); | 
 | } | 
 |  | 
 | void wait_for_kick(void) | 
 | { | 
 | 	wait_for_notify(kickfd); | 
 | } | 
 |  | 
 | void call(void) | 
 | { | 
 | 	notify(callfd); | 
 | } | 
 |  | 
 | void wait_for_call(void) | 
 | { | 
 | 	wait_for_notify(callfd); | 
 | } | 
 |  | 
 | void set_affinity(const char *arg) | 
 | { | 
 | 	cpu_set_t cpuset; | 
 | 	int ret; | 
 | 	pthread_t self; | 
 | 	long int cpu; | 
 | 	char *endptr; | 
 |  | 
 | 	if (!arg) | 
 | 		return; | 
 |  | 
 | 	cpu = strtol(arg, &endptr, 0); | 
 | 	assert(!*endptr); | 
 |  | 
 | 	assert(cpu >= 0 && cpu < CPU_SETSIZE); | 
 |  | 
 | 	self = pthread_self(); | 
 | 	CPU_ZERO(&cpuset); | 
 | 	CPU_SET(cpu, &cpuset); | 
 |  | 
 | 	ret = pthread_setaffinity_np(self, sizeof(cpu_set_t), &cpuset); | 
 | 	assert(!ret); | 
 | } | 
 |  | 
 | void poll_used(void) | 
 | { | 
 | 	while (used_empty()) | 
 | 		busy_wait(); | 
 | } | 
 |  | 
 | static void __attribute__((__flatten__)) run_guest(void) | 
 | { | 
 | 	int completed_before; | 
 | 	int completed = 0; | 
 | 	int started = 0; | 
 | 	int bufs = runcycles; | 
 | 	int spurious = 0; | 
 | 	int r; | 
 | 	unsigned len; | 
 | 	void *buf; | 
 | 	int tokick = batch; | 
 |  | 
 | 	for (;;) { | 
 | 		if (do_sleep) | 
 | 			disable_call(); | 
 | 		completed_before = completed; | 
 | 		do { | 
 | 			if (started < bufs && | 
 | 			    started - completed < max_outstanding) { | 
 | 				r = add_inbuf(0, "Buffer\n", "Hello, world!"); | 
 | 				if (__builtin_expect(r == 0, true)) { | 
 | 					++started; | 
 | 					if (!--tokick) { | 
 | 						tokick = batch; | 
 | 						if (do_sleep) | 
 | 							kick_available(); | 
 | 					} | 
 |  | 
 | 				} | 
 | 			} else | 
 | 				r = -1; | 
 |  | 
 | 			/* Flush out completed bufs if any */ | 
 | 			if (get_buf(&len, &buf)) { | 
 | 				++completed; | 
 | 				if (__builtin_expect(completed == bufs, false)) | 
 | 					return; | 
 | 				r = 0; | 
 | 			} | 
 | 		} while (r == 0); | 
 | 		if (completed == completed_before) | 
 | 			++spurious; | 
 | 		assert(completed <= bufs); | 
 | 		assert(started <= bufs); | 
 | 		if (do_sleep) { | 
 | 			if (used_empty() && enable_call()) | 
 | 				wait_for_call(); | 
 | 		} else { | 
 | 			poll_used(); | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | void poll_avail(void) | 
 | { | 
 | 	while (avail_empty()) | 
 | 		busy_wait(); | 
 | } | 
 |  | 
 | static void __attribute__((__flatten__)) run_host(void) | 
 | { | 
 | 	int completed_before; | 
 | 	int completed = 0; | 
 | 	int spurious = 0; | 
 | 	int bufs = runcycles; | 
 | 	unsigned len; | 
 | 	void *buf; | 
 |  | 
 | 	for (;;) { | 
 | 		if (do_sleep) { | 
 | 			if (avail_empty() && enable_kick()) | 
 | 				wait_for_kick(); | 
 | 		} else { | 
 | 			poll_avail(); | 
 | 		} | 
 | 		if (do_sleep) | 
 | 			disable_kick(); | 
 | 		completed_before = completed; | 
 | 		while (__builtin_expect(use_buf(&len, &buf), true)) { | 
 | 			if (do_sleep) | 
 | 				call_used(); | 
 | 			++completed; | 
 | 			if (__builtin_expect(completed == bufs, false)) | 
 | 				return; | 
 | 		} | 
 | 		if (completed == completed_before) | 
 | 			++spurious; | 
 | 		assert(completed <= bufs); | 
 | 		if (completed == bufs) | 
 | 			break; | 
 | 	} | 
 | } | 
 |  | 
 | void *start_guest(void *arg) | 
 | { | 
 | 	set_affinity(arg); | 
 | 	run_guest(); | 
 | 	pthread_exit(NULL); | 
 | } | 
 |  | 
 | void *start_host(void *arg) | 
 | { | 
 | 	set_affinity(arg); | 
 | 	run_host(); | 
 | 	pthread_exit(NULL); | 
 | } | 
 |  | 
 | static const char optstring[] = ""; | 
 | static const struct option longopts[] = { | 
 | 	{ | 
 | 		.name = "help", | 
 | 		.has_arg = no_argument, | 
 | 		.val = 'h', | 
 | 	}, | 
 | 	{ | 
 | 		.name = "host-affinity", | 
 | 		.has_arg = required_argument, | 
 | 		.val = 'H', | 
 | 	}, | 
 | 	{ | 
 | 		.name = "guest-affinity", | 
 | 		.has_arg = required_argument, | 
 | 		.val = 'G', | 
 | 	}, | 
 | 	{ | 
 | 		.name = "ring-size", | 
 | 		.has_arg = required_argument, | 
 | 		.val = 'R', | 
 | 	}, | 
 | 	{ | 
 | 		.name = "run-cycles", | 
 | 		.has_arg = required_argument, | 
 | 		.val = 'C', | 
 | 	}, | 
 | 	{ | 
 | 		.name = "outstanding", | 
 | 		.has_arg = required_argument, | 
 | 		.val = 'o', | 
 | 	}, | 
 | 	{ | 
 | 		.name = "batch", | 
 | 		.has_arg = required_argument, | 
 | 		.val = 'b', | 
 | 	}, | 
 | 	{ | 
 | 		.name = "param", | 
 | 		.has_arg = required_argument, | 
 | 		.val = 'p', | 
 | 	}, | 
 | 	{ | 
 | 		.name = "sleep", | 
 | 		.has_arg = no_argument, | 
 | 		.val = 's', | 
 | 	}, | 
 | 	{ | 
 | 		.name = "relax", | 
 | 		.has_arg = no_argument, | 
 | 		.val = 'x', | 
 | 	}, | 
 | 	{ | 
 | 		.name = "exit", | 
 | 		.has_arg = no_argument, | 
 | 		.val = 'e', | 
 | 	}, | 
 | 	{ | 
 | 	} | 
 | }; | 
 |  | 
 | static void help(void) | 
 | { | 
 | 	fprintf(stderr, "Usage: <test> [--help]" | 
 | 		" [--host-affinity H]" | 
 | 		" [--guest-affinity G]" | 
 | 		" [--ring-size R (default: %d)]" | 
 | 		" [--run-cycles C (default: %d)]" | 
 | 		" [--batch b]" | 
 | 		" [--outstanding o]" | 
 | 		" [--param p]" | 
 | 		" [--sleep]" | 
 | 		" [--relax]" | 
 | 		" [--exit]" | 
 | 		"\n", | 
 | 		ring_size, | 
 | 		runcycles); | 
 | } | 
 |  | 
 | int main(int argc, char **argv) | 
 | { | 
 | 	int ret; | 
 | 	pthread_t host, guest; | 
 | 	void *tret; | 
 | 	char *host_arg = NULL; | 
 | 	char *guest_arg = NULL; | 
 | 	char *endptr; | 
 | 	long int c; | 
 |  | 
 | 	kickfd = eventfd(0, 0); | 
 | 	assert(kickfd >= 0); | 
 | 	callfd = eventfd(0, 0); | 
 | 	assert(callfd >= 0); | 
 |  | 
 | 	for (;;) { | 
 | 		int o = getopt_long(argc, argv, optstring, longopts, NULL); | 
 | 		switch (o) { | 
 | 		case -1: | 
 | 			goto done; | 
 | 		case '?': | 
 | 			help(); | 
 | 			exit(2); | 
 | 		case 'H': | 
 | 			host_arg = optarg; | 
 | 			break; | 
 | 		case 'G': | 
 | 			guest_arg = optarg; | 
 | 			break; | 
 | 		case 'R': | 
 | 			ring_size = strtol(optarg, &endptr, 0); | 
 | 			assert(ring_size && !(ring_size & (ring_size - 1))); | 
 | 			assert(!*endptr); | 
 | 			break; | 
 | 		case 'C': | 
 | 			c = strtol(optarg, &endptr, 0); | 
 | 			assert(!*endptr); | 
 | 			assert(c > 0 && c < INT_MAX); | 
 | 			runcycles = c; | 
 | 			break; | 
 | 		case 'o': | 
 | 			c = strtol(optarg, &endptr, 0); | 
 | 			assert(!*endptr); | 
 | 			assert(c > 0 && c < INT_MAX); | 
 | 			max_outstanding = c; | 
 | 			break; | 
 | 		case 'p': | 
 | 			c = strtol(optarg, &endptr, 0); | 
 | 			assert(!*endptr); | 
 | 			assert(c > 0 && c < INT_MAX); | 
 | 			param = c; | 
 | 			break; | 
 | 		case 'b': | 
 | 			c = strtol(optarg, &endptr, 0); | 
 | 			assert(!*endptr); | 
 | 			assert(c > 0 && c < INT_MAX); | 
 | 			batch = c; | 
 | 			break; | 
 | 		case 's': | 
 | 			do_sleep = true; | 
 | 			break; | 
 | 		case 'x': | 
 | 			do_relax = true; | 
 | 			break; | 
 | 		case 'e': | 
 | 			do_exit = true; | 
 | 			break; | 
 | 		default: | 
 | 			help(); | 
 | 			exit(4); | 
 | 			break; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* does nothing here, used to make sure all smp APIs compile */ | 
 | 	smp_acquire(); | 
 | 	smp_release(); | 
 | 	smp_mb(); | 
 | done: | 
 |  | 
 | 	if (batch > max_outstanding) | 
 | 		batch = max_outstanding; | 
 |  | 
 | 	if (optind < argc) { | 
 | 		help(); | 
 | 		exit(4); | 
 | 	} | 
 | 	alloc_ring(); | 
 |  | 
 | 	ret = pthread_create(&host, NULL, start_host, host_arg); | 
 | 	assert(!ret); | 
 | 	ret = pthread_create(&guest, NULL, start_guest, guest_arg); | 
 | 	assert(!ret); | 
 |  | 
 | 	ret = pthread_join(guest, &tret); | 
 | 	assert(!ret); | 
 | 	ret = pthread_join(host, &tret); | 
 | 	assert(!ret); | 
 | 	return 0; | 
 | } |