Add fbd -- Faulty Block Device driver

This driver can be loaded as an overlay on top of a real block
device, and can then be used to generate block-level failures for
certain transfer requests. Specifically, a rule-based system allows
the user to introduce (overt and silent) data corruption and errors.

It exposes itself through /dev/fbd, and a file system can be mounted
on top of it. The new fbdctl(8) tool can be used to control the
driver; see ``man fbdctl'' for details. It also comes with a test
set, located in test/fbdtest.
This commit is contained in:
David van Moolenbroek
2011-12-11 19:32:13 +01:00
parent f65e531ee4
commit e7db2d3588
24 changed files with 1925 additions and 9 deletions

View File

@@ -12,7 +12,7 @@ SUBDIR= at_wini bios_wini floppy log tty pci .WAIT ramdisk .WAIT memory
# memory driver must be last for ramdisk image
SUBDIR+= ahci amddev atl2 at_wini audio bios_wini dec21140A dp8390 dpeth \
e1000 filter floppy fxp hello lance log orinoco pci printer \
e1000 fbd filter floppy fxp hello lance log orinoco pci printer \
random readclock rtl8139 rtl8169 ti1225 tty vbox acpi \
.WAIT ramdisk .WAIT memory

22
drivers/fbd/Makefile Normal file
View File

@@ -0,0 +1,22 @@
# Makefile for the Faulty Block Device (FBD)
.include <bsd.own.mk>
PROG= fbd
SRCS= fbd.c rule.c action.c
DPADD+= ${LIBBLOCKDRIVER} ${LIBSYS}
LDADD+= -lblockdriver -lsys -lc
CPPFLAGS+= -DDEBUG=0
# The FBD driver requires NetBSD libc.
.if ${COMPILER_TYPE} != "gnu"
CC:=clang
COMPILER_TYPE:=gnu
.endif
MAN=
BINDIR?= /usr/sbin
.include <minix.service.mk>

302
drivers/fbd/action.c Normal file
View File

@@ -0,0 +1,302 @@
#include <minix/drivers.h>
#include <sys/ioc_fbd.h>
#include <assert.h>
#include "rule.h"
/*===========================================================================*
* get_rand *
*===========================================================================*/
PRIVATE 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 *
*===========================================================================*/
PRIVATE 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 = cmp64(rule->start, rule->end) >= 0;
if (cmp64(pos, rule->start) > 0) {
if (skip != NULL) *skip = sub64(pos, rule->start);
off = 0;
}
else {
if (skip != NULL) *skip = cvu64(0);
delta = sub64(rule->start, pos);
assert(ex64hi(delta) == 0);
off = ex64lo(delta);
}
if (!to_eof) {
assert(cmp64(pos, rule->end) < 0);
delta = sub64(rule->end, pos);
if (cmp64u(delta, *size) < 0)
*size = ex64lo(delta);
}
assert(*size > off);
*size -= off;
return off;
}
/*===========================================================================*
* limit_range *
*===========================================================================*/
PRIVATE 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 *
*===========================================================================*/
PRIVATE 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 *
*===========================================================================*/
PRIVATE 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 *
*===========================================================================*/
PRIVATE 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 *
*===========================================================================*/
PRIVATE 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 = div64u(add64u(sub64(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 = add64(rule->params.misdir.start,
mul64u(choice, rule->params.misdir.align));
}
/*===========================================================================*
* action_pre_losttorn *
*===========================================================================*/
PRIVATE 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 *
*===========================================================================*/
PRIVATE 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 *
*===========================================================================*/
PUBLIC 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 *
*===========================================================================*/
PUBLIC 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 *
*===========================================================================*/
PUBLIC 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 *
*===========================================================================*/
PUBLIC 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);
}
}

12
drivers/fbd/action.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef _FBD_ACTION_H
#define _FBD_ACTION_H
extern int action_mask(struct fbd_rule *rule);
extern void action_pre_hook(struct fbd_rule *rule, iovec_t *iov,
unsigned *count, size_t *size, u64_t *pos);
extern void action_io_hook(struct fbd_rule *rule, char *buf, size_t size,
u64_t pos, int flag);
extern void action_post_hook(struct fbd_rule *rule, size_t osize, int *result);
#endif /* _FBD_ACTION_H */

