lkml.org 
[lkml]   [2003]   [Aug]   [29]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
/
Date
From
Subjectx86, ARM, PARISC, PPC, MIPS and Sparc folks please run this
Dear All,

I'd appreciate if folks would run the program below on various
machines, especially those whose caches aren't automatically coherent
at the hardware level.

It searches for that address multiple which an application can use to
get coherent multiple mappings of shared memory, with good performance.

I want this information for two reasons:

1. To check it correctly detects archs which page fault for
coherency or aren't coherent.
2. To check the timing test is robust, both for 1. and for
detecting archs where the hardware is coherent but slows
down (see Athlon below).
3. To check this is reliable enough to use at run time in an app.

I already got a surprise (to me): my Athlon MP is much slower
accessing multiple mappings which are within 32k of each other, than
mappings which are further apart, although it is coherent. The L1
data cache is 64k. (The explanation is easy: virtually indexed,
physically tagged cache moves data among cache lines, possibly via L2).

This suggests scope for improving x86 kernel performance in the areas
of kmap() and shared library / executable mappings, by good choice of
_virtual_ addresses. This doesn't require a cache colouring
page allocator, so maybe it's a new avenue?

Anyway, please lots of people run the program and post the output +
/proc/cpuinfo. Compile with optimisation, -O or -O2 is fine. (You
can add -DHAVE_SYSV_SHM too if you like):

gcc -o test test.c -O2
time ./test
cat /proc/cpuinfo

Thanks a lot :)
-- Jamie

==============================================================================

/* This code maps shared memory to multiple addresses and tests it
for cache coherency and performance.

Copyright (C) 1999, 2001, 2002, 2003 Jamie Lokier

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at
your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */

#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/signal.h>
#include <sys/mman.h>
#include <sys/time.h>

#if HAVE_SYSV_SHM
#include <sys/ipc.h>
#include <sys/shm.h>
#endif

//#include "pagealias.h"

/* Helpers to temporarily block all signals. These are used for when a
race condition might leave a temporary file that should have been
deleted -- we do our best to prevent this possibility. */

static void
block_signals (sigset_t * save_state)
{
sigset_t all_signals;
sigfillset (&all_signals);
sigprocmask (SIG_BLOCK, &all_signals, save_state);
}

static void
unblock_signals (sigset_t * restore_state)
{
sigprocmask (SIG_SETMASK, restore_state, (sigset_t *) 0);
}

/* Open a new shared memory file, either using the POSIX.4 `shm_open'
function, or using a regular temporary file in /tmp. Immediately
after opening the file, it is unlinked from the global namespace
using `shm_unlink' or `unlink'.

On success, the value returned is a file descriptor. Otherwise, -1
is returned and `errno' is set.

The descriptor can be closed using simply `close'. */

/* Note: `shm_open' requires link argument `-lposix4' on Suns.
On GNU/Linux with Glibc, it requires `-lrt'. Unfortunately, Glibc's
-lrt insists on linking to pthreads, which we may not want to use
because that enables thread locking overhead in other functions. So
we implement a direct method of opening shm on Linux. */

/* If this is changed, change the size of `buffer' below too. */
#if HAVE_SHM_OPEN
#define SHM_DIR_PREFIX "/" /* `shm_open' arg needs "/" for portability. */
#elif defined (__linux__)
#include <sys/statfs.h>
#define SHM_DIR_PREFIX "/dev/shm/"
#else
#undef SHM_DIR_PREFIX
#endif

