448 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			448 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* 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. */
 | 
						|
static int fbd_open(devminor_t minor, int access);
 | 
						|
static int fbd_close(devminor_t minor);
 | 
						|
static int fbd_transfer(devminor_t minor, int do_write, u64_t position,
 | 
						|
	endpoint_t endpt, iovec_t *iov, unsigned int nr_req, int flags);
 | 
						|
static int fbd_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt,
 | 
						|
	cp_grant_id_t grant, endpoint_t user_endpt);
 | 
						|
 | 
						|
/* Variables. */
 | 
						|
static char *fbd_buf;			/* scratch buffer */
 | 
						|
 | 
						|
static char driver_label[32] = "";	/* driver DS label */
 | 
						|
static devminor_t driver_minor = -1;	/* driver's partition minor to use */
 | 
						|
static endpoint_t driver_endpt;		/* driver endpoint */
 | 
						|
 | 
						|
/* Entry points to this driver. */
 | 
						|
static struct blockdriver fbd_dtab = {
 | 
						|
	.bdr_type	= BLOCKDRIVER_TYPE_OTHER,/* do not handle part. reqs */
 | 
						|
	.bdr_open	= fbd_open,	/* open request, initialize device */
 | 
						|
	.bdr_close	= fbd_close,	/* release device */
 | 
						|
	.bdr_transfer	= fbd_transfer,	/* do the I/O */
 | 
						|
	.bdr_ioctl	= fbd_ioctl	/* perform I/O control request */
 | 
						|
};
 | 
						|
 | 
						|
/* Options supported by this driver. */
 | 
						|
static 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			     *
 | 
						|
 *===========================================================================*/
 | 
						|
static 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 = getticks(&uptime)) != OK)
 | 
						|
		panic("getuptime failed (%d)\n", r);
 | 
						|
 | 
						|
	srand48(uptime);
 | 
						|
 | 
						|
	/* Announce we are up! */
 | 
						|
	blockdriver_announce(type);
 | 
						|
 | 
						|
	return OK;
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				sef_cb_signal_handler			     *
 | 
						|
 *===========================================================================*/
 | 
						|
static 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			     *
 | 
						|
 *===========================================================================*/
 | 
						|
static 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					     *
 | 
						|
 *===========================================================================*/
 | 
						|
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				     *
 | 
						|
 *===========================================================================*/
 | 
						|
static int fbd_open(devminor_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 = ipc_sendrec(driver_endpt, &m)) != OK)
 | 
						|
		panic("ipc_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				     *
 | 
						|
 *===========================================================================*/
 | 
						|
static int fbd_close(devminor_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 = ipc_sendrec(driver_endpt, &m)) != OK)
 | 
						|
		panic("ipc_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				     *
 | 
						|
 *===========================================================================*/
 | 
						|
static int fbd_ioctl(devminor_t UNUSED(minor), unsigned long request,
 | 
						|
	endpoint_t endpt, cp_grant_id_t grant, endpoint_t UNUSED(user_endpt))
 | 
						|
{
 | 
						|
	/* 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_USER = NONE;
 | 
						|
	m.BDEV_ID = 0;
 | 
						|
 | 
						|
	if ((r = ipc_sendrec(driver_endpt, &m)) != OK)
 | 
						|
		panic("ipc_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			     *
 | 
						|
 *===========================================================================*/
 | 
						|
static 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 = position;
 | 
						|
 | 
						|
	if ((r = ipc_sendrec(driver_endpt, &m)) != OK)
 | 
						|
		panic("ipc_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			     *
 | 
						|
 *===========================================================================*/
 | 
						|
static 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 = position;
 | 
						|
 | 
						|
	if ((r = ipc_sendrec(driver_endpt, &m)) != OK)
 | 
						|
		panic("ipc_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				     *
 | 
						|
 *===========================================================================*/
 | 
						|
static int fbd_transfer(devminor_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 int 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 %"PRIx64" size %u -> hooks %x\n",
 | 
						|
		do_write ? "write" : "read", 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;
 | 
						|
}
 |