mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	The unsigned int should use "%u" instead of "%d". Signed-off-by: Zhu Jun <zhujun2@cmss.chinamobile.com> Message-Id: <20240724074108.9530-1-zhujun2@cmss.chinamobile.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Reviewed-by: Eugenio Pérez <eperezma@redhat.com> Reviewed-by: Xuan Zhuo <xuanzhuo@linux.alibaba.com>
		
			
				
	
	
		
			391 lines
		
	
	
	
		
			6.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			391 lines
		
	
	
	
		
			6.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0-only
 | 
						|
/*
 | 
						|
 * Copyright (C) 2016 Red Hat, Inc.
 | 
						|
 * Author: Michael S. Tsirkin <mst@redhat.com>
 | 
						|
 *
 | 
						|
 * 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: %u)]"
 | 
						|
		" [--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;
 | 
						|
}
 |