static int
open_shared_memory_file (int use_tmp_file)
{
char * ptr, buffer [19];
int fd, i;
unsigned long number;
sigset_t save_signals;
struct timeval tv;

#if !HAVE_SHM_OPEN && defined (__linux__)
struct statfs sfs;
if (!use_tmp_file && (statfs (SHM_DIR_PREFIX, &sfs) != 0
|| sfs.f_type != 0x01021994 /* SHMFS_SUPER_MAGIC */))
{
errno = ENOSYS;
return -1;
}
#endif

loop:
/* Print a randomised path name into `buffer'. The string depends on
the directory and whether we are using POSIX.4 shared memory or a
regular temporary file. RANDOM is a 5-digit, base-62
representation of a pseudo-random number. The string is used as a
candidate in the search for an unused shared segment or file name. */
#ifdef SHM_DIR_PREFIX
strcpy (buffer, use_tmp_file ? "/tmp/shm-" : SHM_DIR_PREFIX "shm-");
#else
strcpy (buffer, "/tmp/shm-");
#endif
ptr = buffer + strlen (buffer);
gettimeofday (&tv, (struct timezone *) 0);
number = (unsigned long) random ();
number += (unsigned long) getpid ();
number += (unsigned long) tv.tv_sec + (unsigned long) tv.tv_usec;
for (i = 0; i < 5; i++)
{
/* Don't use character arithmetic, as not all systems are ASCII. */
*ptr++ = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" [number % 62];
number /= 62;
}
*ptr = '\0';

/* Block signals between the open and unlink, to really minimise
the chance of accidentally leaving an unwanted file around. */
block_signals (&save_signals);
#if HAVE_SHM_OPEN
if (!use_tmp_file)
{
fd = shm_open (buffer, O_RDWR | O_CREAT | O_EXCL, 0600);
if (fd != -1)
shm_unlink (buffer);
}
else
#endif /* HAVE_SHM_OPEN */
{
fd = open (buffer, O_RDWR | O_CREAT | O_EXCL, 0600);
if (fd != -1)
unlink (buffer);
}
unblock_signals (&save_signals);

/* If we failed due to a name collision or a signal, try again. */
if (fd == -1 && (errno == EEXIST || errno == EINTR || errno == EISDIR))
goto loop;

return fd;
}

/* Allocate a region of address space `size' bytes long, so that the
region will not be allocated for any other purpose. It is freed with
`munmap'.

Returns the mapped base address on success. Otherwise, MAP_FAILED is
returned and `errno' is set. */

static size_t system_page_size;

#if !defined (MAP_ANONYMOUS) && defined (MAP_ANON)
#define MAP_ANONYMOUS MAP_ANON
#endif
#ifndef MAP_NORESERVE
#define MAP_NORESERVE 0
#endif
#ifndef MAP_FILE
#define MAP_FILE 0
#endif
#ifndef MAP_VARIABLE
#define MAP_VARIABLE 0
#endif
#ifndef MAP_FAILED
#define MAP_FAILED ((void *) -1)
#endif
#ifndef PROT_NONE
#define PROT_NONE PROT_READ
#endif

static void *
map_address_space (void * optional_address, size_t size, int access)
{
void * addr;
#ifdef MAP_ANONYMOUS
addr = mmap (optional_address, size,
access ? (PROT_READ | PROT_WRITE) : PROT_NONE,
(MAP_PRIVATE | MAP_ANONYMOUS
| (optional_address ? MAP_FIXED : MAP_VARIABLE)
| (access ? 0 : MAP_NORESERVE)), -1, (off_t) 0);
#else /* not defined MAP_ANONYMOUS */
int save_errno, zero_fd = open ("/dev/zero", O_RDONLY);
if (zero_fd == -1)
return MAP_FAILED;
addr = mmap (optional_address, size,
access ? (PROT_READ | PROT_WRITE) : PROT_NONE,
(MAP_PRIVATE | MAP_FILE
| (optional_address ? MAP_FIXED : MAP_VARIABLE)
| (access ? 0 : MAP_NORESERVE)), zero_fd, (off_t) 0);
save_errno = errno;
close (zero_fd);
errno = save_errno;
#endif /* not defined MAP_ANONMOUS */
return addr;
}

/* Set up a page alias mapping using mmap() on POSIX shared memory or on
a temporary regular file.

Returns the mapped base address on success. Otherwise, 0 is returned
and `errno' is set. */

static void *
page_alias_using_mmap (size_t size, size_t separation, int use_tmp_file)
{
void * base_addr, * addr;
int fd, i, save_errno;
struct stat st;

fd = open_shared_memory_file (use_tmp_file);
if (fd == -1)
goto fail;

/* First, resize the shared memory file to the desired size. */
if (ftruncate (fd, size) != 0 || fstat (fd, &st) != 0 || st.st_size != size)
goto close_fail;

/* Map an anonymous region `separation + size' bytes long. This is how
we allocate sufficient contiguous address space. We over-map
this with the aliased buffer. */
if ((base_addr = map_address_space (0, separation + size, 0)) == MAP_FAILED)
goto close_fail;

/* Map the same shared memory repeatedly, at different addresses. */
for (i = 0; i < 2; i++)
{
addr = mmap ((char *) base_addr + (i ? separation : 0), size,
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FILE | MAP_FIXED,
fd, (off_t) 0);
if (addr == MAP_FAILED)
goto unmap_fail;
if (addr != (char *) base_addr + (i ? separation : 0))
{
/* `mmap' ignored MAP_FIXED! Should never happen. */
munmap (addr, size);
save_errno = EINVAL;
goto unmap_fail_se;
}
}
if (close (fd) != 0)
goto unmap_fail;

/* Success! */
return base_addr;

/* Failure. */
unmap_fail:
save_errno = errno;
unmap_fail_se:
munmap (base_addr, separation + size);
errno = save_errno;
close_fail:
save_errno = errno;
close (fd);
errno = save_errno;
fail:
return 0;
}

