162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/*
462306a36Sopenharmony_ci * Copyright 2019, Nick Piggin, Gautham R. Shenoy, Aneesh Kumar K.V, IBM Corp.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci/*
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Test tlbie/mtpidr race. We have 4 threads doing flush/load/compare/store
1062306a36Sopenharmony_ci * sequence in a loop. The same threads also rung a context switch task
1162306a36Sopenharmony_ci * that does sched_yield() in loop.
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * The snapshot thread mark the mmap area PROT_READ in between, make a copy
1462306a36Sopenharmony_ci * and copy it back to the original area. This helps us to detect if any
1562306a36Sopenharmony_ci * store continued to happen after we marked the memory PROT_READ.
1662306a36Sopenharmony_ci */
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define _GNU_SOURCE
1962306a36Sopenharmony_ci#include <stdio.h>
2062306a36Sopenharmony_ci#include <sys/mman.h>
2162306a36Sopenharmony_ci#include <sys/types.h>
2262306a36Sopenharmony_ci#include <sys/wait.h>
2362306a36Sopenharmony_ci#include <sys/ipc.h>
2462306a36Sopenharmony_ci#include <sys/shm.h>
2562306a36Sopenharmony_ci#include <sys/stat.h>
2662306a36Sopenharmony_ci#include <sys/time.h>
2762306a36Sopenharmony_ci#include <linux/futex.h>
2862306a36Sopenharmony_ci#include <unistd.h>
2962306a36Sopenharmony_ci#include <asm/unistd.h>
3062306a36Sopenharmony_ci#include <string.h>
3162306a36Sopenharmony_ci#include <stdlib.h>
3262306a36Sopenharmony_ci#include <fcntl.h>
3362306a36Sopenharmony_ci#include <sched.h>
3462306a36Sopenharmony_ci#include <time.h>
3562306a36Sopenharmony_ci#include <stdarg.h>
3662306a36Sopenharmony_ci#include <pthread.h>
3762306a36Sopenharmony_ci#include <signal.h>
3862306a36Sopenharmony_ci#include <sys/prctl.h>
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic inline void dcbf(volatile unsigned int *addr)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	__asm__ __volatile__ ("dcbf %y0; sync" : : "Z"(*(unsigned char *)addr) : "memory");
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic void err_msg(char *msg)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	time_t now;
4962306a36Sopenharmony_ci	time(&now);
5062306a36Sopenharmony_ci	printf("=================================\n");
5162306a36Sopenharmony_ci	printf("    Error: %s\n", msg);
5262306a36Sopenharmony_ci	printf("    %s", ctime(&now));
5362306a36Sopenharmony_ci	printf("=================================\n");
5462306a36Sopenharmony_ci	exit(1);
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic char *map1;
5862306a36Sopenharmony_cistatic char *map2;
5962306a36Sopenharmony_cistatic pid_t rim_process_pid;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/*
6262306a36Sopenharmony_ci * A "rim-sequence" is defined to be the sequence of the following
6362306a36Sopenharmony_ci * operations performed on a memory word:
6462306a36Sopenharmony_ci *	1) FLUSH the contents of that word.
6562306a36Sopenharmony_ci *	2) LOAD the contents of that word.
6662306a36Sopenharmony_ci *	3) COMPARE the contents of that word with the content that was
6762306a36Sopenharmony_ci *	           previously stored at that word
6862306a36Sopenharmony_ci *	4) STORE new content into that word.
6962306a36Sopenharmony_ci *
7062306a36Sopenharmony_ci * The threads in this test that perform the rim-sequence are termed
7162306a36Sopenharmony_ci * as rim_threads.
7262306a36Sopenharmony_ci */
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/*
7562306a36Sopenharmony_ci * A "corruption" is defined to be the failed COMPARE operation in a
7662306a36Sopenharmony_ci * rim-sequence.
7762306a36Sopenharmony_ci *
7862306a36Sopenharmony_ci * A rim_thread that detects a corruption informs about it to all the
7962306a36Sopenharmony_ci * other rim_threads, and the mem_snapshot thread.
8062306a36Sopenharmony_ci */
8162306a36Sopenharmony_cistatic volatile unsigned int corruption_found;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/*
8462306a36Sopenharmony_ci * This defines the maximum number of rim_threads in this test.
8562306a36Sopenharmony_ci *
8662306a36Sopenharmony_ci * The THREAD_ID_BITS denote the number of bits required
8762306a36Sopenharmony_ci * to represent the thread_ids [0..MAX_THREADS - 1].
8862306a36Sopenharmony_ci * We are being a bit paranoid here and set it to 8 bits,
8962306a36Sopenharmony_ci * though 6 bits suffice.
9062306a36Sopenharmony_ci *
9162306a36Sopenharmony_ci */
9262306a36Sopenharmony_ci#define MAX_THREADS 		64
9362306a36Sopenharmony_ci#define THREAD_ID_BITS		8
9462306a36Sopenharmony_ci#define THREAD_ID_MASK		((1 << THREAD_ID_BITS) - 1)
9562306a36Sopenharmony_cistatic unsigned int rim_thread_ids[MAX_THREADS];
9662306a36Sopenharmony_cistatic pthread_t rim_threads[MAX_THREADS];
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci/*
10062306a36Sopenharmony_ci * Each rim_thread works on an exclusive "chunk" of size
10162306a36Sopenharmony_ci * RIM_CHUNK_SIZE.
10262306a36Sopenharmony_ci *
10362306a36Sopenharmony_ci * The ith rim_thread works on the ith chunk.
10462306a36Sopenharmony_ci *
10562306a36Sopenharmony_ci * The ith chunk begins at
10662306a36Sopenharmony_ci * map1 + (i * RIM_CHUNK_SIZE)
10762306a36Sopenharmony_ci */
10862306a36Sopenharmony_ci#define RIM_CHUNK_SIZE  	1024
10962306a36Sopenharmony_ci#define BITS_PER_BYTE 		8
11062306a36Sopenharmony_ci#define WORD_SIZE     		(sizeof(unsigned int))
11162306a36Sopenharmony_ci#define WORD_BITS		(WORD_SIZE * BITS_PER_BYTE)
11262306a36Sopenharmony_ci#define WORDS_PER_CHUNK		(RIM_CHUNK_SIZE/WORD_SIZE)
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic inline char *compute_chunk_start_addr(unsigned int thread_id)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	char *chunk_start;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	chunk_start = (char *)((unsigned long)map1 +
11962306a36Sopenharmony_ci			       (thread_id * RIM_CHUNK_SIZE));
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	return chunk_start;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/*
12562306a36Sopenharmony_ci * The "word-offset" of a word-aligned address inside a chunk, is
12662306a36Sopenharmony_ci * defined to be the number of words that precede the address in that
12762306a36Sopenharmony_ci * chunk.
12862306a36Sopenharmony_ci *
12962306a36Sopenharmony_ci * WORD_OFFSET_BITS denote the number of bits required to represent
13062306a36Sopenharmony_ci * the word-offsets of all the word-aligned addresses of a chunk.
13162306a36Sopenharmony_ci */
13262306a36Sopenharmony_ci#define WORD_OFFSET_BITS	(__builtin_ctz(WORDS_PER_CHUNK))
13362306a36Sopenharmony_ci#define WORD_OFFSET_MASK	((1 << WORD_OFFSET_BITS) - 1)
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic inline unsigned int compute_word_offset(char *start, unsigned int *addr)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	unsigned int delta_bytes, ret;
13862306a36Sopenharmony_ci	delta_bytes = (unsigned long)addr - (unsigned long)start;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	ret = delta_bytes/WORD_SIZE;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	return ret;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci/*
14662306a36Sopenharmony_ci * A "sweep" is defined to be the sequential execution of the
14762306a36Sopenharmony_ci * rim-sequence by a rim_thread on its chunk one word at a time,
14862306a36Sopenharmony_ci * starting from the first word of its chunk and ending with the last
14962306a36Sopenharmony_ci * word of its chunk.
15062306a36Sopenharmony_ci *
15162306a36Sopenharmony_ci * Each sweep of a rim_thread is uniquely identified by a sweep_id.
15262306a36Sopenharmony_ci * SWEEP_ID_BITS denote the number of bits required to represent
15362306a36Sopenharmony_ci * the sweep_ids of rim_threads.
15462306a36Sopenharmony_ci *
15562306a36Sopenharmony_ci * As to why SWEEP_ID_BITS are computed as a function of THREAD_ID_BITS,
15662306a36Sopenharmony_ci * WORD_OFFSET_BITS, and WORD_BITS, see the "store-pattern" below.
15762306a36Sopenharmony_ci */
15862306a36Sopenharmony_ci#define SWEEP_ID_BITS		(WORD_BITS - (THREAD_ID_BITS + WORD_OFFSET_BITS))
15962306a36Sopenharmony_ci#define SWEEP_ID_MASK		((1 << SWEEP_ID_BITS) - 1)
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci/*
16262306a36Sopenharmony_ci * A "store-pattern" is the word-pattern that is stored into a word
16362306a36Sopenharmony_ci * location in the 4)STORE step of the rim-sequence.
16462306a36Sopenharmony_ci *
16562306a36Sopenharmony_ci * In the store-pattern, we shall encode:
16662306a36Sopenharmony_ci *
16762306a36Sopenharmony_ci *      - The thread-id of the rim_thread performing the store
16862306a36Sopenharmony_ci *        (The most significant THREAD_ID_BITS)
16962306a36Sopenharmony_ci *
17062306a36Sopenharmony_ci *      - The word-offset of the address into which the store is being
17162306a36Sopenharmony_ci *        performed (The next WORD_OFFSET_BITS)
17262306a36Sopenharmony_ci *
17362306a36Sopenharmony_ci *      - The sweep_id of the current sweep in which the store is
17462306a36Sopenharmony_ci *        being performed. (The lower SWEEP_ID_BITS)
17562306a36Sopenharmony_ci *
17662306a36Sopenharmony_ci * Store Pattern: 32 bits
17762306a36Sopenharmony_ci * |------------------|--------------------|---------------------------------|
17862306a36Sopenharmony_ci * |    Thread id     |  Word offset       |         sweep_id                |
17962306a36Sopenharmony_ci * |------------------|--------------------|---------------------------------|
18062306a36Sopenharmony_ci *    THREAD_ID_BITS     WORD_OFFSET_BITS          SWEEP_ID_BITS
18162306a36Sopenharmony_ci *
18262306a36Sopenharmony_ci * In the store pattern, the (Thread-id + Word-offset) uniquely identify the
18362306a36Sopenharmony_ci * address to which the store is being performed i.e,
18462306a36Sopenharmony_ci *    address == map1 +
18562306a36Sopenharmony_ci *              (Thread-id * RIM_CHUNK_SIZE) + (Word-offset * WORD_SIZE)
18662306a36Sopenharmony_ci *
18762306a36Sopenharmony_ci * And the sweep_id in the store pattern identifies the time when the
18862306a36Sopenharmony_ci * store was performed by the rim_thread.
18962306a36Sopenharmony_ci *
19062306a36Sopenharmony_ci * We shall use this property in the 3)COMPARE step of the
19162306a36Sopenharmony_ci * rim-sequence.
19262306a36Sopenharmony_ci */
19362306a36Sopenharmony_ci#define SWEEP_ID_SHIFT	0
19462306a36Sopenharmony_ci#define WORD_OFFSET_SHIFT	(SWEEP_ID_BITS)
19562306a36Sopenharmony_ci#define THREAD_ID_SHIFT		(WORD_OFFSET_BITS + SWEEP_ID_BITS)
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci/*
19862306a36Sopenharmony_ci * Compute the store pattern for a given thread with id @tid, at
19962306a36Sopenharmony_ci * location @addr in the sweep identified by @sweep_id
20062306a36Sopenharmony_ci */
20162306a36Sopenharmony_cistatic inline unsigned int compute_store_pattern(unsigned int tid,
20262306a36Sopenharmony_ci						 unsigned int *addr,
20362306a36Sopenharmony_ci						 unsigned int sweep_id)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	unsigned int ret = 0;
20662306a36Sopenharmony_ci	char *start = compute_chunk_start_addr(tid);
20762306a36Sopenharmony_ci	unsigned int word_offset = compute_word_offset(start, addr);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	ret += (tid & THREAD_ID_MASK) << THREAD_ID_SHIFT;
21062306a36Sopenharmony_ci	ret += (word_offset & WORD_OFFSET_MASK) << WORD_OFFSET_SHIFT;
21162306a36Sopenharmony_ci	ret += (sweep_id & SWEEP_ID_MASK) << SWEEP_ID_SHIFT;
21262306a36Sopenharmony_ci	return ret;
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci/* Extract the thread-id from the given store-pattern */
21662306a36Sopenharmony_cistatic inline unsigned int extract_tid(unsigned int pattern)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	unsigned int ret;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	ret = (pattern >> THREAD_ID_SHIFT) & THREAD_ID_MASK;
22162306a36Sopenharmony_ci	return ret;
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci/* Extract the word-offset from the given store-pattern */
22562306a36Sopenharmony_cistatic inline unsigned int extract_word_offset(unsigned int pattern)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	unsigned int ret;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	ret = (pattern >> WORD_OFFSET_SHIFT) & WORD_OFFSET_MASK;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	return ret;
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci/* Extract the sweep-id from the given store-pattern */
23562306a36Sopenharmony_cistatic inline unsigned int extract_sweep_id(unsigned int pattern)
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	unsigned int ret;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	ret = (pattern >> SWEEP_ID_SHIFT) & SWEEP_ID_MASK;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	return ret;
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci/************************************************************
24662306a36Sopenharmony_ci *                                                          *
24762306a36Sopenharmony_ci *          Logging the output of the verification          *
24862306a36Sopenharmony_ci *                                                          *
24962306a36Sopenharmony_ci ************************************************************/
25062306a36Sopenharmony_ci#define LOGDIR_NAME_SIZE 100
25162306a36Sopenharmony_cistatic char logdir[LOGDIR_NAME_SIZE];
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cistatic FILE *fp[MAX_THREADS];
25462306a36Sopenharmony_cistatic const char logfilename[] ="Thread-%02d-Chunk";
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic inline void start_verification_log(unsigned int tid,
25762306a36Sopenharmony_ci					  unsigned int *addr,
25862306a36Sopenharmony_ci					  unsigned int cur_sweep_id,
25962306a36Sopenharmony_ci					  unsigned int prev_sweep_id)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	FILE *f;
26262306a36Sopenharmony_ci	char logfile[30];
26362306a36Sopenharmony_ci	char path[LOGDIR_NAME_SIZE + 30];
26462306a36Sopenharmony_ci	char separator[2] = "/";
26562306a36Sopenharmony_ci	char *chunk_start = compute_chunk_start_addr(tid);
26662306a36Sopenharmony_ci	unsigned int size = RIM_CHUNK_SIZE;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	sprintf(logfile, logfilename, tid);
26962306a36Sopenharmony_ci	strcpy(path, logdir);
27062306a36Sopenharmony_ci	strcat(path, separator);
27162306a36Sopenharmony_ci	strcat(path, logfile);
27262306a36Sopenharmony_ci	f = fopen(path, "w");
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	if (!f) {
27562306a36Sopenharmony_ci		err_msg("Unable to create logfile\n");
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	fp[tid] = f;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	fprintf(f, "----------------------------------------------------------\n");
28162306a36Sopenharmony_ci	fprintf(f, "PID                = %d\n", rim_process_pid);
28262306a36Sopenharmony_ci	fprintf(f, "Thread id          = %02d\n", tid);
28362306a36Sopenharmony_ci	fprintf(f, "Chunk Start Addr   = 0x%016lx\n", (unsigned long)chunk_start);
28462306a36Sopenharmony_ci	fprintf(f, "Chunk Size         = %d\n", size);
28562306a36Sopenharmony_ci	fprintf(f, "Next Store Addr    = 0x%016lx\n", (unsigned long)addr);
28662306a36Sopenharmony_ci	fprintf(f, "Current sweep-id   = 0x%08x\n", cur_sweep_id);
28762306a36Sopenharmony_ci	fprintf(f, "Previous sweep-id  = 0x%08x\n", prev_sweep_id);
28862306a36Sopenharmony_ci	fprintf(f, "----------------------------------------------------------\n");
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cistatic inline void log_anamoly(unsigned int tid, unsigned int *addr,
29262306a36Sopenharmony_ci			       unsigned int expected, unsigned int observed)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	FILE *f = fp[tid];
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	fprintf(f, "Thread %02d: Addr 0x%lx: Expected 0x%x, Observed 0x%x\n",
29762306a36Sopenharmony_ci	        tid, (unsigned long)addr, expected, observed);
29862306a36Sopenharmony_ci	fprintf(f, "Thread %02d: Expected Thread id   = %02d\n", tid, extract_tid(expected));
29962306a36Sopenharmony_ci	fprintf(f, "Thread %02d: Observed Thread id   = %02d\n", tid, extract_tid(observed));
30062306a36Sopenharmony_ci	fprintf(f, "Thread %02d: Expected Word offset = %03d\n", tid, extract_word_offset(expected));
30162306a36Sopenharmony_ci	fprintf(f, "Thread %02d: Observed Word offset = %03d\n", tid, extract_word_offset(observed));
30262306a36Sopenharmony_ci	fprintf(f, "Thread %02d: Expected sweep-id    = 0x%x\n", tid, extract_sweep_id(expected));
30362306a36Sopenharmony_ci	fprintf(f, "Thread %02d: Observed sweep-id    = 0x%x\n", tid, extract_sweep_id(observed));
30462306a36Sopenharmony_ci	fprintf(f, "----------------------------------------------------------\n");
30562306a36Sopenharmony_ci}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cistatic inline void end_verification_log(unsigned int tid, unsigned nr_anamolies)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	FILE *f = fp[tid];
31062306a36Sopenharmony_ci	char logfile[30];
31162306a36Sopenharmony_ci	char path[LOGDIR_NAME_SIZE + 30];
31262306a36Sopenharmony_ci	char separator[] = "/";
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	fclose(f);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	if (nr_anamolies == 0) {
31762306a36Sopenharmony_ci		remove(path);
31862306a36Sopenharmony_ci		return;
31962306a36Sopenharmony_ci	}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	sprintf(logfile, logfilename, tid);
32262306a36Sopenharmony_ci	strcpy(path, logdir);
32362306a36Sopenharmony_ci	strcat(path, separator);
32462306a36Sopenharmony_ci	strcat(path, logfile);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	printf("Thread %02d chunk has %d corrupted words. For details check %s\n",
32762306a36Sopenharmony_ci		tid, nr_anamolies, path);
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci/*
33162306a36Sopenharmony_ci * When a COMPARE step of a rim-sequence fails, the rim_thread informs
33262306a36Sopenharmony_ci * everyone else via the shared_memory pointed to by
33362306a36Sopenharmony_ci * corruption_found variable. On seeing this, every thread verifies the
33462306a36Sopenharmony_ci * content of its chunk as follows.
33562306a36Sopenharmony_ci *
33662306a36Sopenharmony_ci * Suppose a thread identified with @tid was about to store (but not
33762306a36Sopenharmony_ci * yet stored) to @next_store_addr in its current sweep identified
33862306a36Sopenharmony_ci * @cur_sweep_id. Let @prev_sweep_id indicate the previous sweep_id.
33962306a36Sopenharmony_ci *
34062306a36Sopenharmony_ci * This implies that for all the addresses @addr < @next_store_addr,
34162306a36Sopenharmony_ci * Thread @tid has already performed a store as part of its current
34262306a36Sopenharmony_ci * sweep. Hence we expect the content of such @addr to be:
34362306a36Sopenharmony_ci *    |-------------------------------------------------|
34462306a36Sopenharmony_ci *    | tid   | word_offset(addr) |    cur_sweep_id     |
34562306a36Sopenharmony_ci *    |-------------------------------------------------|
34662306a36Sopenharmony_ci *
34762306a36Sopenharmony_ci * Since Thread @tid is yet to perform stores on address
34862306a36Sopenharmony_ci * @next_store_addr and above, we expect the content of such an
34962306a36Sopenharmony_ci * address @addr to be:
35062306a36Sopenharmony_ci *    |-------------------------------------------------|
35162306a36Sopenharmony_ci *    | tid   | word_offset(addr) |    prev_sweep_id    |
35262306a36Sopenharmony_ci *    |-------------------------------------------------|
35362306a36Sopenharmony_ci *
35462306a36Sopenharmony_ci * The verifier function @verify_chunk does this verification and logs
35562306a36Sopenharmony_ci * any anamolies that it finds.
35662306a36Sopenharmony_ci */
35762306a36Sopenharmony_cistatic void verify_chunk(unsigned int tid, unsigned int *next_store_addr,
35862306a36Sopenharmony_ci		  unsigned int cur_sweep_id,
35962306a36Sopenharmony_ci		  unsigned int prev_sweep_id)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	unsigned int *iter_ptr;
36262306a36Sopenharmony_ci	unsigned int size = RIM_CHUNK_SIZE;
36362306a36Sopenharmony_ci	unsigned int expected;
36462306a36Sopenharmony_ci	unsigned int observed;
36562306a36Sopenharmony_ci	char *chunk_start = compute_chunk_start_addr(tid);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	int nr_anamolies = 0;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	start_verification_log(tid, next_store_addr,
37062306a36Sopenharmony_ci			       cur_sweep_id, prev_sweep_id);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	for (iter_ptr = (unsigned int *)chunk_start;
37362306a36Sopenharmony_ci	     (unsigned long)iter_ptr < (unsigned long)chunk_start + size;
37462306a36Sopenharmony_ci	     iter_ptr++) {
37562306a36Sopenharmony_ci		unsigned int expected_sweep_id;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci		if (iter_ptr < next_store_addr) {
37862306a36Sopenharmony_ci			expected_sweep_id = cur_sweep_id;
37962306a36Sopenharmony_ci		} else {
38062306a36Sopenharmony_ci			expected_sweep_id = prev_sweep_id;
38162306a36Sopenharmony_ci		}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci		expected = compute_store_pattern(tid, iter_ptr, expected_sweep_id);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci		dcbf((volatile unsigned int*)iter_ptr); //Flush before reading
38662306a36Sopenharmony_ci		observed = *iter_ptr;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	        if (observed != expected) {
38962306a36Sopenharmony_ci			nr_anamolies++;
39062306a36Sopenharmony_ci			log_anamoly(tid, iter_ptr, expected, observed);
39162306a36Sopenharmony_ci		}
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	end_verification_log(tid, nr_anamolies);
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic void set_pthread_cpu(pthread_t th, int cpu)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	cpu_set_t run_cpu_mask;
40062306a36Sopenharmony_ci	struct sched_param param;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	CPU_ZERO(&run_cpu_mask);
40362306a36Sopenharmony_ci	CPU_SET(cpu, &run_cpu_mask);
40462306a36Sopenharmony_ci	pthread_setaffinity_np(th, sizeof(cpu_set_t), &run_cpu_mask);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	param.sched_priority = 1;
40762306a36Sopenharmony_ci	if (0 && sched_setscheduler(0, SCHED_FIFO, &param) == -1) {
40862306a36Sopenharmony_ci		/* haven't reproduced with this setting, it kills random preemption which may be a factor */
40962306a36Sopenharmony_ci		fprintf(stderr, "could not set SCHED_FIFO, run as root?\n");
41062306a36Sopenharmony_ci	}
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic void set_mycpu(int cpu)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	cpu_set_t run_cpu_mask;
41662306a36Sopenharmony_ci	struct sched_param param;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	CPU_ZERO(&run_cpu_mask);
41962306a36Sopenharmony_ci	CPU_SET(cpu, &run_cpu_mask);
42062306a36Sopenharmony_ci	sched_setaffinity(0, sizeof(cpu_set_t), &run_cpu_mask);
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	param.sched_priority = 1;
42362306a36Sopenharmony_ci	if (0 && sched_setscheduler(0, SCHED_FIFO, &param) == -1) {
42462306a36Sopenharmony_ci		fprintf(stderr, "could not set SCHED_FIFO, run as root?\n");
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_cistatic volatile int segv_wait;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_cistatic void segv_handler(int signo, siginfo_t *info, void *extra)
43162306a36Sopenharmony_ci{
43262306a36Sopenharmony_ci	while (segv_wait) {
43362306a36Sopenharmony_ci		sched_yield();
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_cistatic void set_segv_handler(void)
43962306a36Sopenharmony_ci{
44062306a36Sopenharmony_ci	struct sigaction sa;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	sa.sa_flags = SA_SIGINFO;
44362306a36Sopenharmony_ci	sa.sa_sigaction = segv_handler;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	if (sigaction(SIGSEGV, &sa, NULL) == -1) {
44662306a36Sopenharmony_ci		perror("sigaction");
44762306a36Sopenharmony_ci		exit(EXIT_FAILURE);
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ciint timeout = 0;
45262306a36Sopenharmony_ci/*
45362306a36Sopenharmony_ci * This function is executed by every rim_thread.
45462306a36Sopenharmony_ci *
45562306a36Sopenharmony_ci * This function performs sweeps over the exclusive chunks of the
45662306a36Sopenharmony_ci * rim_threads executing the rim-sequence one word at a time.
45762306a36Sopenharmony_ci */
45862306a36Sopenharmony_cistatic void *rim_fn(void *arg)
45962306a36Sopenharmony_ci{
46062306a36Sopenharmony_ci	unsigned int tid = *((unsigned int *)arg);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	int size = RIM_CHUNK_SIZE;
46362306a36Sopenharmony_ci	char *chunk_start = compute_chunk_start_addr(tid);
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	unsigned int prev_sweep_id;
46662306a36Sopenharmony_ci	unsigned int cur_sweep_id = 0;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	/* word access */
46962306a36Sopenharmony_ci	unsigned int pattern = cur_sweep_id;
47062306a36Sopenharmony_ci	unsigned int *pattern_ptr = &pattern;
47162306a36Sopenharmony_ci	unsigned int *w_ptr, read_data;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	set_segv_handler();
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	/*
47662306a36Sopenharmony_ci	 * Let us initialize the chunk:
47762306a36Sopenharmony_ci	 *
47862306a36Sopenharmony_ci	 * Each word-aligned address addr in the chunk,
47962306a36Sopenharmony_ci	 * is initialized to :
48062306a36Sopenharmony_ci	 *    |-------------------------------------------------|
48162306a36Sopenharmony_ci	 *    | tid   | word_offset(addr) |         0           |
48262306a36Sopenharmony_ci	 *    |-------------------------------------------------|
48362306a36Sopenharmony_ci	 */
48462306a36Sopenharmony_ci	for (w_ptr = (unsigned int *)chunk_start;
48562306a36Sopenharmony_ci	     (unsigned long)w_ptr < (unsigned long)(chunk_start) + size;
48662306a36Sopenharmony_ci	     w_ptr++) {
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci		*pattern_ptr = compute_store_pattern(tid, w_ptr, cur_sweep_id);
48962306a36Sopenharmony_ci		*w_ptr = *pattern_ptr;
49062306a36Sopenharmony_ci	}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	while (!corruption_found && !timeout) {
49362306a36Sopenharmony_ci		prev_sweep_id = cur_sweep_id;
49462306a36Sopenharmony_ci		cur_sweep_id = cur_sweep_id + 1;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci		for (w_ptr = (unsigned int *)chunk_start;
49762306a36Sopenharmony_ci		     (unsigned long)w_ptr < (unsigned long)(chunk_start) + size;
49862306a36Sopenharmony_ci		     w_ptr++)  {
49962306a36Sopenharmony_ci			unsigned int old_pattern;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci			/*
50262306a36Sopenharmony_ci			 * Compute the pattern that we would have
50362306a36Sopenharmony_ci			 * stored at this location in the previous
50462306a36Sopenharmony_ci			 * sweep.
50562306a36Sopenharmony_ci			 */
50662306a36Sopenharmony_ci			old_pattern = compute_store_pattern(tid, w_ptr, prev_sweep_id);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci			/*
50962306a36Sopenharmony_ci			 * FLUSH:Ensure that we flush the contents of
51062306a36Sopenharmony_ci			 *       the cache before loading
51162306a36Sopenharmony_ci			 */
51262306a36Sopenharmony_ci			dcbf((volatile unsigned int*)w_ptr); //Flush
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci			/* LOAD: Read the value */
51562306a36Sopenharmony_ci			read_data = *w_ptr; //Load
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci			/*
51862306a36Sopenharmony_ci			 * COMPARE: Is it the same as what we had stored
51962306a36Sopenharmony_ci			 *          in the previous sweep ? It better be!
52062306a36Sopenharmony_ci			 */
52162306a36Sopenharmony_ci			if (read_data != old_pattern) {
52262306a36Sopenharmony_ci				/* No it isn't! Tell everyone */
52362306a36Sopenharmony_ci				corruption_found = 1;
52462306a36Sopenharmony_ci			}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci			/*
52762306a36Sopenharmony_ci			 * Before performing a store, let us check if
52862306a36Sopenharmony_ci			 * any rim_thread has found a corruption.
52962306a36Sopenharmony_ci			 */
53062306a36Sopenharmony_ci			if (corruption_found || timeout) {
53162306a36Sopenharmony_ci				/*
53262306a36Sopenharmony_ci				 * Yes. Someone (including us!) has found
53362306a36Sopenharmony_ci				 * a corruption :(
53462306a36Sopenharmony_ci				 *
53562306a36Sopenharmony_ci				 * Let us verify that our chunk is
53662306a36Sopenharmony_ci				 * correct.
53762306a36Sopenharmony_ci				 */
53862306a36Sopenharmony_ci				/* But first, let us allow the dust to settle down! */
53962306a36Sopenharmony_ci				verify_chunk(tid, w_ptr, cur_sweep_id, prev_sweep_id);
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci				return 0;
54262306a36Sopenharmony_ci			}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci			/*
54562306a36Sopenharmony_ci			 * Compute the new pattern that we are going
54662306a36Sopenharmony_ci			 * to write to this location
54762306a36Sopenharmony_ci			 */
54862306a36Sopenharmony_ci			*pattern_ptr = compute_store_pattern(tid, w_ptr, cur_sweep_id);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci			/*
55162306a36Sopenharmony_ci			 * STORE: Now let us write this pattern into
55262306a36Sopenharmony_ci			 *        the location
55362306a36Sopenharmony_ci			 */
55462306a36Sopenharmony_ci			*w_ptr = *pattern_ptr;
55562306a36Sopenharmony_ci		}
55662306a36Sopenharmony_ci	}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	return NULL;
55962306a36Sopenharmony_ci}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic unsigned long start_cpu = 0;
56362306a36Sopenharmony_cistatic unsigned long nrthreads = 4;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_cistatic pthread_t mem_snapshot_thread;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_cistatic void *mem_snapshot_fn(void *arg)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	int page_size = getpagesize();
57062306a36Sopenharmony_ci	size_t size = page_size;
57162306a36Sopenharmony_ci	void *tmp = malloc(size);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	while (!corruption_found && !timeout) {
57462306a36Sopenharmony_ci		/* Stop memory migration once corruption is found */
57562306a36Sopenharmony_ci		segv_wait = 1;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci		mprotect(map1, size, PROT_READ);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci		/*
58062306a36Sopenharmony_ci		 * Load from the working alias (map1). Loading from map2
58162306a36Sopenharmony_ci		 * also fails.
58262306a36Sopenharmony_ci		 */
58362306a36Sopenharmony_ci		memcpy(tmp, map1, size);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci		/*
58662306a36Sopenharmony_ci		 * Stores must go via map2 which has write permissions, but
58762306a36Sopenharmony_ci		 * the corrupted data tends to be seen in the snapshot buffer,
58862306a36Sopenharmony_ci		 * so corruption does not appear to be introduced at the
58962306a36Sopenharmony_ci		 * copy-back via map2 alias here.
59062306a36Sopenharmony_ci		 */
59162306a36Sopenharmony_ci		memcpy(map2, tmp, size);
59262306a36Sopenharmony_ci		/*
59362306a36Sopenharmony_ci		 * Before releasing other threads, must ensure the copy
59462306a36Sopenharmony_ci		 * back to
59562306a36Sopenharmony_ci		 */
59662306a36Sopenharmony_ci		asm volatile("sync" ::: "memory");
59762306a36Sopenharmony_ci		mprotect(map1, size, PROT_READ|PROT_WRITE);
59862306a36Sopenharmony_ci		asm volatile("sync" ::: "memory");
59962306a36Sopenharmony_ci		segv_wait = 0;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci		usleep(1); /* This value makes a big difference */
60262306a36Sopenharmony_ci	}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	return 0;
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_civoid alrm_sighandler(int sig)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	timeout = 1;
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ciint main(int argc, char *argv[])
61362306a36Sopenharmony_ci{
61462306a36Sopenharmony_ci	int c;
61562306a36Sopenharmony_ci	int page_size = getpagesize();
61662306a36Sopenharmony_ci	time_t now;
61762306a36Sopenharmony_ci	int i, dir_error;
61862306a36Sopenharmony_ci	pthread_attr_t attr;
61962306a36Sopenharmony_ci	key_t shm_key = (key_t) getpid();
62062306a36Sopenharmony_ci	int shmid, run_time = 20 * 60;
62162306a36Sopenharmony_ci	struct sigaction sa_alrm;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	snprintf(logdir, LOGDIR_NAME_SIZE,
62462306a36Sopenharmony_ci		 "/tmp/logdir-%u", (unsigned int)getpid());
62562306a36Sopenharmony_ci	while ((c = getopt(argc, argv, "r:hn:l:t:")) != -1) {
62662306a36Sopenharmony_ci		switch(c) {
62762306a36Sopenharmony_ci		case 'r':
62862306a36Sopenharmony_ci			start_cpu = strtoul(optarg, NULL, 10);
62962306a36Sopenharmony_ci			break;
63062306a36Sopenharmony_ci		case 'h':
63162306a36Sopenharmony_ci			printf("%s [-r <start_cpu>] [-n <nrthreads>] [-l <logdir>] [-t <timeout>]\n", argv[0]);
63262306a36Sopenharmony_ci			exit(0);
63362306a36Sopenharmony_ci			break;
63462306a36Sopenharmony_ci		case 'n':
63562306a36Sopenharmony_ci			nrthreads = strtoul(optarg, NULL, 10);
63662306a36Sopenharmony_ci			break;
63762306a36Sopenharmony_ci		case 'l':
63862306a36Sopenharmony_ci			strncpy(logdir, optarg, LOGDIR_NAME_SIZE - 1);
63962306a36Sopenharmony_ci			break;
64062306a36Sopenharmony_ci		case 't':
64162306a36Sopenharmony_ci			run_time = strtoul(optarg, NULL, 10);
64262306a36Sopenharmony_ci			break;
64362306a36Sopenharmony_ci		default:
64462306a36Sopenharmony_ci			printf("invalid option\n");
64562306a36Sopenharmony_ci			exit(0);
64662306a36Sopenharmony_ci			break;
64762306a36Sopenharmony_ci		}
64862306a36Sopenharmony_ci	}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	if (nrthreads > MAX_THREADS)
65162306a36Sopenharmony_ci		nrthreads = MAX_THREADS;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	shmid = shmget(shm_key, page_size, IPC_CREAT|0666);
65462306a36Sopenharmony_ci	if (shmid < 0) {
65562306a36Sopenharmony_ci		err_msg("Failed shmget\n");
65662306a36Sopenharmony_ci	}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	map1 = shmat(shmid, NULL, 0);
65962306a36Sopenharmony_ci	if (map1 == (void *) -1) {
66062306a36Sopenharmony_ci		err_msg("Failed shmat");
66162306a36Sopenharmony_ci	}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	map2 = shmat(shmid, NULL, 0);
66462306a36Sopenharmony_ci	if (map2 == (void *) -1) {
66562306a36Sopenharmony_ci		err_msg("Failed shmat");
66662306a36Sopenharmony_ci	}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	dir_error = mkdir(logdir, 0755);
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	if (dir_error) {
67162306a36Sopenharmony_ci		err_msg("Failed mkdir");
67262306a36Sopenharmony_ci	}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	printf("start_cpu list:%lu\n", start_cpu);
67562306a36Sopenharmony_ci	printf("number of worker threads:%lu + 1 snapshot thread\n", nrthreads);
67662306a36Sopenharmony_ci	printf("Allocated address:0x%016lx + secondary map:0x%016lx\n", (unsigned long)map1, (unsigned long)map2);
67762306a36Sopenharmony_ci	printf("logdir at : %s\n", logdir);
67862306a36Sopenharmony_ci	printf("Timeout: %d seconds\n", run_time);
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	time(&now);
68162306a36Sopenharmony_ci	printf("=================================\n");
68262306a36Sopenharmony_ci	printf("     Starting Test\n");
68362306a36Sopenharmony_ci	printf("     %s", ctime(&now));
68462306a36Sopenharmony_ci	printf("=================================\n");
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	for (i = 0; i < nrthreads; i++) {
68762306a36Sopenharmony_ci		if (1 && !fork()) {
68862306a36Sopenharmony_ci			prctl(PR_SET_PDEATHSIG, SIGKILL);
68962306a36Sopenharmony_ci			set_mycpu(start_cpu + i);
69062306a36Sopenharmony_ci			for (;;)
69162306a36Sopenharmony_ci				sched_yield();
69262306a36Sopenharmony_ci			exit(0);
69362306a36Sopenharmony_ci		}
69462306a36Sopenharmony_ci	}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	sa_alrm.sa_handler = &alrm_sighandler;
69862306a36Sopenharmony_ci	sigemptyset(&sa_alrm.sa_mask);
69962306a36Sopenharmony_ci	sa_alrm.sa_flags = 0;
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	if (sigaction(SIGALRM, &sa_alrm, 0) == -1) {
70262306a36Sopenharmony_ci		err_msg("Failed signal handler registration\n");
70362306a36Sopenharmony_ci	}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	alarm(run_time);
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	pthread_attr_init(&attr);
70862306a36Sopenharmony_ci	for (i = 0; i < nrthreads; i++) {
70962306a36Sopenharmony_ci		rim_thread_ids[i] = i;
71062306a36Sopenharmony_ci		pthread_create(&rim_threads[i], &attr, rim_fn, &rim_thread_ids[i]);
71162306a36Sopenharmony_ci		set_pthread_cpu(rim_threads[i], start_cpu + i);
71262306a36Sopenharmony_ci	}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	pthread_create(&mem_snapshot_thread, &attr, mem_snapshot_fn, map1);
71562306a36Sopenharmony_ci	set_pthread_cpu(mem_snapshot_thread, start_cpu + i);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	pthread_join(mem_snapshot_thread, NULL);
71962306a36Sopenharmony_ci	for (i = 0; i < nrthreads; i++) {
72062306a36Sopenharmony_ci		pthread_join(rim_threads[i], NULL);
72162306a36Sopenharmony_ci	}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	if (!timeout) {
72462306a36Sopenharmony_ci		time(&now);
72562306a36Sopenharmony_ci		printf("=================================\n");
72662306a36Sopenharmony_ci		printf("      Data Corruption Detected\n");
72762306a36Sopenharmony_ci		printf("      %s", ctime(&now));
72862306a36Sopenharmony_ci		printf("      See logfiles in %s\n", logdir);
72962306a36Sopenharmony_ci		printf("=================================\n");
73062306a36Sopenharmony_ci		return 1;
73162306a36Sopenharmony_ci	}
73262306a36Sopenharmony_ci	return 0;
73362306a36Sopenharmony_ci}
734