302 lines
8.1 KiB
C
302 lines
8.1 KiB
C
#include <minix/drivers.h>
|
|
#include <sys/ioc_fbd.h>
|
|
#include <assert.h>
|
|
|
|
#include "rule.h"
|
|
|
|
/*===========================================================================*
|
|
* get_rand *
|
|
*===========================================================================*/
|
|
static u32_t get_rand(u32_t max)
|
|
{
|
|
/* Las Vegas algorithm for getting a random number in the range from
|
|
* 0 to max, inclusive.
|
|
*/
|
|
u32_t val, top;
|
|
|
|
/* Get an initial random number. */
|
|
val = lrand48() ^ (lrand48() << 1);
|
|
|
|
/* Make 'max' exclusive. If it wraps, we can use the full width. */
|
|
if (++max == 0) return val;
|
|
|
|
/* Find the largest multiple of the given range, and return a random
|
|
* number from the range, throwing away any random numbers not below
|
|
* this largest multiple.
|
|
*/
|
|
top = (((u32_t) -1) / max) * max;
|
|
|
|
while (val >= top)
|
|
val = lrand48() ^ (lrand48() << 1);
|
|
|
|
return val % max;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* get_range *
|
|
*===========================================================================*/
|
|
static size_t get_range(struct fbd_rule *rule, u64_t pos, size_t *size,
|
|
u64_t *skip)
|
|
{
|
|
/* Compute the range within the given request range that is affected
|
|
* by the given rule, and optionally the number of bytes preceding
|
|
* the range that are also affected by the rule.
|
|
*/
|
|
u64_t delta;
|
|
size_t off;
|
|
int to_eof;
|
|
|
|
to_eof = rule->start >= rule->end;
|
|
|
|
if (pos > rule->start) {
|
|
if (skip != NULL) *skip = pos - rule->start;
|
|
|
|
off = 0;
|
|
}
|
|
else {
|
|
if (skip != NULL) *skip = ((u64_t)(0));
|
|
|
|
delta = rule->start - pos;
|
|
|
|
assert(ex64hi(delta) == 0);
|
|
|
|
off = ex64lo(delta);
|
|
}
|
|
|
|
if (!to_eof) {
|
|
assert(pos < rule->end);
|
|
|
|
delta = rule->end - pos;
|
|
|
|
if (delta < *size)
|
|
*size = ex64lo(delta);
|
|
}
|
|
|
|
assert(*size > off);
|
|
|
|
*size -= off;
|
|
|
|
return off;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* limit_range *
|
|
*===========================================================================*/
|
|
static void limit_range(iovec_t *iov, unsigned *count, size_t size)
|
|
{
|
|
/* Limit the given vector to the given size.
|
|
*/
|
|
size_t chunk;
|
|
int i;
|
|
|
|
for (i = 0; i < *count && size > 0; i++) {
|
|
chunk = MIN(iov[i].iov_size, size);
|
|
|
|
if (chunk == size)
|
|
iov[i].iov_size = size;
|
|
|
|
size -= chunk;
|
|
}
|
|
|
|
*count = i;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* action_io_corrupt *
|
|
*===========================================================================*/
|
|
static void action_io_corrupt(struct fbd_rule *rule, char *buf, size_t size,
|
|
u64_t pos, int UNUSED(flag))
|
|
{
|
|
u64_t skip;
|
|
u32_t val;
|
|
|
|
buf += get_range(rule, pos, &size, &skip);
|
|
|
|
switch (rule->params.corrupt.type) {
|
|
case FBD_CORRUPT_ZERO:
|
|
memset(buf, 0, size);
|
|
break;
|
|
|
|
case FBD_CORRUPT_PERSIST:
|
|
/* Non-dword-aligned positions and sizes are not supported;
|
|
* not by us, and not by the driver.
|
|
*/
|
|
if (ex64lo(pos) & (sizeof(val) - 1)) break;
|
|
if (size & (sizeof(val) - 1)) break;
|
|
|
|
/* Consistently produce the same pattern for the same range. */
|
|
val = ex64lo(skip);
|
|
|
|
for ( ; size >= sizeof(val); size -= sizeof(val)) {
|
|
*((u32_t *) buf) = val ^ 0xdeadbeefUL;
|
|
|
|
val += sizeof(val);
|
|
buf += sizeof(val);
|
|
}
|
|
|
|
break;
|
|
|
|
case FBD_CORRUPT_RANDOM:
|
|
while (size--)
|
|
*buf++ = get_rand(255);
|
|
|
|
break;
|
|
|
|
default:
|
|
printf("FBD: unknown corruption type %d\n",
|
|
rule->params.corrupt.type);
|
|
}
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* action_pre_error *
|
|
*===========================================================================*/
|
|
static void action_pre_error(struct fbd_rule *rule, iovec_t *iov,
|
|
unsigned *count, size_t *size, u64_t *pos)
|
|
{
|
|
/* Limit the request to the part that precedes the matched range. */
|
|
*size = get_range(rule, *pos, size, NULL);
|
|
|
|
limit_range(iov, count, *size);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* action_post_error *
|
|
*===========================================================================*/
|
|
static void action_post_error(struct fbd_rule *rule, size_t UNUSED(osize),
|
|
int *result)
|
|
{
|
|
/* Upon success of the first part, return the specified error code. */
|
|
if (*result >= 0 && rule->params.error.code != OK)
|
|
*result = rule->params.error.code;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* action_pre_misdir *
|
|
*===========================================================================*/
|
|
static void action_pre_misdir(struct fbd_rule *rule, iovec_t *UNUSED(iov),
|
|
unsigned *UNUSED(count), size_t *UNUSED(size), u64_t *pos)
|
|
{
|
|
/* Randomize the request position to fall within the range (and have
|
|
* the alignment) given by the rule.
|
|
*/
|
|
u32_t range, choice;
|
|
|
|
/* Unfortunately, we cannot interpret 0 as end as "up to end of disk"
|
|
* here, because we have no idea about the actual disk size, and the
|
|
* resulting address must of course be valid..
|
|
*/
|
|
range = ((rule->params.misdir.end - rule->params.misdir.start) + 1)
|
|
/ rule->params.misdir.align;
|
|
|
|
if (range > 0)
|
|
choice = get_rand(range - 1);
|
|
else
|
|
choice = 0;
|
|
|
|
*pos = rule->params.misdir.start +
|
|
((u64_t)choice * rule->params.misdir.align);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* action_pre_losttorn *
|
|
*===========================================================================*/
|
|
static void action_pre_losttorn(struct fbd_rule *rule, iovec_t *iov,
|
|
unsigned *count, size_t *size, u64_t *UNUSED(pos))
|
|
{
|
|
if (*size > rule->params.losttorn.lead)
|
|
*size = rule->params.losttorn.lead;
|
|
|
|
limit_range(iov, count, *size);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* action_post_losttorn *
|
|
*===========================================================================*/
|
|
static void action_post_losttorn(struct fbd_rule *UNUSED(rule), size_t osize,
|
|
int *result)
|
|
{
|
|
/* On success, pretend full completion. */
|
|
|
|
if (*result < 0) return;
|
|
|
|
*result = osize;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* action_mask *
|
|
*===========================================================================*/
|
|
int action_mask(struct fbd_rule *rule)
|
|
{
|
|
/* Return the hook mask for the given rule's action type. */
|
|
|
|
switch (rule->action) {
|
|
case FBD_ACTION_CORRUPT: return IO_HOOK;
|
|
case FBD_ACTION_ERROR: return PRE_HOOK | POST_HOOK;
|
|
case FBD_ACTION_MISDIR: return PRE_HOOK;
|
|
case FBD_ACTION_LOSTTORN: return PRE_HOOK | POST_HOOK;
|
|
default:
|
|
printf("FBD: unknown action type %d\n", rule->action);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* action_pre_hook *
|
|
*===========================================================================*/
|
|
void action_pre_hook(struct fbd_rule *rule, iovec_t *iov,
|
|
unsigned *count, size_t *size, u64_t *pos)
|
|
{
|
|
switch (rule->action) {
|
|
case FBD_ACTION_ERROR:
|
|
action_pre_error(rule, iov, count, size, pos);
|
|
break;
|
|
|
|
case FBD_ACTION_MISDIR:
|
|
action_pre_misdir(rule, iov, count, size, pos);
|
|
break;
|
|
|
|
case FBD_ACTION_LOSTTORN:
|
|
action_pre_losttorn(rule, iov, count, size, pos);
|
|
break;
|
|
|
|
default:
|
|
printf("FBD: bad action type %d for PRE hook\n", rule->action);
|
|
}
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* action_io_hook *
|
|
*===========================================================================*/
|
|
void action_io_hook(struct fbd_rule *rule, char *buf, size_t size,
|
|
u64_t pos, int flag)
|
|
{
|
|
switch (rule->action) {
|
|
case FBD_ACTION_CORRUPT:
|
|
action_io_corrupt(rule, buf, size, pos, flag);
|
|
break;
|
|
|
|
default:
|
|
printf("FBD: bad action type %d for IO hook\n", rule->action);
|
|
}
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* action_post_hook *
|
|
*===========================================================================*/
|
|
void action_post_hook(struct fbd_rule *rule, size_t osize, int *result)
|
|
{
|
|
switch (rule->action) {
|
|
case FBD_ACTION_ERROR:
|
|
action_post_error(rule, osize, result);
|
|
return;
|
|
|
|
case FBD_ACTION_LOSTTORN:
|
|
action_post_losttorn(rule, osize, result);
|
|
return;
|
|
|
|
default:
|
|
printf("FBD: bad action type %d for POST hook\n",
|
|
rule->action);
|
|
}
|
|
}
|