/* Set up a page alias mapping using SYSV IPC shared memory.

Returns the mapped base address on success. Otherwise, 0 is returned
and `errno' is set. */

#if HAVE_SYSV_SHM

static void *
page_alias_using_sysv_shm (size_t size, size_t separation)
{
void * base_addr, * addr;
sigset_t save_signals;
int shmid, i, save_errno;

/* Map an anonymous region `separation + size' bytes long. This is how
we allocate sufficient contiguous address space. We over-map
this with the aliased buffer. */
if ((base_addr = map_address_space (0, separation + size, 0)) == MAP_FAILED)
goto fail;

/* Block signals between the shmget() and IPC_RMID, to minimise the chance
of accidentally leaving an unwanted shared segment around. */
block_signals (&save_signals);

shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | IPC_EXCL | 0600);
if (shmid == -1)
goto unmap_fail;

/* Map the same shared memory repeatedly, at different addresses. */
for (i = 0; i < 2; i++)
{
/* `shmat' is tried twice. The fist time it can fail if the local
implementation of `shmat' refuses to map over a region mapped
with `mmap'. In that case, we punch a hole using `munmap' and
do it again.

If the local `shmat' has this property, the `shmat' calls
to fixed addresses might collide with a concurrent thread
which is also doing mappings, and will fail. At least it
is a safe failure.

On the other hand, if the local `shmat' can map over
already-mapped regions (in the same way that `mmap' does), it
is essential that we do actually use an already-mapped region,
so that collisions with a concurrent thread can't possibly
result in both of us grabbing the same address range with no
indication of error. */
addr = shmat (shmid, (char *) base_addr + (i ? separation : 0), 0);
if (addr == (void *) -1 && errno == EINVAL)
{
munmap ((char *) base_addr + (i ? separation : 0), size);
addr = shmat (shmid, (char *) base_addr + (i ? separation : 0), 0);
}

/* Check for errors. */
if (addr == (void *) -1)
{
save_errno = errno;
if (i == 1)
shmdt (base_addr);
goto remove_shm_fail_se;
}
else if (addr != (char *) base_addr + (i ? separation : 0))
{
/* `shmat' ignored the requested address! */
if (i == 1)
shmdt (base_addr);
save_errno = EINVAL;
goto remove_shm_fail_se;
}
}

if (shmctl (shmid, IPC_RMID, (struct shmid_ds *) 0) != 0)
goto remove_shm_fail;
unblock_signals (&save_signals);

/* Success! */
return base_addr;

/* Failure. */
remove_shm_fail:
save_errno = errno;
remove_shm_fail_se:
while (--i >= 0)
shmdt ((char *) base_addr + (i ? separation : 0));
shmctl (shmid, IPC_RMID, (struct shmid_ds *) 0);
errno = save_errno;
unmap_fail:
save_errno = errno;
unblock_signals (&save_signals);
munmap (base_addr, separation + size);
errno = save_errno;
fail:
return 0;
}

#endif /* HAVE_SYSV_SHM */

/* Map a page-aliased ring buffer. Shared memory of size `size' is
mapped twice, with the difference between the two addresses being
`separation', which must be at least `size'. The total address range
used is `separation + size' bytes long.

On success, *METHOD is filled with a number which must be passed to
`page_alias_unmap', and the mapped base address is returned.
Otherwise, 0 is returned and `errno' is set. */

static void *
__page_alias_map (size_t size, size_t separation, int * method)
{
void * addr;
if (((size | separation) & (system_page_size - 1)) != 0 || size > separation)
{
errno = -EINVAL;
return 0;
}

/* Try these strategies in turn: POSIX shm_open(), SYSV IPC, regular file. */
#ifdef SHM_DIR_PREFIX
*method = 0;
if ((addr = page_alias_using_mmap (size, separation, 0)) != 0)
return addr;
#endif
#if HAVE_SYSV_SHM
*method = 1;
if ((addr = page_alias_using_sysv_shm (size, separation)) != 0)
return addr;
#endif
*method = 2;
return page_alias_using_mmap (size, separation, 1);
}