456
drivers/fbd/fbd.c Normal file
View File

@@ -0,0 +1,456 @@
/* Faulty Block Device (fault injection proxy), by D.C. van Moolenbroek */
#include <stdlib.h>
#include <minix/drivers.h>
#include <minix/blockdriver.h>
#include <minix/drvlib.h>
#include <minix/ioctl.h>
#include <sys/ioc_fbd.h>
#include <minix/ds.h>
#include <minix/optset.h>
#include <assert.h>
#include "rule.h"
/* Constants. */
#define BUF_SIZE (NR_IOREQS * CLICK_SIZE) /* 256k */
/* Function declarations. */
PRIVATE int fbd_open(dev_t minor, int access);
PRIVATE int fbd_close(dev_t minor);
PRIVATE int fbd_transfer(dev_t minor, int do_write, u64_t position,
endpoint_t endpt, iovec_t *iov, unsigned int nr_req, int flags);
PRIVATE int fbd_ioctl(dev_t minor, unsigned int request, endpoint_t endpt,
cp_grant_id_t grant);
/* Variables. */
PRIVATE char *fbd_buf; /* scratch buffer */
PRIVATE char driver_label[32] = ""; /* driver DS label */
PRIVATE dev_t driver_minor = -1; /* driver's partition minor to use */
PRIVATE endpoint_t driver_endpt; /* driver endpoint */
/* Entry points to this driver. */
PRIVATE struct blockdriver fbd_dtab = {
BLOCKDRIVER_TYPE_OTHER, /* do not handle partition requests */
fbd_open, /* open or mount request, initialize device */
fbd_close, /* release device */
fbd_transfer, /* do the I/O */
fbd_ioctl, /* perform I/O control request */
NULL, /* nothing to clean up */
NULL, /* we will not be asked about partitions */
NULL, /* we will not be asked for geometry */
NULL, /* ignore leftover hardware interrupts */
NULL, /* ignore alarms */
NULL, /* ignore other messages */
NULL /* no multithreading support */
};
/* Options supported by this driver. */
PRIVATE struct optset optset_table[] = {
{ "label", OPT_STRING, driver_label, sizeof(driver_label) },
{ "minor", OPT_INT, &driver_minor, 10 },
{ NULL, 0, NULL, 0 }
};
/*===========================================================================*
* sef_cb_init_fresh *
*===========================================================================*/
PRIVATE int sef_cb_init_fresh(int type, sef_init_info_t *UNUSED(info))
{
clock_t uptime;
int r;
/* Parse the given parameters. */
if (env_argc > 1)
optset_parse(optset_table, env_argv[1]);
if (driver_label[0] == '\0')
panic("no driver label given");
if (ds_retrieve_label_endpt(driver_label, &driver_endpt))
panic("unable to resolve driver label");
if (driver_minor > 255)
panic("no or invalid driver minor given");
#if DEBUG
printf("FBD: driver label '%s' (endpt %d), minor %d\n",
driver_label, driver_endpt, driver_minor);
#endif
/* Initialize resources. */
fbd_buf = alloc_contig(BUF_SIZE, 0, NULL);
assert(fbd_buf != NULL);
if ((r = getuptime(&uptime)) != OK)
panic("getuptime failed (%d)\n", r);
srand48(uptime);
/* Announce we are up! */
blockdriver_announce(type);
return OK;
}
/*===========================================================================*
* sef_cb_signal_handler *
*===========================================================================*/
PRIVATE void sef_cb_signal_handler(int signo)
{
/* Terminate immediately upon receiving a SIGTERM. */
if (signo != SIGTERM) return;
#if DEBUG
printf("FBD: shutting down\n");
#endif
/* Clean up resources. */
free_contig(fbd_buf, BUF_SIZE);
exit(0);
}
/*===========================================================================*
* sef_local_startup *
*===========================================================================*/
PRIVATE void sef_local_startup(void)
{
/* Register init callbacks. */
sef_setcb_init_fresh(sef_cb_init_fresh);
sef_setcb_init_restart(sef_cb_init_fresh);
sef_setcb_init_lu(sef_cb_init_fresh);
/* Register signal callback. */
sef_setcb_signal_handler(sef_cb_signal_handler);
/* Let SEF perform startup. */
sef_startup();
}
/*===========================================================================*
* main *
*===========================================================================*/
PUBLIC int main(int argc, char **argv)
{
/* SEF local startup. */
env_setargs(argc, argv);
sef_local_startup();
/* Call the generic receive loop. */
blockdriver_task(&fbd_dtab);
return OK;
}
/*===========================================================================*
* fbd_open *
*===========================================================================*/
PRIVATE int fbd_open(dev_t UNUSED(minor), int access)
{
/* Open a device. */
message m;
int r;
/* We simply forward this request to the real driver. */
memset(&m, 0, sizeof(m));
m.m_type = BDEV_OPEN;
m.BDEV_MINOR = driver_minor;
m.BDEV_ACCESS = access;
m.BDEV_ID = 0;
if ((r = sendrec(driver_endpt, &m)) != OK)
panic("sendrec to driver failed (%d)\n", r);
if (m.m_type != BDEV_REPLY)
panic("invalid reply from driver (%d)\n", m.m_type);
return m.BDEV_STATUS;
}
/*===========================================================================*
* fbd_close *
*===========================================================================*/
PRIVATE int fbd_close(dev_t UNUSED(minor))
{
/* Close a device. */
message m;
int r;
/* We simply forward this request to the real driver. */
memset(&m, 0, sizeof(m));
m.m_type = BDEV_CLOSE;
m.BDEV_MINOR = driver_minor;
m.BDEV_ID = 0;
if ((r = sendrec(driver_endpt, &m)) != OK)
panic("sendrec to driver failed (%d)\n", r);
if (m.m_type != BDEV_REPLY)
panic("invalid reply from driver (%d)\n", m.m_type);
return m.BDEV_STATUS;
}
/*===========================================================================*
* fbd_ioctl *
*===========================================================================*/
PRIVATE int fbd_ioctl(dev_t UNUSED(minor), unsigned int request,
endpoint_t endpt, cp_grant_id_t grant)
{
/* Handle an I/O control request. */
cp_grant_id_t gid;
message m;
int r;
/* We only handle the FBD requests, and pass on everything else. */
switch (request) {
case FBDCADDRULE:
case FBDCDELRULE:
case FBDCGETRULE:
return rule_ctl(request, endpt, grant);
}
assert(grant != GRANT_INVALID);
gid = cpf_grant_indirect(driver_endpt, endpt, grant);
assert(gid != GRANT_INVALID);
memset(&m, 0, sizeof(m));
m.m_type = BDEV_IOCTL;
m.BDEV_MINOR = driver_minor;
m.BDEV_REQUEST = request;
m.BDEV_GRANT = gid;
m.BDEV_ID = 0;
if ((r = sendrec(driver_endpt, &m)) != OK)
panic("sendrec to driver failed (%d)\n", r);
if (m.m_type != BDEV_REPLY)
panic("invalid reply from driver (%d)\n", m.m_type);
cpf_revoke(gid);
return m.BDEV_STATUS;
}
/*===========================================================================*
* fbd_transfer_direct *
*===========================================================================*/
PRIVATE ssize_t fbd_transfer_direct(int do_write, u64_t position,
endpoint_t endpt, iovec_t *iov, unsigned int count, int flags)
{
/* Forward the entire transfer request, without any intervention. */
iovec_s_t iovec[NR_IOREQS];
cp_grant_id_t grant;
message m;
int i, r;
for (i = 0; i < count; i++) {
iovec[i].iov_size = iov[i].iov_size;
iovec[i].iov_grant = cpf_grant_indirect(driver_endpt, endpt,
iov[i].iov_addr);
assert(iovec[i].iov_grant != GRANT_INVALID);
}
grant = cpf_grant_direct(driver_endpt, (vir_bytes) iovec,
count * sizeof(iovec[0]), CPF_READ);
assert(grant != GRANT_INVALID);
m.m_type = do_write ? BDEV_SCATTER : BDEV_GATHER;
m.BDEV_MINOR = driver_minor;
m.BDEV_COUNT = count;
m.BDEV_GRANT = grant;
m.BDEV_FLAGS = flags;
m.BDEV_ID = 0;
m.BDEV_POS_LO = ex64lo(position);
m.BDEV_POS_HI = ex64hi(position);
if ((r = sendrec(driver_endpt, &m)) != OK)
panic("sendrec to driver failed (%d)\n", r);
if (m.m_type != BDEV_REPLY)
panic("invalid reply from driver (%d)\n", m.m_type);
cpf_revoke(grant);
for (i = 0; i < count; i++)
cpf_revoke(iovec[i].iov_grant);
return m.BDEV_STATUS;
}
/*===========================================================================*
* fbd_transfer_copy *
*===========================================================================*/
PRIVATE ssize_t fbd_transfer_copy(int do_write, u64_t position,
endpoint_t endpt, iovec_t *iov, unsigned int count, size_t size,
int flags)
{
/* Interpose on the request. */
iovec_s_t iovec[NR_IOREQS];
struct vscp_vec vscp_vec[SCPVEC_NR];
cp_grant_id_t grant;
size_t off, len;
message m;
char *ptr;
int i, j, r;
ssize_t rsize;
assert(count > 0 && count <= SCPVEC_NR);
if (size > BUF_SIZE) {
printf("FBD: allocating memory for %d bytes\n", size);
ptr = alloc_contig(size, 0, NULL);
assert(ptr != NULL);
}
else ptr = fbd_buf;
/* For write operations, first copy in the data to write. */
if (do_write) {
for (i = off = 0; i < count; i++) {
len = iov[i].iov_size;
vscp_vec[i].v_from = endpt;
vscp_vec[i].v_to = SELF;
vscp_vec[i].v_gid = iov[i].iov_addr;
vscp_vec[i].v_offset = 0;
vscp_vec[i].v_addr = (vir_bytes) (ptr + off);
vscp_vec[i].v_bytes = len;
off += len;
}
if ((r = sys_vsafecopy(vscp_vec, i)) != OK)
panic("vsafecopy failed (%d)\n", r);
/* Trigger write hook. */
rule_io_hook(ptr, size, position, FBD_FLAG_WRITE);
}
/* Allocate grants for the data, in the same chunking as the original
* vector. This avoids performance fluctuations with bad hardware as
* observed with the filter driver.
*/
for (i = off = 0; i < count; i++) {
len = iov[i].iov_size;
iovec[i].iov_size = len;
iovec[i].iov_grant = cpf_grant_direct(driver_endpt,
(vir_bytes) (ptr + off), len,
do_write ? CPF_READ : CPF_WRITE);
assert(iovec[i].iov_grant != GRANT_INVALID);
off += len;
}
grant = cpf_grant_direct(driver_endpt, (vir_bytes) iovec,
count * sizeof(iovec[0]), CPF_READ);
assert(grant != GRANT_INVALID);
m.m_type = do_write ? BDEV_SCATTER : BDEV_GATHER;
m.BDEV_MINOR = driver_minor;
m.BDEV_COUNT = count;
m.BDEV_GRANT = grant;
m.BDEV_FLAGS = flags;
m.BDEV_ID = 0;
m.BDEV_POS_LO = ex64lo(position);
m.BDEV_POS_HI = ex64hi(position);
if ((r = sendrec(driver_endpt, &m)) != OK)
panic("sendrec to driver failed (%d)\n", r);
if (m.m_type != BDEV_REPLY)
panic("invalid reply from driver (%d)\n", m.m_type);
cpf_revoke(grant);
for (i = 0; i < count; i++)
cpf_revoke(iovec[i].iov_grant);
/* For read operations, finish by copying out the data read. */
if (!do_write) {
/* Trigger read hook. */
rule_io_hook(ptr, size, position, FBD_FLAG_READ);
/* Upon success, copy back whatever has been processed. */
rsize = m.BDEV_STATUS;
for (i = j = off = 0; rsize > 0 && i < count; i++) {
len = MIN(rsize, iov[i].iov_size);
vscp_vec[j].v_from = SELF;
vscp_vec[j].v_to = endpt;
vscp_vec[j].v_gid = iov[i].iov_addr;
vscp_vec[j].v_offset = 0;
vscp_vec[j].v_addr = (vir_bytes) (ptr + off);
vscp_vec[j].v_bytes = len;
off += len;
rsize -= len;
j++;
}
if (j > 0 && (r = sys_vsafecopy(vscp_vec, j)) != OK)
panic("vsafecopy failed (%d)\n", r);
}
if (ptr != fbd_buf)
free_contig(ptr, size);
return m.BDEV_STATUS;
}
/*===========================================================================*
* fbd_transfer *
*===========================================================================*/
PRIVATE int fbd_transfer(dev_t UNUSED(minor), int do_write, u64_t position,
endpoint_t endpt, iovec_t *iov, unsigned int nr_req, int flags)
{
/* Transfer data from or to the device. */
unsigned count;
size_t size, osize;
int i, hooks;
ssize_t r;
/* Compute the total size of the request. */
for (size = i = 0; i < nr_req; i++)
size += iov[i].iov_size;
osize = size;
count = nr_req;
hooks = rule_find(position, size,
do_write ? FBD_FLAG_WRITE : FBD_FLAG_READ);
#if DEBUG
printf("FBD: %s operation for pos %lx:%08lx size %u -> hooks %x\n",
do_write ? "write" : "read", ex64hi(position),
ex64lo(position), size, hooks);
#endif
if (hooks & PRE_HOOK)
rule_pre_hook(iov, &count, &size, &position);
if (count > 0) {
if (hooks & IO_HOOK) {
r = fbd_transfer_copy(do_write, position, endpt, iov,
count, size, flags);
} else {
r = fbd_transfer_direct(do_write, position, endpt, iov,
count, flags);
}
}
else r = 0;
if (hooks & POST_HOOK)
rule_post_hook(osize, &r);
#if DEBUG
printf("FBD: returning %d\n", r);
#endif
return r;
}

