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:
@@ -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
22
drivers/fbd/Makefile
Normal 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
302
drivers/fbd/action.c
Normal 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
12
drivers/fbd/action.h
Normal 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
456
drivers/fbd/fbd.c
Normal 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
185
drivers/fbd/rule.c
Normal 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
19
drivers/fbd/rule.h
Normal 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 */
|
||||
Reference in New Issue
Block a user