/* Unmap a page-aliased ring buffer previously allocated by
`page_alias_map'. `address' is the base address, and `size' and
`separation' are the arguments previously passed to
`__page_alias_map'. `method' is the value previously stored in *METHOD.

Returns 0 on success. Otherwise, -1 is returned and `errno' is set. */

static int
__page_alias_unmap (void * address, size_t size, size_t separation, int method)
{
#if HAVE_SYSV_SHM
if (method == 1)
{
shmdt (address);
shmdt (address + separation);
if (separation > size)
munmap (address + size, separation - size);
return 0;
}
#endif

return munmap (address, separation + size);
}

/* Map a page-aliased ring buffer. `size' is the size of the buffer to
create; it will be mapped twice to cover a total address range
`size * 2' bytes long.

On success, *METHOD is filled with a number which must be passed to
`page_alias_unmap', and the mapped base address is returned.
Otherwise, 0 is returned and `errno' is set. */

void *
page_alias_map (size_t size, int * method)
{
return __page_alias_map (size, size, method);
}

/* Unmap a page-aliased ring buffer previously allocated by
`page_alias_map'. `address' is the base address, and `size' is the
size of the buffer (which is half of the total mapped address range).
`method' is a value previously stored in *METHOD by `page_alias_map'.

Returns 0 on success. Otherwise, -1 is returned and `errno' is set. */

int
page_alias_unmap (void * address, size_t size, int method)
{
return __page_alias_unmap (address, size, size, method);
}

/* Map some memory which is not aliased, for timing comparisons against
aliased pages. We use a combination of mappings similar to
page_alias_*(), in case there are resource limitations which would
prevent malloc() or a single mmap() working for the larger address
range tests. */

static void *
page_no_alias (size_t size, size_t separation)
{
void * base_addr, * addr;
int i, save_errno;

if ((base_addr = map_address_space (0, separation + size, 0)) == MAP_FAILED)
goto fail;

/* Map anonymous memory at the different addresses. */
for (i = 0; i < 2; i++)
{
addr = map_address_space ((char *) base_addr + (i ? separation : 0),
size, 1);
if (addr == MAP_FAILED)
goto unmap_fail;
if (addr != (char *) base_addr + (i ? separation : 0))
{
/* `mmap' ignored MAP_FIXED! Should never happen. */
munmap (addr, size);
save_errno = EINVAL;
goto unmap_fail_se;
}
}

/* Success! */
return base_addr;

/* Failure. */
unmap_fail:
save_errno = errno;
unmap_fail_se:
munmap (base_addr, separation + size);
errno = save_errno;
fail:
return 0;
}

/* This should be a word size that the architecture can read and write
fast in a single instruction. In principle, C's `int' is the natural
word size, but in practice it isn't on 64-bit machines. */

#define WORD long

/* These GCC-specific asm statements force values into registers, and
also act as compiler memory barriers. These are used to force a
group of write/write/read instructions as close together as possible,
to maximise the detection of store buffer conditions. Despite being
asm statements, these will work with any of GCC's target architectures,
provided they have >= 4 registers. */

#if __GNUC__ >= 3
#define __noinline __attribute__ ((__noinline__))
#else
#define __noinline
#endif

#ifdef __GNUC__
#define force_into_register(var) \
__asm__ ("" : "=r" (var) : "0" (var) : "memory")
#define force_into_registers(var1, var2, var3, var4) \
__asm__ ("" : "=r" (var1), "=r" (var2), "=r" (var3), "=r" (var4) \
: "0" (var1), "1" (var2), "2" (var3), "3" (var4) : "memory")
#else
#define force_into_register(var) do {} while (0)
#define force_into_registers(var1, var2, var3, var4) do {} while (0)
#endif

/* This function tries to test whether a CPU snoops its store buffer for
reads within a few instructions, and ignores virtual to physical
address translations when doing that. In principle a CPU might do
this even if it's L1 cache is physically tagged or indexed, although
I have not seen such a system. (A CPU which uses store buffer
snooping and with an off-board MMU, which the CPU is unaware of,
could have this property).

It isn't possible to do this test perfectly; we do our best. The
`force_into_register' macros ensure that the write/write/read
sequence is as compact as the compiler can make it. */

