// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2003-2004 Silicon Graphics, Inc.
 * All Rights Reserved.
 */
/*
 * Test scaling of multiple processes opening/reading
 * a number of small files simultaneously.
 *	- create <f> files
 *	- fork <n> processes
 *	- wait for all processes ready
 *	- start all proceses at the same time
 *	- each processes opens , read, closes each file
 *	- option to resync each process at each file
 *
 *	test [-c cpus] [-b bytes] [-f files] [-v] [-s] [-S]
 *			OR
 *	test -i [-b bytes] [-f files] 
 */
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>

void do_initfiles(void);
void slave(int);

#define VPRINT(x...)	do { if(verbose) fprintf(x);} while(0)
#define perrorx(s) do {perror(s); exit(1);} while (0)

long bytes=8192;
int cpus=1;
int init=0;
int strided=0;
int files=1;
int blksize=512;
int syncstep=0;
int verbose=0;

typedef struct {
        volatile long   go;
        long            fill[15];
        volatile long   rdy[512];
} share_t;

share_t	*sharep;


int
runon(int cpu)
{
#ifdef sys_sched_setaffinity
	unsigned long mask[8];
	
	if (cpu < 0 || cpu >= 512)
		return -1;
	memset(mask, 0, sizeof(mask));
	mask[cpu/64] |= 1UL<<(cpu&63);

	if (syscall(sys_sched_setaffinity, 0, sizeof(mask), mask))
		return -1;
#endif
	return 0;
}

long
scaled_atol(char *p)
{
	long val;
	char  *pe;

	val = strtol(p, &pe, 0);
	if (*pe == 'K' || *pe == 'k')
		val *= 1024L;
	else if (*pe == 'M' || *pe == 'm')
		val *= 1024L*1024L;
	else if (*pe == 'G' || *pe == 'g')
		val *= 1024L*1024L*1024L;
	else if (*pe == 'p' || *pe == 'P')
		val *= getpagesize();
	return val;
}


int
main(int argc, char** argv) {
        int shmid;
        static  char            optstr[] = "c:b:f:sSivH";
        int                     notdone, stat, i, j, c, er=0;

        opterr=1;
        while ((c = getopt(argc, argv, optstr)) != EOF)
                switch (c) {
                case 'c':
                        cpus = atoi(optarg);
                        break;
                case 'b':
                        bytes = scaled_atol(optarg);
                        break;
                case 'f':
                        files = atoi(optarg);
                        break;
                case 'i':
                        init++;
                        break;
                case 's':
                        syncstep++;
                        break;
                case 'S':
                        strided++;
                        break;
                case 'v':
                        verbose++;
                        break;
                case '?':
                        er = 1;
                        break;
                }
        if (er) {
                printf("usage: %s %s\n", argv[0], optstr);
                exit(1);
        }


	if ((shmid = shmget(IPC_PRIVATE, sizeof (share_t), IPC_CREAT|SHM_R|SHM_W))  == -1)
		perrorx("shmget failed");
	sharep = (share_t*)shmat(shmid, (void*)0, SHM_R|SHM_W);
	memset(sharep, -1, sizeof (share_t));

	if (init) {
		do_initfiles();
		exit(0);
	}
        for (i=0; i<cpus; i++) {
                if (fork() == 0)
                        slave(i);
        }

	for (i=0; i<files; i++) {
		VPRINT(stderr, "%d:", i);
		notdone = cpus;
		do {
			for (j=0; j<cpus; j++) {
				if (sharep->rdy[j] == i) {
					sharep->rdy[j] = -1;
					VPRINT(stderr, " %d", j);
					notdone--;
				}
			}
		} while (notdone);
		VPRINT(stderr, "\n");
		sharep->go = i;
		if (!syncstep)
			break;
	}
	VPRINT(stderr, "\n");

        while (wait(&stat)> 0)
		VPRINT(stderr, ".");
	VPRINT(stderr, "\n");

	exit(0);
}

void 
slave(int id)
{
	int	i, fd, byte;
	char	*buf, filename[32];

	runon (id+1);
	buf = malloc(blksize);
	bzero(buf, blksize);
	for (i=0; i<files; i++) {
		if (!i || syncstep) {
			sharep->rdy[id] = i;
			while(sharep->go != i);
		}
		sprintf(filename, "/tmp/tst.%d", (strided ? ((i + id) % files) : i));
		if ((fd = open (filename, O_RDONLY)) < 0) {
			perrorx(filename);
		}
	
		for (byte=0; byte<bytes; byte+=blksize) {
			if (read (fd, buf, blksize) != blksize)
				perrorx("read of file failed");
		}
		close(fd);
	}
	exit(0);
}

void
do_initfiles(void)
{
	int	i, fd, byte;
	char	*buf, filename[32];

	buf = malloc(blksize);
	bzero(buf, blksize);

	for (i=0; i<files; i++) {
		sprintf(filename, "/tmp/tst.%d", i);
		unlink(filename);
		if ((fd = open (filename, O_RDWR|O_CREAT, 0644)) < 0)
			perrorx(filename);
	
		for (byte=0; byte<bytes; byte+=blksize) {
			if (write (fd, buf, blksize) != blksize)
				perrorx("write of file failed");
		}
		close(fd);
	}
	sync();
}


