/* Block Device Driver Test driver, by D.C. van Moolenbroek */
#include <stdlib.h>
+#include <stdarg.h>
#include <minix/blockdriver.h>
#include <minix/drvlib.h>
#include <minix/ds.h>
#include <minix/optset.h>
#include <sys/ioc_disk.h>
+#include <sys/mman.h>
#include <assert.h>
enum {
* it to a value lower than the driver supports.
*/
+/* These settings are used for automated test runs. */
+static int contig = TRUE; /* allocate contiguous DMA memory? */
+static int silent = FALSE; /* do not produce console output? */
+
static struct part_geom part; /* base and size of target partition */
#define NR_OPENED 10 /* maximum number of opened devices */
{ "min_read", OPT_INT, &min_read, 10 },
{ "min_write", OPT_INT, &min_write, 10 },
{ "max", OPT_INT, &max_size, 10 },
+ { "nocontig", OPT_BOOL, &contig, FALSE },
+ { "silent", OPT_BOOL, &silent, TRUE },
{ NULL, 0, NULL, 0 }
};
+static void output(char *fmt, ...)
+{
+ /* Print debugging information, unless configured to be silent.
+ */
+ va_list argp;
+
+ if (silent)
+ return;
+
+ va_start(argp, fmt);
+
+ vprintf(fmt, argp);
+
+ va_end(argp);
+}
+
+static void *alloc_dma_memory(size_t size)
+{
+ /* Allocate memory that may be used for direct DMA. For most drivers,
+ * this means that the memory has to be physically contiguous. For some
+ * drivers (e.g. VND) we allow non-contiguous allocation, because VM is
+ * currently flaky and does not always manage to provide contiguous
+ * memory even when it should, thus causing needless test failures.
+ */
+ void *ptr;
+
+ if (contig)
+ ptr = alloc_contig(size, 0, NULL);
+ else
+ ptr = minix_mmap(NULL, size, PROT_READ | PROT_WRITE,
+ MAP_PREALLOC | MAP_ANON, -1, 0);
+
+ if (ptr == MAP_FAILED)
+ panic("unable to allocate %d bytes of memory", size);
+
+ return ptr;
+}
+
+static void free_dma_memory(void *ptr, size_t size)
+{
+ /* Free memory previously allocated for direct DMA. */
+ if (contig)
+ free_contig(ptr, size);
+ else
+ minix_munmap(ptr, size);
+}
+
static int set_result(result_t *res, int type, ssize_t value)
{
/* Set the result to the given result type and with the given optional
}
}
- printf("#%02d: %-38s\t[%s]\n", ++i, desc,
+ output("#%02d: %-38s\t[%s]\n", ++i, desc,
(res->type == RESULT_OK) ? "PASS" : "FAIL");
switch (res->type) {
case RESULT_DEATH:
- printf("- driver died\n");
+ output("- driver died\n");
break;
case RESULT_COMMFAIL:
- printf("- communication failed; sendrec returned %d\n",
+ output("- communication failed; sendrec returned %d\n",
res->value);
break;
case RESULT_BADTYPE:
- printf("- bad type %d in reply message\n", res->value);
+ output("- bad type %d in reply message\n", res->value);
break;
case RESULT_BADID:
- printf("- mismatched ID %d in reply message\n", res->value);
+ output("- mismatched ID %d in reply message\n", res->value);
break;
case RESULT_BADSTATUS:
- printf("- bad or unexpected status %d in reply message\n",
+ output("- bad or unexpected status %d in reply message\n",
res->value);
break;
case RESULT_TRUNC:
- printf("- result size not as expected (%u bytes left)\n",
+ output("- result size not as expected (%u bytes left)\n",
res->value);
break;
case RESULT_CORRUPT:
- printf("- buffer has been modified erroneously\n");
+ output("- buffer has been modified erroneously\n");
break;
case RESULT_MISSING:
- printf("- buffer has been left untouched erroneously\n");
+ output("- buffer has been left untouched erroneously\n");
break;
case RESULT_OVERFLOW:
- printf("- area around target buffer modified\n");
+ output("- area around target buffer modified\n");
break;
case RESULT_BADVALUE:
- printf("- bad or unexpected return value %d from call\n",
+ output("- bad or unexpected return value %d from call\n",
res->value);
break;
}
/* Start a new group of tests.
*/
- printf("Test group: %s%s\n", name, exec ? "" : " (skipping)");
+ output("Test group: %s%s\n", name, exec ? "" : " (skipping)");
group_failure = FALSE;
}
/* The driver has died. Find its new endpoint, and reopen all
* devices that we opened earlier. Then return failure.
*/
- printf("WARNING: driver has died, attempting to proceed\n");
+ output("WARNING: driver has died, attempting to proceed\n");
driver_deaths++;
* grant for it with the requested CPF_* grant permissions.
*/
- *ptr = alloc_contig(size, 0, NULL);
- if (*ptr == NULL)
- panic("unable to allocate memory");
+ *ptr = alloc_dma_memory(size);
if ((*grant = cpf_grant_direct(driver_endpt, (vir_bytes) *ptr, size,
perms)) == GRANT_INVALID)
cpf_revoke(grant);
- free_contig(ptr, size);
+ free_dma_memory(ptr, size);
}
static void bad_read1(void)
buf_size = large_size + sizeof(u32_t) * 2;
buf2_size = large_size + sizeof(u32_t) * (NR_IOREQS + 1);
- buf_ptr = alloc_contig(buf_size, 0, NULL);
- buf2_ptr = alloc_contig(buf2_size, 0, NULL);
- if (buf_ptr == NULL || buf2_ptr == NULL)
- panic("unable to allocate memory");
+ buf_ptr = alloc_dma_memory(buf_size);
+ buf2_ptr = alloc_dma_memory(buf2_size);
/* The first buffer has one large chunk with dword-sized guards on each
* side. LPTR(n) points to the start of the nth small data chunk within
#undef SPTR
/* Clean up. */
- free_contig(buf2_ptr, buf2_size);
- free_contig(buf_ptr, buf_size);
+ free_dma_memory(buf2_ptr, buf2_size);
+ free_dma_memory(buf_ptr, buf_size);
}
static void vector_and_large(void)
*/
size_t max_block;
+ /* Make sure that the maximum size does not exceed the target device
+ * size, minus the margins we need for testing here and there.
+ */
+ if (max_size > part.size - sector_size * 4)
+ max_size = part.size - sector_size * 4;
+
/* Compute the largest sector multiple which, when multiplied by
* NR_IOREQS, is no more than the maximum transfer size. Note that if
* max_size is not a multiple of sector_size, we're not going up to the
/* The other tests do not check whether there is sufficient room. */
if (res.type == RESULT_OK && part.size < (u64_t)max_size * 2)
- printf("WARNING: small partition, some tests may fail\n");
+ output("WARNING: small partition, some tests may fail\n");
/* Test retrieving global driver open count. */
openct = 0x0badcafe;
test_group("read around subpartition limits", TRUE);
buf_size = sector_size * 3;
-
- if ((buf_ptr = alloc_contig(buf_size, 0, NULL)) == NULL)
- panic("unable to allocate memory");
+ buf_ptr = alloc_dma_memory(buf_size);
/* Read one sector up to the partition limit. */
fill_rand(buf_ptr, buf_size);
got_result(&res, "read with negative offset");
/* Clean up. */
- free_contig(buf_ptr, buf_size);
+ free_dma_memory(buf_ptr, buf_size);
}
static void write_limits(dev_t sub0_minor, dev_t sub1_minor, size_t sub_size)
return;
buf_size = sector_size * 3;
-
- if ((buf_ptr = alloc_contig(buf_size, 0, NULL)) == NULL)
- panic("unable to allocate memory");
+ buf_ptr = alloc_dma_memory(buf_size);
/* Write to the start of the second subpartition, so that we can
* reliably check whether the contents have changed later.
got_result(&res, "read up to partition end");
/* Clean up. */
- free_contig(buf_ptr, buf_size);
+ free_dma_memory(buf_ptr, buf_size);
}
static void vir_limits(dev_t sub0_minor, dev_t sub1_minor, int part_secs)
* anyway.
*/
buf_size = sector_size;
-
- if ((buf_ptr = alloc_contig(buf_size, 0, NULL)) == NULL)
- panic("unable to allocate memory");
+ buf_ptr = alloc_dma_memory(buf_size);
memset(buf_ptr, 0, buf_size);
close_device(sub0_minor);
close_device(sub1_minor);
- free_contig(buf_ptr, buf_size);
+ free_dma_memory(buf_ptr, buf_size);
}
static void part_limits(void)
* cannot conduct this test.
*/
if (driver_minor >= MINOR_d0p0s0) {
- printf("WARNING: operating on subpartition, "
+ output("WARNING: operating on subpartition, "
"skipping partition tests\n");
return;
}
base_pos = (u64_t)sector_size * 2;
- if ((buf_ptr = alloc_contig(buf_size, 0, NULL)) == NULL)
- panic("unable to allocate memory");
-
- if ((sec_ptr[0] = alloc_contig(sector_size, 0, NULL)) == NULL)
- panic("unable to allocate memory");
-
- if ((sec_ptr[1] = alloc_contig(sector_size, 0, NULL)) == NULL)
- panic("unable to allocate memory");
+ buf_ptr = alloc_dma_memory(buf_size);
+ sec_ptr[0] = alloc_dma_memory(sector_size);
+ sec_ptr[1] = alloc_dma_memory(sector_size);
if (may_write) {
sum = fill_rand(buf_ptr, buf_size);
}
/* Clean up. */
- free_contig(sec_ptr[1], sector_size);
- free_contig(sec_ptr[0], sector_size);
- free_contig(buf_ptr, buf_size);
+ free_dma_memory(sec_ptr[1], sector_size);
+ free_dma_memory(sec_ptr[0], sector_size);
+ free_dma_memory(buf_ptr, buf_size);
}
static void unaligned_pos1(void)
base_pos = (u64_t)sector_size * 3;
- if ((buf_ptr = alloc_contig(buf_size, 0, NULL)) == NULL)
- panic("unable to allocate memory");
-
- if ((buf2_ptr = alloc_contig(buf2_size, 0, NULL)) == NULL)
- panic("unable to allocate memory");
+ buf_ptr = alloc_dma_memory(buf_size);
+ buf2_ptr = alloc_dma_memory(buf2_size);
if (may_write) {
sum = fill_rand(buf_ptr, buf_size);
got_result(&res, "multisector read with lead and trail");
/* Clean up. */
- free_contig(buf2_ptr, buf2_size);
- free_contig(buf_ptr, buf_size);
+ free_dma_memory(buf2_ptr, buf2_size);
+ free_dma_memory(buf_ptr, buf_size);
}
static void unaligned_pos2(void)
base_pos = (u64_t)sector_size * 3;
- if ((buf_ptr = alloc_contig(buf_size, 0, NULL)) == NULL)
- panic("unable to allocate memory");
-
- if ((buf2_ptr = alloc_contig(buf2_size, 0, NULL)) == NULL)
- panic("unable to allocate memory");
+ buf_ptr = alloc_dma_memory(buf_size);
+ buf2_ptr = alloc_dma_memory(buf2_size);
/* First establish a baseline. We need two requests for this, as the
* total area intentionally exceeds the max request size.
got_result(&res, "large fully unaligned filled vector");
/* Clean up. */
- free_contig(buf2_ptr, buf2_size);
- free_contig(buf_ptr, buf_size);
+ free_dma_memory(buf2_ptr, buf2_size);
+ free_dma_memory(buf_ptr, buf_size);
}
static void sweep_area(u64_t base_pos)
int i, j;
buf_size = sector_size * 8;
-
- if ((buf_ptr = alloc_contig(buf_size, 0, NULL)) == NULL)
- panic("unable to allocate memory");
+ buf_ptr = alloc_dma_memory(buf_size);
/* First (write to, if allowed, and) read from the entire area in one
* go, so that we know the (initial) contents of the area.
}
/* Clean up. */
- free_contig(buf_ptr, buf_size);
+ free_dma_memory(buf_ptr, buf_size);
}
static void sweep_and_check(u64_t pos, int check_integ)
if (check_integ) {
buf_size = sector_size * 3;
-
- if ((buf_ptr = alloc_contig(buf_size, 0, NULL)) == NULL)
- panic("unable to allocate memory");
+ buf_ptr = alloc_dma_memory(buf_size);
if (may_write) {
sum = fill_rand(buf_ptr, buf_size);
got_result(&res, "check integrity zone");
- free_contig(buf_ptr, buf_size);
+ free_dma_memory(buf_ptr, buf_size);
}
}
srand48(now);
- return OK;
+ output("BLOCKTEST: driver label '%s' (endpt %d), minor %d\n",
+ driver_label, driver_endpt, driver_minor);
+
+ do_tests();
+
+ output("BLOCKTEST: summary: %d out of %d tests failed "
+ "across %d group%s; %d driver deaths\n",
+ failed_tests, total_tests, failed_groups,
+ failed_groups == 1 ? "" : "s", driver_deaths);
+
+ /* The returned code will determine the outcome of the RS call, and
+ * thus the entire test. The actual error code does not matter.
+ */
+ return (failed_tests) ? EINVAL : OK;
}
static void sef_local_startup(void)
env_setargs(argc, argv);
sef_local_startup();
- printf("BLOCKTEST: driver label '%s' (endpt %d), minor %d\n",
- driver_label, driver_endpt, driver_minor);
-
- do_tests();
-
- printf("BLOCKTEST: summary: %d out of %d tests failed "
- "across %d group%s; %d driver deaths\n",
- failed_tests, total_tests, failed_groups,
- failed_groups == 1 ? "" : "s", driver_deaths);
-
return 0;
}