// SPDX-License-Identifier: BSD-2-Clause /* * Copyright (c) 2020, Rafael Aquini * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * compile with: gcc -o tlb-test -D_GNU_SOURCE -lpthread tlb-test.c * dependencies: * - _GNU_SOURCE required for asprintf(3), sched_getcpu(3) && sched_setaffinity(2) * - libpthreads required for POSIX semaphores */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef NCHILDS #define NCHILDS 4 #endif #ifndef NPAGES #define NPAGES 32 #endif #ifndef NRUNS #define NRUNS 8192 #endif #ifdef DEBUG #define DPRINTF(...) fprintf(stderr, __VA_ARGS__) #else #define DPRINTF(...) #endif #define ERROR_EXIT(msg) \ do { \ char *estr = NULL; \ asprintf(&estr, "[%s:%d] %s", __FILE__, __LINE__, msg); \ perror(estr); \ exit(EXIT_FAILURE); \ } while (0) static const char *prg_name = "tlb-test"; static long system_hz; static long page_size; static sem_t *sem; /* * Fisher-Yates shuffler algorithm [Statistical Tables (London, 1938), Ex.12], * adapted to computer language by R. Durstenfeld [CACM 7 (1964), 420], and * presented by Donald E. Knuth at: * "The Art of Computer Programming, Volume 2: Seminumerical Algorithms" * [Algorithm P (shuffling) under Section 3.4 OTHER TYPES OF RANDOM QUANTITIES] */ void fy_shuffler(unsigned long *buf, unsigned long len) { unsigned long j, u, tmp; for (j = len - 1; j > 0; j--) { u = rand() % j; tmp = *(buf + u); *(buf + u) = *(buf + j); *(buf + j) = tmp; } } unsigned long usec_diff(struct timeval *a, struct timeval *b) { unsigned long usec; usec = (b->tv_sec - a->tv_sec) * 1000000; usec += b->tv_usec - a->tv_usec; return usec; } unsigned long workload(void *addr, size_t len, unsigned long *fault_order, int child) { struct timeval start, end; unsigned long i; gettimeofday(&start, NULL); for (i = 0; i < len; i++) { unsigned long p = *(fault_order + i); *((unsigned char *)(addr + (p * page_size))) = ((i * p) % 0xff); } gettimeofday(&end, NULL); DPRINTF("[%s: child-%d (CPU=%d PID=%ld)] RUNNING! \n", prg_name, child, sched_getcpu(), (long) getpid()); return usec_diff(&start, &end); } int child(int n, FILE *stream) { unsigned long pages[NPAGES]; size_t map_sz; int i, runs; void *addr; double elapsed = 0; for (i = 0; i < NPAGES; i++) pages[i] = i; map_sz = page_size * NPAGES; addr = mmap(NULL, map_sz, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); if (addr == MAP_FAILED) ERROR_EXIT("mmap"); if (madvise(addr, map_sz, MADV_NOHUGEPAGE) == -1) ERROR_EXIT("madvise"); srand(time(NULL)); for (runs = 0; runs < NRUNS; runs++) { sem_wait(sem); elapsed += workload(addr, NPAGES, pages, n); fy_shuffler(pages, NPAGES); sem_post(sem); /* * relinquish the CPU to provide a small backoff, so other tasks * get a fair chance on aquiring the semaphore. */ sched_yield(); } fprintf(stream, "[%s: child-%d (CPU=%d PID=%ld)] %lf msecs\n", prg_name, n, sched_getcpu(), (long) getpid(), (double )(elapsed / 1000)); return 0; } int main(int argc, char *argv[]) { pid_t pid[NCHILDS]; int i, ret, status; cpu_set_t set; CPU_ZERO(&set); /* clear the set */ CPU_SET(1, &set); if (sched_setaffinity(0, sizeof(cpu_set_t), &set) == -1) ERROR_EXIT("sched_setaffinity"); if ((system_hz = sysconf(_SC_CLK_TCK)) == -1) ERROR_EXIT("sysconf"); if ((page_size = sysconf(_SC_PAGESIZE)) == -1) ERROR_EXIT("sysconf"); sem = sem_open(prg_name, O_CREAT, S_IRUSR | S_IWUSR, 0); if (sem == SEM_FAILED) ERROR_EXIT("sem_open"); for (i = 0; i < NCHILDS; i++) { pid[i] = fork(); switch (pid[i]) { case -1: /* fork() has failed */ ERROR_EXIT("fork"); break; case 0: /* child of a sucessful fork() */ ret = child(i+1, stdout); exit(ret); break; } } sem_post(sem); for (;;) { if (wait(&status) == -1) { if (errno == ECHILD) { goto out; } else { ERROR_EXIT("wait"); } } } out: sem_close(sem); sem_unlink(prg_name); exit(EXIT_SUCCESS); }