185
drivers/fbd/rule.c Normal file
View File

@@ -0,0 +1,185 @@
#include <minix/drivers.h>
#include <minix/ioctl.h>
#include <sys/ioc_fbd.h>
#include "action.h"
#include "rule.h"
PRIVATE struct fbd_rule rules[MAX_RULES];
PRIVATE struct fbd_rule *matches[MAX_RULES];
PRIVATE int nr_matches;
/*===========================================================================*
* rule_ctl *
*===========================================================================*/
PUBLIC int rule_ctl(int request, endpoint_t endpt, cp_grant_id_t grant)
{
/* Handle an I/O control request regarding rules. */
fbd_rulenum_t i;
int r;
/* Note that any of the safecopy calls may fail if the ioctl is
* improperly defined in userland; never panic if they fail!
*/
switch (request) {
case FBDCADDRULE:
/* Find a free rule slot. */
for (i = 1; i <= MAX_RULES; i++)
if (rules[i-1].num == 0)
break;
if (i == MAX_RULES+1)
return ENOMEM;
/* Copy in the rule. */
if ((r = sys_safecopyfrom(endpt, grant, 0,
(vir_bytes) &rules[i-1], sizeof(rules[0]),
D)) != OK)
return r;
/* Mark the rule as active, and return its number. */
rules[i-1].num = i;
return i;
case FBDCDELRULE:
/* Copy in the given rule number. */
if ((r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &i,
sizeof(i), D)) != OK)
return r;
/* Fail if the given rule number is not valid or in use.
* Allow the caller to determine the maximum rule number.
*/
if (i <= 0 || i > MAX_RULES) return EINVAL;
if (rules[i-1].num != i) return ENOENT;
/* Mark the rule as not active. */
rules[i-1].num = 0;
return OK;
case FBDCGETRULE:
/* Copy in just the rule number from the given structure. */
if ((r = sys_safecopyfrom(endpt, grant,
offsetof(struct fbd_rule, num), (vir_bytes) &i,
sizeof(i), D)) != OK)
return r;
/* Fail if the given rule number is not valid or in use.
* Allow the caller to determine the maximum rule number.
*/
if (i <= 0 || i > MAX_RULES) return EINVAL;
if (rules[i-1].num != i) return ENOENT;
/* Copy out the entire rule as is. */
return sys_safecopyto(endpt, grant, 0, (vir_bytes) &rules[i-1],
sizeof(rules[0]), D);
default:
return EINVAL;
}
}
/*===========================================================================*
* rule_match *
*===========================================================================*/
PRIVATE int rule_match(struct fbd_rule *rule, u64_t pos, size_t size, int flag)
{
/* Check whether the given rule matches the given parameters. As side
* effect, update counters in the rule as appropriate.
*/
/* Ranges must overlap (start < pos+size && end > pos). */
if (cmp64(rule->start, add64u(pos, size)) >= 0 ||
(cmp64u(rule->end, 0) && cmp64(rule->end, pos) <= 0))
return FALSE;
/* Flags must match. */
if (!(rule->flags & flag)) return FALSE;
/* This is a match, but is it supposed to trigger yet? */
if (rule->skip > 0) {
rule->skip--;
return FALSE;
}
return TRUE;
}
/*===========================================================================*
* rule_find *
*===========================================================================*/
PUBLIC int rule_find(u64_t pos, size_t size, int flag)
{
/* Find all matching rules, and return a hook mask. */
struct fbd_rule *rule;
int i, hooks;
nr_matches = 0;
hooks = 0;
for (i = 0; i < MAX_RULES; i++) {
rule = &rules[i];
if (rule->num == 0) continue;
if (!rule_match(rule, pos, size, flag))
continue;
matches[nr_matches++] = rule;
/* If the rule has a limited lifetime, update it now. */
if (rule->count > 0) {
rule->count--;
/* Disable the rule from future matching. */
if (rule->count == 0)
rule->num = 0;
}
hooks |= action_mask(rule);
}
return hooks;
}
/*===========================================================================*
* rule_pre_hook *
*===========================================================================*/
PUBLIC void rule_pre_hook(iovec_t *iov, unsigned *count, size_t *size,
u64_t *pos)
{
int i;
for (i = 0; i < nr_matches; i++)
if (action_mask(matches[i]) & PRE_HOOK)
action_pre_hook(matches[i], iov, count, size, pos);
}
/*===========================================================================*
* rule_io_hook *
*===========================================================================*/
PUBLIC void rule_io_hook(char *buf, size_t size, u64_t pos, int flag)
{
int i;
for (i = 0; i < nr_matches; i++)
if (action_mask(matches[i]) & IO_HOOK)
action_io_hook(matches[i], buf, size, pos, flag);
}
/*===========================================================================*
* rule_post_hook *
*===========================================================================*/
PUBLIC void rule_post_hook(size_t osize, int *result)
{
int i;
for (i = 0; i < nr_matches; i++)
if (action_mask(matches[i]) & POST_HOOK)
action_post_hook(matches[i], osize, result);
}

19
drivers/fbd/rule.h Normal file
View File

@@ -0,0 +1,19 @@
#ifndef _FBD_RULE_H
#define _FBD_RULE_H
#define MAX_RULES 16
extern int rule_ctl(int request, endpoint_t endpt, cp_grant_id_t grant);
extern int rule_find(u64_t pos, size_t size, int flag);
extern void rule_pre_hook(iovec_t *iov, unsigned *count, size_t *size,
u64_t *pos);
extern void rule_io_hook(char *buf, size_t size, u64_t pos, int flag);
extern void rule_post_hook(size_t osize, int *result);
#define PRE_HOOK 0x1
#define IO_HOOK 0x2
#define POST_HOOK 0x4
#endif /* _FBD_RULE_H */