static WORD __noinline
test_store_buffer_snoop (volatile WORD * ptr1, volatile WORD * ptr2)
{
register volatile WORD * __regptr1 = ptr1, * __regptr2 = ptr2;
register WORD __reg1 = 1, __reg2 = 0;
force_into_registers (__reg1, __reg2, __regptr1, __regptr2);
*__regptr1 = __reg1;
*__regptr2 = __reg2;
__reg1 = *__regptr1;
force_into_register (__reg1);
return __reg1;
}

/* This function tests whether writes to one page are seen in another
page at a different virtual address, and whether they are nearly as
fast as normal writes.

The accesses are timed by the caller of this function.
Alternate writes go to alternate pages, so that if aliasing is
implemented using page faults, it will clearly show up in the
timings. */

static int __noinline
test_page_alias (volatile WORD * ptr1, volatile WORD * ptr2, int timing_loops)
{
WORD fail = 0;
while (--timing_loops >= 0)
fail |= test_store_buffer_snoop (ptr1, ptr2);
return fail != 0;
}

/* This function tests L1 cache coherency without checking for store
buffer snoop coherency. To do this, we add delays after each store
to allow the store buffer to drain. The result of this function is
not important: it is only used in a diagnostic message. */

static int __noinline
test_l1_only (volatile WORD * ptr1, volatile WORD * ptr2)
{
static volatile WORD dummy;
int i, j;
WORD fail = 0;
for (i = 0; i < 10; i++)
{
*ptr1 = 1;
for (j = 0; j < 1000; j++) /* Dummy volatile writes for delay. */
dummy = 0;
*ptr2 = 0;
for (j = 0; j < 1000; j++) /* Dummy volatile writes for delay. */
dummy = 0;
fail |= *ptr1;
}
return fail != 0;
}

/* Thoroughly test a pair of aliased pages with a fixed address
separation, to see if they really behave like memory appearing at two
locations, and efficiently. We search through different values of
`separation' searching for a suitable "cache colour" on this machine. */

