16c46a77dSDavid van Moolenbroek /* Test for end-of-file during block device I/O - by D.C. van Moolenbroek */
26c46a77dSDavid van Moolenbroek /* This test needs to be run as root; it sets up and uses a VND instance. */
36c46a77dSDavid van Moolenbroek /*
46c46a77dSDavid van Moolenbroek * The test should work with all root file system block sizes, but only tests
56c46a77dSDavid van Moolenbroek * certain corner cases if the root FS block size is twice the page size.
66c46a77dSDavid van Moolenbroek */
76c46a77dSDavid van Moolenbroek #include <stdlib.h>
86c46a77dSDavid van Moolenbroek #include <string.h>
96c46a77dSDavid van Moolenbroek #include <signal.h>
106c46a77dSDavid van Moolenbroek #include <sys/param.h>
116c46a77dSDavid van Moolenbroek #include <sys/wait.h>
126c46a77dSDavid van Moolenbroek #include <sys/mman.h>
136c46a77dSDavid van Moolenbroek #include <sys/ioctl.h>
146c46a77dSDavid van Moolenbroek #include <minix/partition.h>
156c46a77dSDavid van Moolenbroek #include <fcntl.h>
166c46a77dSDavid van Moolenbroek #include <unistd.h>
176c46a77dSDavid van Moolenbroek #include <assert.h>
186c46a77dSDavid van Moolenbroek
196c46a77dSDavid van Moolenbroek #define VNCONFIG "/usr/sbin/vnconfig"
206c46a77dSDavid van Moolenbroek
216c46a77dSDavid van Moolenbroek #define SECTOR_SIZE 512 /* this should be the sector size of VND */
226c46a77dSDavid van Moolenbroek
236c46a77dSDavid van Moolenbroek #define ITERATIONS 3
246c46a77dSDavid van Moolenbroek
256c46a77dSDavid van Moolenbroek enum {
266c46a77dSDavid van Moolenbroek BEFORE_EOF,
276c46a77dSDavid van Moolenbroek UPTO_EOF,
286c46a77dSDavid van Moolenbroek ACROSS_EOF,
296c46a77dSDavid van Moolenbroek ONEPAST_EOF,
306c46a77dSDavid van Moolenbroek FROM_EOF,
316c46a77dSDavid van Moolenbroek BEYOND_EOF
326c46a77dSDavid van Moolenbroek };
336c46a77dSDavid van Moolenbroek
346c46a77dSDavid van Moolenbroek #include "common.h"
356c46a77dSDavid van Moolenbroek
366c46a77dSDavid van Moolenbroek static int need_cleanup = 0;
376c46a77dSDavid van Moolenbroek
386c46a77dSDavid van Moolenbroek static int dev_fd;
396c46a77dSDavid van Moolenbroek static size_t dev_size;
406c46a77dSDavid van Moolenbroek static char *dev_buf;
416c46a77dSDavid van Moolenbroek static char *dev_ref;
426c46a77dSDavid van Moolenbroek
436c46a77dSDavid van Moolenbroek static size_t block_size;
446c46a77dSDavid van Moolenbroek static size_t page_size;
456c46a77dSDavid van Moolenbroek static int test_peek;
466c46a77dSDavid van Moolenbroek
476c46a77dSDavid van Moolenbroek static char *mmap_ptr = NULL;
486c46a77dSDavid van Moolenbroek static size_t mmap_size;
496c46a77dSDavid van Moolenbroek
506c46a77dSDavid van Moolenbroek static int pipe_fd[2];
516c46a77dSDavid van Moolenbroek
526c46a77dSDavid van Moolenbroek /*
536c46a77dSDavid van Moolenbroek * Fill the given buffer with random contents.
546c46a77dSDavid van Moolenbroek */
556c46a77dSDavid van Moolenbroek static void
fill_buf(char * buf,size_t size)566c46a77dSDavid van Moolenbroek fill_buf(char * buf, size_t size)
576c46a77dSDavid van Moolenbroek {
586c46a77dSDavid van Moolenbroek
596c46a77dSDavid van Moolenbroek while (size--)
606c46a77dSDavid van Moolenbroek *buf++ = lrand48() & 0xff;
616c46a77dSDavid van Moolenbroek }
626c46a77dSDavid van Moolenbroek
636c46a77dSDavid van Moolenbroek /*
646c46a77dSDavid van Moolenbroek * Place the elements of the source array in the destination array in random
656c46a77dSDavid van Moolenbroek * order. There are probably better ways to do this, but it is morning, and I
666c46a77dSDavid van Moolenbroek * haven't had coffee yet, so go away.
676c46a77dSDavid van Moolenbroek */
686c46a77dSDavid van Moolenbroek static void
scramble(int * dst,const int * src,int count)696c46a77dSDavid van Moolenbroek scramble(int * dst, const int * src, int count)
706c46a77dSDavid van Moolenbroek {
716c46a77dSDavid van Moolenbroek int i, j, k;
726c46a77dSDavid van Moolenbroek
736c46a77dSDavid van Moolenbroek for (i = 0; i < count; i++)
746c46a77dSDavid van Moolenbroek dst[i] = i;
756c46a77dSDavid van Moolenbroek
766c46a77dSDavid van Moolenbroek for (i = count - 1; i >= 0; i--) {
776c46a77dSDavid van Moolenbroek j = lrand48() % (i + 1);
786c46a77dSDavid van Moolenbroek
796c46a77dSDavid van Moolenbroek k = dst[j];
806c46a77dSDavid van Moolenbroek dst[j] = dst[i];
816c46a77dSDavid van Moolenbroek dst[i] = src[k];
826c46a77dSDavid van Moolenbroek }
836c46a77dSDavid van Moolenbroek }
846c46a77dSDavid van Moolenbroek
856c46a77dSDavid van Moolenbroek /*
866c46a77dSDavid van Moolenbroek * Perform I/O using read(2) and check the returned results against the
876c46a77dSDavid van Moolenbroek * expected result and the image reference data.
886c46a77dSDavid van Moolenbroek */
896c46a77dSDavid van Moolenbroek static void
io_read(size_t pos,size_t len,size_t expected)906c46a77dSDavid van Moolenbroek io_read(size_t pos, size_t len, size_t expected)
916c46a77dSDavid van Moolenbroek {
926c46a77dSDavid van Moolenbroek ssize_t bytes;
936c46a77dSDavid van Moolenbroek
946c46a77dSDavid van Moolenbroek assert(len > 0 && len <= dev_size);
956c46a77dSDavid van Moolenbroek assert(expected <= len);
966c46a77dSDavid van Moolenbroek
976c46a77dSDavid van Moolenbroek if (lseek(dev_fd, (off_t)pos, SEEK_SET) != pos) e(0);
986c46a77dSDavid van Moolenbroek
996c46a77dSDavid van Moolenbroek memset(dev_buf, 0, len);
1006c46a77dSDavid van Moolenbroek
1016c46a77dSDavid van Moolenbroek if ((bytes = read(dev_fd, dev_buf, len)) < 0) e(0);
1026c46a77dSDavid van Moolenbroek
1036c46a77dSDavid van Moolenbroek if (bytes != expected) e(0);
1046c46a77dSDavid van Moolenbroek
1056c46a77dSDavid van Moolenbroek if (memcmp(&dev_ref[pos], dev_buf, bytes)) e(0);
1066c46a77dSDavid van Moolenbroek }
1076c46a77dSDavid van Moolenbroek
1086c46a77dSDavid van Moolenbroek /*
1096c46a77dSDavid van Moolenbroek * Perform I/O using write(2) and check the returned result against the
1106c46a77dSDavid van Moolenbroek * expected result. Update the image reference data as appropriate.
1116c46a77dSDavid van Moolenbroek */
1126c46a77dSDavid van Moolenbroek static void
io_write(size_t pos,size_t len,size_t expected)1136c46a77dSDavid van Moolenbroek io_write(size_t pos, size_t len, size_t expected)
1146c46a77dSDavid van Moolenbroek {
1156c46a77dSDavid van Moolenbroek ssize_t bytes;
1166c46a77dSDavid van Moolenbroek
1176c46a77dSDavid van Moolenbroek assert(len > 0 && len <= dev_size);
1186c46a77dSDavid van Moolenbroek assert(expected <= len);
1196c46a77dSDavid van Moolenbroek
1206c46a77dSDavid van Moolenbroek if (lseek(dev_fd, (off_t)pos, SEEK_SET) != pos) e(0);
1216c46a77dSDavid van Moolenbroek
1226c46a77dSDavid van Moolenbroek fill_buf(dev_buf, len);
1236c46a77dSDavid van Moolenbroek
1246c46a77dSDavid van Moolenbroek if ((bytes = write(dev_fd, dev_buf, len)) < 0) e(0);
1256c46a77dSDavid van Moolenbroek
1266c46a77dSDavid van Moolenbroek if (bytes != expected) e(0);
1276c46a77dSDavid van Moolenbroek
1286c46a77dSDavid van Moolenbroek if (bytes > 0) {
1296c46a77dSDavid van Moolenbroek assert(pos + bytes <= dev_size);
1306c46a77dSDavid van Moolenbroek
1316c46a77dSDavid van Moolenbroek memcpy(&dev_ref[pos], dev_buf, bytes);
1326c46a77dSDavid van Moolenbroek }
1336c46a77dSDavid van Moolenbroek }
1346c46a77dSDavid van Moolenbroek
1356c46a77dSDavid van Moolenbroek /*
1366c46a77dSDavid van Moolenbroek * Test if reading from the given pointer succeeds or not, and return the
1376c46a77dSDavid van Moolenbroek * result.
1386c46a77dSDavid van Moolenbroek */
1396c46a77dSDavid van Moolenbroek static int
is_readable(char * ptr)1406c46a77dSDavid van Moolenbroek is_readable(char * ptr)
1416c46a77dSDavid van Moolenbroek {
1426c46a77dSDavid van Moolenbroek ssize_t r;
1436c46a77dSDavid van Moolenbroek char byte;
1446c46a77dSDavid van Moolenbroek
1456c46a77dSDavid van Moolenbroek /*
1466c46a77dSDavid van Moolenbroek * If we access the pointer directly, we will get a fatal signal.
1476c46a77dSDavid van Moolenbroek * Thus, for that to work we would need a child process, making the
1486c46a77dSDavid van Moolenbroek * whole test slow and noisy. Let a service try the operation instead.
1496c46a77dSDavid van Moolenbroek */
1506c46a77dSDavid van Moolenbroek r = write(pipe_fd[1], ptr, 1);
1516c46a77dSDavid van Moolenbroek
1526c46a77dSDavid van Moolenbroek if (r == 1) {
1536c46a77dSDavid van Moolenbroek /* Don't fill up the pipe. */
1546c46a77dSDavid van Moolenbroek if (read(pipe_fd[0], &byte, 1) != 1) e(0);
1556c46a77dSDavid van Moolenbroek
1566c46a77dSDavid van Moolenbroek return 1;
1576c46a77dSDavid van Moolenbroek } else if (r != -1 || errno != EFAULT)
1586c46a77dSDavid van Moolenbroek e(0);
1596c46a77dSDavid van Moolenbroek
1606c46a77dSDavid van Moolenbroek return 0;
1616c46a77dSDavid van Moolenbroek }
1626c46a77dSDavid van Moolenbroek
1636c46a77dSDavid van Moolenbroek /*
1646c46a77dSDavid van Moolenbroek * Perform I/O using mmap(2) and check the returned results against the
1656c46a77dSDavid van Moolenbroek * expected result and the image reference data. Ensure that bytes beyond the
1666c46a77dSDavid van Moolenbroek * device end are either zero (on the remainder of the last page) or
1676c46a77dSDavid van Moolenbroek * inaccessible on pages entirely beyond the device end.
1686c46a77dSDavid van Moolenbroek */
1696c46a77dSDavid van Moolenbroek static void
io_peek(size_t pos,size_t len,size_t expected)1706c46a77dSDavid van Moolenbroek io_peek(size_t pos, size_t len, size_t expected)
1716c46a77dSDavid van Moolenbroek {
1726c46a77dSDavid van Moolenbroek size_t n, delta, mapped_size;
1736c46a77dSDavid van Moolenbroek char *ptr;
1746c46a77dSDavid van Moolenbroek
1756c46a77dSDavid van Moolenbroek assert(test_peek);
1766c46a77dSDavid van Moolenbroek
1776c46a77dSDavid van Moolenbroek delta = pos % page_size;
1786c46a77dSDavid van Moolenbroek
1796c46a77dSDavid van Moolenbroek pos -= delta;
1806c46a77dSDavid van Moolenbroek len += delta;
1816c46a77dSDavid van Moolenbroek
1826c46a77dSDavid van Moolenbroek len = roundup(len, page_size);
1836c46a77dSDavid van Moolenbroek
1846c46a77dSDavid van Moolenbroek /* Don't bother with the given expected value. Recompute it. */
1856c46a77dSDavid van Moolenbroek if (pos < dev_size)
1866c46a77dSDavid van Moolenbroek expected = MIN(dev_size - pos, len);
1876c46a77dSDavid van Moolenbroek else
1886c46a77dSDavid van Moolenbroek expected = 0;
1896c46a77dSDavid van Moolenbroek
1906c46a77dSDavid van Moolenbroek mapped_size = roundup(dev_size, page_size);
1916c46a77dSDavid van Moolenbroek
1926c46a77dSDavid van Moolenbroek assert(!(len % page_size));
1936c46a77dSDavid van Moolenbroek
1946c46a77dSDavid van Moolenbroek ptr = mmap(NULL, len, PROT_READ, MAP_PRIVATE | MAP_FILE, dev_fd,
1956c46a77dSDavid van Moolenbroek (off_t)pos);
1966c46a77dSDavid van Moolenbroek
1976c46a77dSDavid van Moolenbroek /*
1986c46a77dSDavid van Moolenbroek * As of writing, VM allows memory mapping at any offset and for any
1996c46a77dSDavid van Moolenbroek * length. At least for block devices, VM should probably be changed
2006c46a77dSDavid van Moolenbroek * to throw ENXIO for any pages beyond the file end, which in turn
2016c46a77dSDavid van Moolenbroek * renders all the SIGBUS tests below obsolete.
2026c46a77dSDavid van Moolenbroek */
2036c46a77dSDavid van Moolenbroek if (ptr == MAP_FAILED) {
2046c46a77dSDavid van Moolenbroek if (pos + len <= mapped_size) e(0);
2056c46a77dSDavid van Moolenbroek if (errno != ENXIO) e(0);
2066c46a77dSDavid van Moolenbroek
2076c46a77dSDavid van Moolenbroek return;
2086c46a77dSDavid van Moolenbroek }
2096c46a77dSDavid van Moolenbroek
2106c46a77dSDavid van Moolenbroek mmap_ptr = ptr;
2116c46a77dSDavid van Moolenbroek mmap_size = len;
2126c46a77dSDavid van Moolenbroek
2136c46a77dSDavid van Moolenbroek /*
2146c46a77dSDavid van Moolenbroek * Any page that contains any valid part of the mapped device should be
2156c46a77dSDavid van Moolenbroek * readable and have correct contents for that part. If the last valid
2166c46a77dSDavid van Moolenbroek * page extends beyond the mapped device, its remainder should be zero.
2176c46a77dSDavid van Moolenbroek */
2186c46a77dSDavid van Moolenbroek if (pos < dev_size) {
2196c46a77dSDavid van Moolenbroek /* The valid part should have the expected device contents. */
2206c46a77dSDavid van Moolenbroek if (memcmp(&dev_ref[pos], ptr, expected)) e(0);
2216c46a77dSDavid van Moolenbroek
2226c46a77dSDavid van Moolenbroek /* The remainder, if any, should be zero. */
2236c46a77dSDavid van Moolenbroek for (n = expected; n % page_size; n++)
2246c46a77dSDavid van Moolenbroek if (ptr[n] != 0) e(0);
2256c46a77dSDavid van Moolenbroek }
2266c46a77dSDavid van Moolenbroek
2276c46a77dSDavid van Moolenbroek /*
2286c46a77dSDavid van Moolenbroek * Any page entirely beyond EOF should not be mapped in. In order to
2296c46a77dSDavid van Moolenbroek * ensure that is_readable() works, also test pages that are mapped in.
2306c46a77dSDavid van Moolenbroek */
2316c46a77dSDavid van Moolenbroek for (n = pos; n < pos + len; n += page_size)
2326c46a77dSDavid van Moolenbroek if (is_readable(&ptr[n - pos]) != (n < mapped_size)) e(0);
2336c46a77dSDavid van Moolenbroek
2346c46a77dSDavid van Moolenbroek munmap(ptr, len);
2356c46a77dSDavid van Moolenbroek
2366c46a77dSDavid van Moolenbroek mmap_ptr = NULL;
2376c46a77dSDavid van Moolenbroek }
2386c46a77dSDavid van Moolenbroek
2396c46a77dSDavid van Moolenbroek /*
2406c46a77dSDavid van Moolenbroek * Perform one of the supported end-of-file access attempts using one I/O
2416c46a77dSDavid van Moolenbroek * operation.
2426c46a77dSDavid van Moolenbroek */
2436c46a77dSDavid van Moolenbroek static void
do_one_io(int where,void (* io_proc)(size_t,size_t,size_t))2446c46a77dSDavid van Moolenbroek do_one_io(int where, void (* io_proc)(size_t, size_t, size_t))
2456c46a77dSDavid van Moolenbroek {
2466c46a77dSDavid van Moolenbroek size_t start, bytes;
2476c46a77dSDavid van Moolenbroek
2486c46a77dSDavid van Moolenbroek switch (where) {
2496c46a77dSDavid van Moolenbroek case BEFORE_EOF:
2506c46a77dSDavid van Moolenbroek bytes = lrand48() % (dev_size - 1) + 1;
2516c46a77dSDavid van Moolenbroek
2526c46a77dSDavid van Moolenbroek io_proc(dev_size - bytes - 1, bytes, bytes);
2536c46a77dSDavid van Moolenbroek
2546c46a77dSDavid van Moolenbroek break;
2556c46a77dSDavid van Moolenbroek
2566c46a77dSDavid van Moolenbroek case UPTO_EOF:
2576c46a77dSDavid van Moolenbroek bytes = lrand48() % dev_size + 1;
2586c46a77dSDavid van Moolenbroek
2596c46a77dSDavid van Moolenbroek io_proc(dev_size - bytes, bytes, bytes);
2606c46a77dSDavid van Moolenbroek
2616c46a77dSDavid van Moolenbroek break;
2626c46a77dSDavid van Moolenbroek
2636c46a77dSDavid van Moolenbroek case ACROSS_EOF:
2646c46a77dSDavid van Moolenbroek start = lrand48() % (dev_size - 1) + 1;
2656c46a77dSDavid van Moolenbroek bytes = dev_size - start + 1;
2666c46a77dSDavid van Moolenbroek assert(start < dev_size && start + bytes > dev_size);
2676c46a77dSDavid van Moolenbroek bytes += lrand48() % (dev_size - bytes + 1);
2686c46a77dSDavid van Moolenbroek
2696c46a77dSDavid van Moolenbroek io_proc(start, bytes, dev_size - start);
2706c46a77dSDavid van Moolenbroek
2716c46a77dSDavid van Moolenbroek break;
2726c46a77dSDavid van Moolenbroek
2736c46a77dSDavid van Moolenbroek case ONEPAST_EOF:
2746c46a77dSDavid van Moolenbroek bytes = lrand48() % (dev_size - 1) + 1;
2756c46a77dSDavid van Moolenbroek
2766c46a77dSDavid van Moolenbroek io_proc(dev_size - bytes + 1, bytes, bytes - 1);
2776c46a77dSDavid van Moolenbroek
2786c46a77dSDavid van Moolenbroek break;
2796c46a77dSDavid van Moolenbroek
2806c46a77dSDavid van Moolenbroek case FROM_EOF:
2816c46a77dSDavid van Moolenbroek bytes = lrand48() % dev_size + 1;
2826c46a77dSDavid van Moolenbroek
2836c46a77dSDavid van Moolenbroek io_proc(dev_size, bytes, 0);
2846c46a77dSDavid van Moolenbroek
2856c46a77dSDavid van Moolenbroek break;
2866c46a77dSDavid van Moolenbroek
2876c46a77dSDavid van Moolenbroek case BEYOND_EOF:
2886c46a77dSDavid van Moolenbroek start = dev_size + lrand48() % dev_size + 1;
2896c46a77dSDavid van Moolenbroek bytes = lrand48() % dev_size + 1;
2906c46a77dSDavid van Moolenbroek
2916c46a77dSDavid van Moolenbroek io_proc(start, bytes, 0);
2926c46a77dSDavid van Moolenbroek
2936c46a77dSDavid van Moolenbroek break;
2946c46a77dSDavid van Moolenbroek
2956c46a77dSDavid van Moolenbroek default:
2966c46a77dSDavid van Moolenbroek assert(0);
2976c46a77dSDavid van Moolenbroek }
2986c46a77dSDavid van Moolenbroek }
2996c46a77dSDavid van Moolenbroek
3006c46a77dSDavid van Moolenbroek /*
3016c46a77dSDavid van Moolenbroek * Perform I/O operations, testing all the supported end-of-file access
3026c46a77dSDavid van Moolenbroek * attempts in a random order so as to detect possible problems with caching.
3036c46a77dSDavid van Moolenbroek */
3046c46a77dSDavid van Moolenbroek static void
do_io(void (* io_proc)(size_t,size_t,size_t))3056c46a77dSDavid van Moolenbroek do_io(void (* io_proc)(size_t, size_t, size_t))
3066c46a77dSDavid van Moolenbroek {
3076c46a77dSDavid van Moolenbroek static const int list[] = { BEFORE_EOF, UPTO_EOF, ACROSS_EOF,
3086c46a77dSDavid van Moolenbroek ONEPAST_EOF, FROM_EOF, BEYOND_EOF };
3096c46a77dSDavid van Moolenbroek static const int count = sizeof(list) / sizeof(list[0]);
3106c46a77dSDavid van Moolenbroek int i, where[count];
3116c46a77dSDavid van Moolenbroek
3126c46a77dSDavid van Moolenbroek scramble(where, list, count);
3136c46a77dSDavid van Moolenbroek
3146c46a77dSDavid van Moolenbroek for (i = 0; i < count; i++)
3156c46a77dSDavid van Moolenbroek do_one_io(where[i], io_proc);
3166c46a77dSDavid van Moolenbroek }
3176c46a77dSDavid van Moolenbroek
3186c46a77dSDavid van Moolenbroek /*
3196c46a77dSDavid van Moolenbroek * Set up an image file of the given size, assign it to a VND, and open the
3206c46a77dSDavid van Moolenbroek * resulting block device. The size is size_t because we keep a reference copy
3216c46a77dSDavid van Moolenbroek * of its entire contents in memory.
3226c46a77dSDavid van Moolenbroek */
3236c46a77dSDavid van Moolenbroek static void
setup_image(size_t size)3246c46a77dSDavid van Moolenbroek setup_image(size_t size)
3256c46a77dSDavid van Moolenbroek {
3266c46a77dSDavid van Moolenbroek struct part_geom part;
3276c46a77dSDavid van Moolenbroek size_t off;
3286c46a77dSDavid van Moolenbroek ssize_t bytes;
3296c46a77dSDavid van Moolenbroek int fd, status;
3306c46a77dSDavid van Moolenbroek
3316c46a77dSDavid van Moolenbroek dev_size = size;
3326c46a77dSDavid van Moolenbroek if ((dev_buf = malloc(dev_size)) == NULL) e(0);
3336c46a77dSDavid van Moolenbroek if ((dev_ref = malloc(dev_size)) == NULL) e(0);
3346c46a77dSDavid van Moolenbroek
3356c46a77dSDavid van Moolenbroek if ((fd = open("image", O_CREAT | O_TRUNC | O_RDWR, 0644)) < 0) e(0);
3366c46a77dSDavid van Moolenbroek
3376c46a77dSDavid van Moolenbroek fill_buf(dev_ref, dev_size);
3386c46a77dSDavid van Moolenbroek
3396c46a77dSDavid van Moolenbroek for (off = 0; off < dev_size; off += bytes) {
3406c46a77dSDavid van Moolenbroek bytes = write(fd, &dev_ref[off], dev_size - off);
3416c46a77dSDavid van Moolenbroek
3426c46a77dSDavid van Moolenbroek if (bytes <= 0) e(0);
3436c46a77dSDavid van Moolenbroek }
3446c46a77dSDavid van Moolenbroek
3456c46a77dSDavid van Moolenbroek close(fd);
3466c46a77dSDavid van Moolenbroek
3476c46a77dSDavid van Moolenbroek status = system(VNCONFIG " vnd0 image 2>/dev/null");
3486c46a77dSDavid van Moolenbroek if (!WIFEXITED(status)) e(0);
3496c46a77dSDavid van Moolenbroek if (WEXITSTATUS(status) != 0) {
3506c46a77dSDavid van Moolenbroek printf("skipped\n"); /* most likely cause: vnd0 is in use */
3516c46a77dSDavid van Moolenbroek cleanup();
3526c46a77dSDavid van Moolenbroek exit(0);
3536c46a77dSDavid van Moolenbroek }
3546c46a77dSDavid van Moolenbroek
3556c46a77dSDavid van Moolenbroek need_cleanup = 1;
3566c46a77dSDavid van Moolenbroek
3576c46a77dSDavid van Moolenbroek if ((dev_fd = open("/dev/vnd0", O_RDWR)) < 0) e(0);
3586c46a77dSDavid van Moolenbroek
3596c46a77dSDavid van Moolenbroek if (ioctl(dev_fd, DIOCGETP, &part) < 0) e(0);
3606c46a77dSDavid van Moolenbroek
3616c46a77dSDavid van Moolenbroek if (part.size != dev_size) e(0);
3626c46a77dSDavid van Moolenbroek }
3636c46a77dSDavid van Moolenbroek
3646c46a77dSDavid van Moolenbroek /*
3656c46a77dSDavid van Moolenbroek * Clean up the VND we set up previously. This function is also called in case
3666c46a77dSDavid van Moolenbroek * of an unexpected exit.
3676c46a77dSDavid van Moolenbroek */
3686c46a77dSDavid van Moolenbroek static void
cleanup_device(void)3696c46a77dSDavid van Moolenbroek cleanup_device(void)
3706c46a77dSDavid van Moolenbroek {
3716c46a77dSDavid van Moolenbroek int status;
3726c46a77dSDavid van Moolenbroek
3736c46a77dSDavid van Moolenbroek if (!need_cleanup)
3746c46a77dSDavid van Moolenbroek return;
3756c46a77dSDavid van Moolenbroek
3766c46a77dSDavid van Moolenbroek if (mmap_ptr != NULL) {
3776c46a77dSDavid van Moolenbroek munmap(mmap_ptr, mmap_size);
3786c46a77dSDavid van Moolenbroek
3796c46a77dSDavid van Moolenbroek mmap_ptr = NULL;
3806c46a77dSDavid van Moolenbroek }
3816c46a77dSDavid van Moolenbroek
3826c46a77dSDavid van Moolenbroek if (dev_fd >= 0)
3836c46a77dSDavid van Moolenbroek close(dev_fd);
3846c46a77dSDavid van Moolenbroek
3856c46a77dSDavid van Moolenbroek status = system(VNCONFIG " -u vnd0 2>/dev/null");
3866c46a77dSDavid van Moolenbroek if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0);
3876c46a77dSDavid van Moolenbroek
3886c46a77dSDavid van Moolenbroek need_cleanup = 0;
3896c46a77dSDavid van Moolenbroek }
3906c46a77dSDavid van Moolenbroek
3916c46a77dSDavid van Moolenbroek /*
3926c46a77dSDavid van Moolenbroek * Signal handler for exceptions.
3936c46a77dSDavid van Moolenbroek */
3946c46a77dSDavid van Moolenbroek static void
got_signal(int __unused sig)3956c46a77dSDavid van Moolenbroek got_signal(int __unused sig)
3966c46a77dSDavid van Moolenbroek {
3976c46a77dSDavid van Moolenbroek
3986c46a77dSDavid van Moolenbroek cleanup_device();
3996c46a77dSDavid van Moolenbroek
4006c46a77dSDavid van Moolenbroek exit(1);
4016c46a77dSDavid van Moolenbroek }
4026c46a77dSDavid van Moolenbroek
4036c46a77dSDavid van Moolenbroek /*
4046c46a77dSDavid van Moolenbroek * Clean up the VND and image file we set up previously.
4056c46a77dSDavid van Moolenbroek */
4066c46a77dSDavid van Moolenbroek static void
cleanup_image(void)4076c46a77dSDavid van Moolenbroek cleanup_image(void)
4086c46a77dSDavid van Moolenbroek {
4096c46a77dSDavid van Moolenbroek size_t off;
4106c46a77dSDavid van Moolenbroek ssize_t bytes;
4116c46a77dSDavid van Moolenbroek int fd;
4126c46a77dSDavid van Moolenbroek
4136c46a77dSDavid van Moolenbroek cleanup_device();
4146c46a77dSDavid van Moolenbroek
4156c46a77dSDavid van Moolenbroek if ((fd = open("image", O_RDONLY, 0644)) < 0) e(0);
4166c46a77dSDavid van Moolenbroek
4176c46a77dSDavid van Moolenbroek for (off = 0; off < dev_size; off += bytes) {
4186c46a77dSDavid van Moolenbroek bytes = read(fd, &dev_buf[off], dev_size - off);
4196c46a77dSDavid van Moolenbroek
4206c46a77dSDavid van Moolenbroek if (bytes <= 0) e(0);
4216c46a77dSDavid van Moolenbroek }
4226c46a77dSDavid van Moolenbroek
4236c46a77dSDavid van Moolenbroek close(fd);
4246c46a77dSDavid van Moolenbroek
4256c46a77dSDavid van Moolenbroek /* Have all changes written back to the device? */
4266c46a77dSDavid van Moolenbroek if (memcmp(dev_buf, dev_ref, dev_size)) e(0);
4276c46a77dSDavid van Moolenbroek
4286c46a77dSDavid van Moolenbroek unlink("image");
4296c46a77dSDavid van Moolenbroek
4306c46a77dSDavid van Moolenbroek free(dev_buf);
4316c46a77dSDavid van Moolenbroek free(dev_ref);
4326c46a77dSDavid van Moolenbroek }
4336c46a77dSDavid van Moolenbroek
4346c46a77dSDavid van Moolenbroek /*
4356c46a77dSDavid van Moolenbroek * Run the full test for a block device with the given size.
4366c46a77dSDavid van Moolenbroek */
4376c46a77dSDavid van Moolenbroek static void
do_test(size_t size)4386c46a77dSDavid van Moolenbroek do_test(size_t size)
4396c46a77dSDavid van Moolenbroek {
4406c46a77dSDavid van Moolenbroek int i;
4416c46a77dSDavid van Moolenbroek
4426c46a77dSDavid van Moolenbroek /*
4436c46a77dSDavid van Moolenbroek * Using the three I/O primitives (read, write, peek), we run four
4446c46a77dSDavid van Moolenbroek * sequences, mainly to test the effects of blocks being cached or not.
4456c46a77dSDavid van Moolenbroek * We set up a new image for each sequence, because -if everything goes
4466c46a77dSDavid van Moolenbroek * right- closing the device file also clears all cached blocks for it,
4476c46a77dSDavid van Moolenbroek * in both the root file system's cache and the VM cache. Note that we
4486c46a77dSDavid van Moolenbroek * currently do not even attempt to push the blocks out of the root FS'
4496c46a77dSDavid van Moolenbroek * cache in order to test retrieval from the VM cache, since this would
4506c46a77dSDavid van Moolenbroek * involve doing a LOT of extra I/O.
4516c46a77dSDavid van Moolenbroek */
4526c46a77dSDavid van Moolenbroek for (i = 0; i < 4; i++) {
4536c46a77dSDavid van Moolenbroek setup_image(size);
4546c46a77dSDavid van Moolenbroek
4556c46a77dSDavid van Moolenbroek switch (i) {
4566c46a77dSDavid van Moolenbroek case 0:
4576c46a77dSDavid van Moolenbroek do_io(io_read);
4586c46a77dSDavid van Moolenbroek
4596c46a77dSDavid van Moolenbroek /* FALLTHROUGH */
4606c46a77dSDavid van Moolenbroek case 1:
4616c46a77dSDavid van Moolenbroek do_io(io_write);
4626c46a77dSDavid van Moolenbroek
4636c46a77dSDavid van Moolenbroek do_io(io_read);
4646c46a77dSDavid van Moolenbroek
4656c46a77dSDavid van Moolenbroek break;
4666c46a77dSDavid van Moolenbroek
4676c46a77dSDavid van Moolenbroek case 2:
4686c46a77dSDavid van Moolenbroek do_io(io_peek);
4696c46a77dSDavid van Moolenbroek
4706c46a77dSDavid van Moolenbroek /* FALLTHROUGH */
4716c46a77dSDavid van Moolenbroek
4726c46a77dSDavid van Moolenbroek case 3:
4736c46a77dSDavid van Moolenbroek do_io(io_write);
4746c46a77dSDavid van Moolenbroek
4756c46a77dSDavid van Moolenbroek do_io(io_peek);
4766c46a77dSDavid van Moolenbroek
4776c46a77dSDavid van Moolenbroek break;
4786c46a77dSDavid van Moolenbroek }
4796c46a77dSDavid van Moolenbroek
4806c46a77dSDavid van Moolenbroek cleanup_image();
4816c46a77dSDavid van Moolenbroek }
4826c46a77dSDavid van Moolenbroek }
4836c46a77dSDavid van Moolenbroek
4846c46a77dSDavid van Moolenbroek /*
4856c46a77dSDavid van Moolenbroek * Test program for end-of-file conditions during block device I/O.
4866c46a77dSDavid van Moolenbroek */
4876c46a77dSDavid van Moolenbroek int
main(void)4886c46a77dSDavid van Moolenbroek main(void)
4896c46a77dSDavid van Moolenbroek {
4906c46a77dSDavid van Moolenbroek static const unsigned int blocks[] = { 1, 4, 3, 5, 2 };
4916c46a77dSDavid van Moolenbroek struct statvfs buf;
4926c46a77dSDavid van Moolenbroek int i, j;
4936c46a77dSDavid van Moolenbroek
4946c46a77dSDavid van Moolenbroek start(85);
4956c46a77dSDavid van Moolenbroek
496*0a6a1f1dSLionel Sambuc setuid(geteuid());
497*0a6a1f1dSLionel Sambuc
4986c46a77dSDavid van Moolenbroek signal(SIGINT, got_signal);
4996c46a77dSDavid van Moolenbroek signal(SIGABRT, got_signal);
5006c46a77dSDavid van Moolenbroek signal(SIGSEGV, got_signal);
5016c46a77dSDavid van Moolenbroek signal(SIGBUS, got_signal);
5026c46a77dSDavid van Moolenbroek atexit(cleanup_device);
5036c46a77dSDavid van Moolenbroek
5046c46a77dSDavid van Moolenbroek srand48(time(NULL));
5056c46a77dSDavid van Moolenbroek
5066c46a77dSDavid van Moolenbroek if (pipe(pipe_fd) != 0) e(0);
5076c46a77dSDavid van Moolenbroek
5086c46a77dSDavid van Moolenbroek /*
5096c46a77dSDavid van Moolenbroek * Get the system page size, and align all memory mapping offsets and
5106c46a77dSDavid van Moolenbroek * sizes accordingly.
5116c46a77dSDavid van Moolenbroek */
5126c46a77dSDavid van Moolenbroek page_size = sysconf(_SC_PAGESIZE);
5136c46a77dSDavid van Moolenbroek
5146c46a77dSDavid van Moolenbroek /*
5156c46a77dSDavid van Moolenbroek * Get the root file system block size. In the current MINIX3 system
5166c46a77dSDavid van Moolenbroek * architecture, the root file system's block size determines the
5176c46a77dSDavid van Moolenbroek * transfer granularity for I/O on unmounted block devices. If this
5186c46a77dSDavid van Moolenbroek * block size is not a multiple of the page size, we are (currently!)
5196c46a77dSDavid van Moolenbroek * not expecting memory-mapped block devices to work.
5206c46a77dSDavid van Moolenbroek */
5216c46a77dSDavid van Moolenbroek if (statvfs("/", &buf) < 0) e(0);
5226c46a77dSDavid van Moolenbroek
5236c46a77dSDavid van Moolenbroek block_size = buf.f_bsize;
5246c46a77dSDavid van Moolenbroek
5256c46a77dSDavid van Moolenbroek test_peek = !(block_size % page_size);
5266c46a77dSDavid van Moolenbroek
5276c46a77dSDavid van Moolenbroek for (i = 0; i < ITERATIONS; i++) {
5286c46a77dSDavid van Moolenbroek /*
5296c46a77dSDavid van Moolenbroek * The 'blocks' array is scrambled so as to detect any blocks
5306c46a77dSDavid van Moolenbroek * left in the VM cache (or not) across runs, just in case.
5316c46a77dSDavid van Moolenbroek */
5326c46a77dSDavid van Moolenbroek for (j = 0; j < sizeof(blocks) / sizeof(blocks[0]); j++) {
5336c46a77dSDavid van Moolenbroek do_test(blocks[j] * block_size + SECTOR_SIZE);
5346c46a77dSDavid van Moolenbroek
5356c46a77dSDavid van Moolenbroek do_test(blocks[j] * block_size);
5366c46a77dSDavid van Moolenbroek
5376c46a77dSDavid van Moolenbroek do_test(blocks[j] * block_size - SECTOR_SIZE);
5386c46a77dSDavid van Moolenbroek }
5396c46a77dSDavid van Moolenbroek }
5406c46a77dSDavid van Moolenbroek
5416c46a77dSDavid van Moolenbroek quit();
5426c46a77dSDavid van Moolenbroek }
543