1/*
2 * Test soft page offline for process pages using madvise injector.
3 * Requires special injection support in the kernel.
4 *
5 * Copyright 2009 Intel Corporation
6 *
7 * tsoftinj is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public
9 * License as published by the Free Software Foundation; version
10 * 2.
11 *
12 * tinjpage is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * General Public License for more details.
16 *
17 * You should find a copy of v2 of the GNU General Public License somewhere
18 * on your Linux system; if not, write to the Free Software Foundation,
19 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 * Author: Andi Kleen
22 */
23#define _GNU_SOURCE 1
24#include <sys/mman.h>
25#include <stdio.h>
26#include <unistd.h>
27#include <sys/fcntl.h>
28#include <stdlib.h>
29#include <errno.h>
30#include "hugepage.h"
31
32#define MADV_SOFT_OFFLINE 101
33
34#define TMPDIR "./"
35
36int PS;
37int exitcode;
38char empty[4096];
39int corrupted;
40
41void *checked_mmap(void *addr, size_t length, int prot, int flags,
42                  int fd, off_t offset)
43{
44	void *p = mmap(addr, length, prot, flags, fd, offset);
45	if (p == (void *)-1L)
46		err("mmap");
47	return p;
48}
49
50unsigned meminfo(char *fmt)
51{
52	int found = 0;
53	FILE *f = fopen("/proc/meminfo", "r");
54	if (!f) err("open /proc/meminfo");
55	char *line = NULL;
56	size_t linelen = 0;
57	unsigned val = 0;
58	while (getline(&line, &linelen, f) > 0) {
59		if (sscanf(line, fmt, &val) == 1) {
60			found = 1;
61			break;
62		}
63	}
64	free(line);
65	fclose(f);
66	if (!found)  {
67		printf("cannot read HardwareCorruptedPages in meminfo\n");
68		exitcode = 1;
69	}
70	return val;
71}
72
73unsigned hardware_corrupted(void)
74{
75	return (meminfo("HardwareCorrupted: %u") * 1024) / PS;
76}
77
78char *ndesc(char *buf, char *a, char *b)
79{
80	snprintf(buf, 100, "%s %s", a, b);
81	return buf;
82}
83
84void offline(char *name, void *p)
85{
86	char buf[100];
87	if (madvise(p, PS, MADV_SOFT_OFFLINE) < 0)
88		err(ndesc(buf, name, "offline"));
89	corrupted++;
90}
91
92void disk_backed(char *name, int flags)
93{
94	char fn[100];
95	snprintf(fn, sizeof fn, TMPDIR "~test%u", getpid());
96	printf("shared, diskbacked\n");
97	int fd = open(fn, O_RDWR|O_CREAT|O_TRUNC, 0644);
98	if (fd < 0) err("open tmpfile");
99	write(fd, empty, sizeof empty);
100	char *p = checked_mmap(NULL, PS, PROT_READ|PROT_WRITE,
101			MAP_SHARED|flags, fd, 0);
102	*(volatile int *)p = 1;
103	offline(ndesc(fn, "disk backed", name), p);
104	munmap(p, PS);
105}
106
107void anonymous(char *name, int flags)
108{
109	char buf[100];
110	char *p = checked_mmap(NULL, PS, PROT_READ|PROT_WRITE,
111			MAP_PRIVATE|MAP_ANONYMOUS|flags, 0, 0);
112	printf("anonymous\n");
113	*(volatile int *)p = 1;
114	offline(ndesc(buf, "anonymous", name), p);
115	*(volatile int *)p = 1;
116	munmap(p, PS);
117}
118
119void shm_hugepage(char *name, int flags)
120{
121	int shmid = 0;
122	char buf[100];
123	char *p = alloc_shm_hugepage(&shmid, HPS);
124	if (!p)
125		errmsg("failed in alloc_shm_hugepage\n");
126	printf("shm hugepage\n");
127	*(volatile int *)p = 1;
128	offline(ndesc(buf, "shm hugepage", name), p);
129	*(volatile int *)p = 1;
130	free_shm_hugepage(shmid, p);
131}
132
133void anonymous_hugepage(char *name, int flags)
134{
135	char buf[100];
136	char *p = alloc_anonymous_hugepage(HPS, 1);
137	printf("anonymous hugepage\n");
138	*(volatile int *)p = 1;
139	offline(ndesc(buf, "anonymous hugepage", name), p);
140	*(volatile int *)p = 1;
141	free_anonymous_hugepage(p, HPS);
142}
143
144void filebacked_hugepage(char *name, int flags)
145{
146	int fd;
147	char path[100];
148	char fn[100];
149	snprintf(path, sizeof path, "%s/~test-hugepage%u",
150		 hugetlbfsdir, getpid());
151	char *p = alloc_filebacked_hugepage(path, HPS, 0, &fd);
152	printf("file backed hugepage\n");
153	*(volatile int *)p = 1;
154	offline(ndesc(fn, "file backed hugepage", name), p);
155	*(volatile int *)p = 1;
156	free_filebacked_hugepage(p, HPS, fd, path);
157}
158
159void check(unsigned *count, char *name, unsigned expected)
160{
161	unsigned count2 = hardware_corrupted();
162	unsigned diff = count2 - *count;
163	if (diff != expected) {
164		printf("%s: expected %d corrupted pages, got %u\n", name,
165			expected,
166			diff);
167		if (diff < expected)
168			exitcode = 1;
169	}
170	*count = count2;
171	corrupted = 0;
172}
173
174int main(void)
175{
176	PS = getpagesize();
177	HPS = gethugepagesize();
178
179	unsigned count = hardware_corrupted();
180	if (!hugetlbfs_root(hugetlbfsdir))
181		err("hugetlbfs_root");
182	anonymous("anonymous", 0);
183	check(&count, "anonymous", 1);
184	anonymous("anonymous mlock", MAP_LOCKED);
185	check(&count, "anonymous mlock", 1);
186	disk_backed("disk backed", 0);
187	check(&count, "disk backed", 1);
188	disk_backed("disk backed mlock", 0);
189	check(&count, "disk backed mlock", 1);
190	shm_hugepage("shm hugepage", 0);
191	check(&count, "shm hugepage", HPS / PS);
192	anonymous_hugepage("anonymous hugepage", 0);
193	check(&count, "anonymous hugepage", HPS / PS);
194	filebacked_hugepage("file backed hugepage", 0);
195	check(&count, "file backed hugepage", HPS / PS);
196	// add more test cases here
197
198	return exitcode;
199}
200