static inline const char *
test_one_separation (size_t separation)
{
void * buffers [2];
long timings [3];
int i, method, timing_loops = 64;

/* We measure timings of 3 different tests, each 128 times to find the
minimum. 0: Writes and reads to aliased pages. 1: Writes and
reads to non-aliased pages, to compare with 1. 2: Doing nothing,
to measure the time for `gettimeofday' itself.

The measurements are done in a mixed up order. If we did 64
measurements of type 0, then 64 of type 1, then 64 of type 2, the
results could be mislead due to synchronisation with other
processes occuring on the machine. */

/* A previously generated random shuffle of bit-pairs. Each pair is a
number from the set {0,1,2}. Each number occurs exactly 128 times. */
static const unsigned char pattern [96] =
{
0x64, 0x68, 0x9a, 0x86, 0x42, 0x10, 0x90, 0x81, 0x58, 0x91, 0x18, 0x56,
0x12, 0x44, 0x64, 0x89, 0x29, 0xa9, 0x96, 0x05, 0x61, 0x80, 0x82, 0x49,
0x02, 0x16, 0x89, 0x12, 0x9a, 0x45, 0x41, 0x12, 0xa9, 0xa6, 0x01, 0x99,
0x88, 0x80, 0x94, 0x20, 0x86, 0x29, 0x29, 0x1a, 0xa5, 0x46, 0x66, 0x25,
0x42, 0x20, 0xa4, 0x81, 0x20, 0x81, 0x50, 0x44, 0x01, 0x06, 0xa5, 0x19,
0x4a, 0x56, 0x28, 0x89, 0x88, 0x14, 0x94, 0x88, 0x1a, 0xa4, 0x95, 0x15,
0x82, 0x99, 0x84, 0x64, 0x52, 0x56, 0x69, 0x64, 0x00, 0x95, 0x9a, 0x89,
0x48, 0x01, 0x58, 0x88, 0x60, 0xa6, 0x29, 0x06, 0x64, 0xa0, 0x56, 0x85,
};

buffers [0] = __page_alias_map (system_page_size, separation, &method);
if (buffers [0] == 0)
return "alias map failed";
buffers [1] = page_no_alias (system_page_size, separation);
if (buffers [1] == 0)
{
__page_alias_unmap (buffers [0], system_page_size, separation, method);
return "non-alias map failed";
}

retry:
timings [2] = timings [1] = timings [0] = LONG_MAX;
for (i = 0; i < 384; i++)
{
struct timeval time_before, time_after;
long time_delta;
int fail = 0, which_test = (pattern [i >> 2] >> ((i & 3) << 1)) & 3;
volatile WORD * ptr1 = (volatile WORD *) buffers [which_test];
volatile WORD * ptr2 = (volatile WORD *) ((char *) ptr1 + separation);

/* Test whether writes to one page appear immediately in the other,
and time how long the memory accesses take. */
gettimeofday (&time_before, (struct timezone *) 0);
if (which_test < 2)
fail = test_page_alias (ptr1, ptr2, timing_loops);
gettimeofday (&time_after, (struct timezone *) 0);

if (fail && which_test == 0)
{
/* Test whether the failure is due to a store buffer bypass
which ignores virtual address translation. */
int l1_fail = test_l1_only (ptr1, ptr2);
__page_alias_unmap (buffers [0], system_page_size, separation,
method);
munmap (buffers [1], separation + system_page_size);
return l1_fail ? "cache not coherent" : "store buffer not coherent";
}

time_delta = ((time_after.tv_usec - time_before.tv_usec)
+ 1000000 * (time_after.tv_sec - time_before.tv_sec));

/* Find the smallest time taken for each test. Ignore negative
glitches due to Linux' tendancy to jump the clock backwards. */
if (time_delta >= 0 && time_delta < timings [which_test])
timings [which_test] = time_delta;
}

/* Remove the cost of `gettimeofday()' itself from measurements. */
timings [0] -= timings [2];
timings [1] -= timings [2];

/* Keep looping until at least one measurement becomes significant. A
very fast CPU will show measurements of zero microseconds for
smaller values of `timing_loops'. Also loop until the cost of
`gettimeofday()' becomes insignificant. When the program is run
under `strace', the latter is a big and this is needed to stabilise
the results. */
if (timings [0] <= 10 * (1 + timings [2])
&& timings [1] <= 10 * (1 + timings [2]))
{
timing_loops <<= 1;
goto retry;
}

__page_alias_unmap (buffers [0], system_page_size, separation, method);
munmap (buffers [1], separation + system_page_size);

/* Reject page aliasing if it is much slower than accessing a single,
definitely cached page directly. */
if (timings [0] > 2 * timings [1])
return "too slow";

/* Success! Passed all tests for these parameters. */
return 0;
}

size_t page_alias_smallest_size;

void
page_alias_init (void)
{
size_t size;

#ifdef _SC_PAGESIZE
system_page_size = sysconf (_SC_PAGESIZE);
#elif defined (_SC_PAGE_SIZE)
system_page_size = sysconf (_SC_PAGE_SIZE);
#else
system_page_size = getpagesize ();
#endif

for (size = system_page_size; size <= 16 * 1024 * 1024; size *= 2)
{
const char * reason = test_one_separation (size);

printf ("Test separation: %lu bytes: %s%s\n",
(unsigned long) size, reason ? "FAIL - " : "pass",
reason ? reason : "");

/* This logic searches for the smallest _contiguous_ range
of page sizes for which `page_alias_test' passes. */
if (reason == 0 && page_alias_smallest_size == 0)
page_alias_smallest_size = size;
else if (reason != 0 && page_alias_smallest_size != 0)
{
/* Fail, indicating that page-aliasing is not reliable,
because there's a maximum size. We don't support that as
it seems quite unlikely given our model of cache colouring. */
page_alias_smallest_size = 0;
break;
}
}

printf ("VM page alias coherency test: ");

if (page_alias_smallest_size == 0)
printf ("failed; will use copy buffers instead\n");
else if (page_alias_smallest_size == system_page_size)
printf ("all sizes passed\n");
else
printf ("minimum fast spacing: %lu (%lu page%s)\n",
(unsigned long) page_alias_smallest_size,
(unsigned long) (page_alias_smallest_size / system_page_size),
(page_alias_smallest_size == system_page_size) ? "" : "s");
}

//#ifdef TEST_PAGEALIAS
int
main ()
{
page_alias_init ();
return 0;
}
//#endif
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

\
 
 \ /
  Last update: 2005-03-22 13:48    [W:0.298 / U:0.056 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site