 2bfeeed885
			
		
	
	
		2bfeeed885
		
	
	
	
	
		
			
			. all invocations were S or D, so can safely be dropped to prepare for the segmentless world . still assign D to the SCP_SEG field in the message to make previous kernels usable
		
			
				
	
	
		
			494 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			494 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* Filter driver - top layer - block interface */
 | |
| 
 | |
| /* This is a filter driver, which lays above disk driver, and forwards
 | |
|  * messages between disk driver and its callers. The filter can detect
 | |
|  * corrupted data (toggled by USE_CHECKSUM) and recover it (toggled
 | |
|  * by USE_MIRROR). These two functions are independent from each other. 
 | |
|  * The mirroring function requires two disks, on separate disk drivers.
 | |
|  */
 | |
| 
 | |
| #include "inc.h"
 | |
| 
 | |
| #define _POSIX_SOURCE 1
 | |
| #include <signal.h>
 | |
| 
 | |
| /* Global settings. */
 | |
| int USE_CHECKSUM = 0;	/* enable checksumming */
 | |
| int USE_MIRROR = 0;	/* enable mirroring */
 | |
| 
 | |
| int BAD_SUM_ERROR = 1;	/* bad checksums are considered a driver error */
 | |
| 
 | |
| int USE_SUM_LAYOUT = 0;	/* use checksumming layout on disk */
 | |
| int NR_SUM_SEC = 8;	/* number of checksums per checksum sector */
 | |
| 
 | |
| int SUM_TYPE = ST_CRC;	/* use NIL, XOR, CRC, or MD5 */
 | |
| int SUM_SIZE = 0;	/* size of the stored checksum */
 | |
| 
 | |
| int NR_RETRIES = 3;	/* number of times the request will be retried (N) */
 | |
| int NR_RESTARTS = 3;	/* number of times a driver will be restarted (M) */
 | |
| int DRIVER_TIMEOUT = 5;	/* timeout in seconds to declare a driver dead (T) */
 | |
| 
 | |
| int CHUNK_SIZE = 0;	/* driver requests will be vectorized at this size */
 | |
| 
 | |
| char MAIN_LABEL[LABEL_SIZE] = "";		/* main disk driver label */
 | |
| char BACKUP_LABEL[LABEL_SIZE] = "";		/* backup disk driver label */
 | |
| int MAIN_MINOR = -1;				/* main partition minor nr */
 | |
| int BACKUP_MINOR = -1;				/* backup partition minor nr */
 | |
| 
 | |
| static struct optset optset_table[] = {
 | |
|   { "label0",	OPT_STRING,	MAIN_LABEL,		LABEL_SIZE	},
 | |
|   { "label1",	OPT_STRING,	BACKUP_LABEL,		LABEL_SIZE	},
 | |
|   { "minor0",	OPT_INT,	&MAIN_MINOR,		10		},
 | |
|   { "minor1",	OPT_INT,	&BACKUP_MINOR,		10		},
 | |
|   { "sum_sec",	OPT_INT,	&NR_SUM_SEC,		10		},
 | |
|   { "layout",	OPT_BOOL,	&USE_SUM_LAYOUT,	1		},
 | |
|   { "nolayout",	OPT_BOOL,	&USE_SUM_LAYOUT,	0		},
 | |
|   { "sum",	OPT_BOOL,	&USE_CHECKSUM,		1		},
 | |
|   { "nosum",	OPT_BOOL,	&USE_CHECKSUM,		0		},
 | |
|   { "mirror",	OPT_BOOL,	&USE_MIRROR,		1		},
 | |
|   { "nomirror",	OPT_BOOL,	&USE_MIRROR,		0		},
 | |
|   { "nil",	OPT_BOOL,	&SUM_TYPE,		ST_NIL		},
 | |
|   { "xor",	OPT_BOOL,	&SUM_TYPE,		ST_XOR		},
 | |
|   { "crc",	OPT_BOOL,	&SUM_TYPE,		ST_CRC		},
 | |
|   { "md5",	OPT_BOOL,	&SUM_TYPE,		ST_MD5		},
 | |
|   { "sumerr",	OPT_BOOL,	&BAD_SUM_ERROR,		1		},
 | |
|   { "nosumerr",	OPT_BOOL,	&BAD_SUM_ERROR,		0		},
 | |
|   { "retries",	OPT_INT,	&NR_RETRIES,		10		},
 | |
|   { "N",	OPT_INT,	&NR_RETRIES,		10		},
 | |
|   { "restarts",	OPT_INT,	&NR_RESTARTS,		10		},
 | |
|   { "M",	OPT_INT,	&NR_RESTARTS,		10		},
 | |
|   { "timeout",	OPT_INT,	&DRIVER_TIMEOUT,	10		},
 | |
|   { "T",	OPT_INT,	&DRIVER_TIMEOUT,	10		},
 | |
|   { "chunk",	OPT_INT,	&CHUNK_SIZE,		10		},
 | |
|   { NULL,	0,		NULL,			0		}
 | |
| };
 | |
| 
 | |
| /* Request message. */
 | |
| static message m_in;
 | |
| static endpoint_t who_e;			/* m_source */
 | |
| static long req_id;				/* BDEV_ID */
 | |
| static cp_grant_id_t grant_id;			/* BDEV_GRANT */
 | |
| 
 | |
| /* Data buffers. */
 | |
| static char *buf_array, *buffer;		/* contiguous buffer */
 | |
| 
 | |
| /* SEF functions and variables. */
 | |
| static void sef_local_startup(void);
 | |
| static int sef_cb_init_fresh(int type, sef_init_info_t *info);
 | |
| static void sef_cb_signal_handler(int signo);
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				carry					     *
 | |
|  *===========================================================================*/
 | |
| static int carry(size_t size, int flag_rw)
 | |
| {
 | |
| 	/* Carry data between caller proc and filter.
 | |
| 	 */
 | |
| 
 | |
| 	if (flag_rw == FLT_WRITE)
 | |
| 		return sys_safecopyfrom(who_e, grant_id, 0,
 | |
| 			(vir_bytes) buffer, size);
 | |
| 	else
 | |
| 		return sys_safecopyto(who_e, grant_id, 0,
 | |
| 			(vir_bytes) buffer, size);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				vcarry					     *
 | |
|  *===========================================================================*/
 | |
| static int vcarry(int grants, iovec_t *iov, int flag_rw, size_t size)
 | |
| {
 | |
| 	/* Carry data between caller proc and filter, through grant-vector.
 | |
| 	 */
 | |
| 	char *bufp;
 | |
| 	int i, r;
 | |
| 	size_t bytes;
 | |
| 
 | |
| 	bufp = buffer;
 | |
| 	for(i = 0; i < grants && size > 0; i++) {
 | |
| 		bytes = MIN(size, iov[i].iov_size);
 | |
| 
 | |
| 		if (flag_rw == FLT_WRITE)
 | |
| 			r = sys_safecopyfrom(who_e,
 | |
| 				(vir_bytes) iov[i].iov_addr, 0,
 | |
| 				(vir_bytes) bufp, bytes);
 | |
| 		else
 | |
| 			r = sys_safecopyto(who_e,
 | |
| 				(vir_bytes) iov[i].iov_addr, 0,
 | |
| 				(vir_bytes) bufp, bytes);
 | |
| 
 | |
| 		if(r != OK)
 | |
| 			return r;
 | |
| 
 | |
| 		bufp += bytes;
 | |
| 		size -= bytes;
 | |
| 	}
 | |
| 
 | |
| 	return OK;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				do_rdwt					     *
 | |
|  *===========================================================================*/
 | |
| static int do_rdwt(int flag_rw)
 | |
| {
 | |
| 	size_t size, size_ret;
 | |
| 	u64_t pos;
 | |
| 	int r;
 | |
| 
 | |
| 	pos = make64(m_in.BDEV_POS_LO, m_in.BDEV_POS_HI);
 | |
| 	size = m_in.BDEV_COUNT;
 | |
| 
 | |
| 	if (rem64u(pos, SECTOR_SIZE) != 0 || size % SECTOR_SIZE != 0) {
 | |
| 		printf("Filter: unaligned request from caller!\n");
 | |
| 
 | |
| 		return EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	buffer = flt_malloc(size, buf_array, BUF_SIZE);
 | |
| 
 | |
| 	if(flag_rw == FLT_WRITE)
 | |
| 		carry(size, flag_rw);
 | |
| 
 | |
| 	reset_kills();
 | |
| 
 | |
| 	for (;;) {
 | |
| 		size_ret = size;
 | |
| 		r = transfer(pos, buffer, &size_ret, flag_rw);
 | |
| 		if(r != RET_REDO)
 | |
| 			break;
 | |
| 
 | |
| #if DEBUG
 | |
| 		printf("Filter: transfer yielded RET_REDO, checking drivers\n");
 | |
| #endif
 | |
| 		if((r = check_driver(DRIVER_MAIN)) != OK) break;
 | |
| 		if((r = check_driver(DRIVER_BACKUP)) != OK) break;
 | |
| 	}
 | |
| 
 | |
| 	if(r == OK && flag_rw == FLT_READ)
 | |
| 		carry(size_ret, flag_rw);
 | |
| 
 | |
| 	flt_free(buffer, size, buf_array);
 | |
| 
 | |
| 	if (r != OK)
 | |
| 		return r;
 | |
| 
 | |
| 	return size_ret;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				do_vrdwt				     *
 | |
|  *===========================================================================*/
 | |
| static int do_vrdwt(int flag_rw)
 | |
| {
 | |
| 	size_t size, size_ret;
 | |
| 	int grants;
 | |
| 	int r, i;
 | |
| 	u64_t pos;
 | |
| 	iovec_t iov_proc[NR_IOREQS];
 | |
| 
 | |
| 	/* Extract informations. */
 | |
| 	grants = m_in.BDEV_COUNT;
 | |
| 	if((r = sys_safecopyfrom(who_e, grant_id, 0, (vir_bytes) iov_proc,
 | |
| 		grants * sizeof(iovec_t))) != OK) {
 | |
| 		panic("copying in grant vector failed: %d", r);
 | |
| 	}
 | |
| 
 | |
| 	pos = make64(m_in.BDEV_POS_LO, m_in.BDEV_POS_HI);
 | |
| 	for(size = 0, i = 0; i < grants; i++)
 | |
| 		size += iov_proc[i].iov_size;
 | |
| 
 | |
| 	if (rem64u(pos, SECTOR_SIZE) != 0 || size % SECTOR_SIZE != 0) {
 | |
| 		printf("Filter: unaligned request from caller!\n");
 | |
| 		return EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	buffer = flt_malloc(size, buf_array, BUF_SIZE);
 | |
| 
 | |
| 	if(flag_rw == FLT_WRITE)
 | |
| 		vcarry(grants, iov_proc, flag_rw, size);
 | |
| 
 | |
| 	reset_kills();
 | |
| 
 | |
| 	for (;;) {
 | |
| 		size_ret = size;
 | |
| 		r = transfer(pos, buffer, &size_ret, flag_rw);
 | |
| 		if(r != RET_REDO)
 | |
| 			break;
 | |
| 
 | |
| #if DEBUG
 | |
| 		printf("Filter: transfer yielded RET_REDO, checking drivers\n");
 | |
| #endif
 | |
| 		if((r = check_driver(DRIVER_MAIN)) != OK) break;
 | |
| 		if((r = check_driver(DRIVER_BACKUP)) != OK) break;
 | |
| 	}
 | |
| 
 | |
| 	if(r != OK) {
 | |
| 		flt_free(buffer, size, buf_array);
 | |
| 		return r;
 | |
| 	}
 | |
| 
 | |
| 	if(flag_rw == FLT_READ)
 | |
| 		vcarry(grants, iov_proc, flag_rw, size_ret);
 | |
| 
 | |
| 	flt_free(buffer, size, buf_array);
 | |
| 
 | |
| 	return size_ret;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				do_ioctl				     *
 | |
|  *===========================================================================*/
 | |
| static int do_ioctl(message *m)
 | |
| {
 | |
| 	struct partition sizepart;
 | |
| 
 | |
| 	switch(m->BDEV_REQUEST) {
 | |
| 	case DIOCSETP:
 | |
| 	case DIOCTIMEOUT:
 | |
| 	case DIOCOPENCT:
 | |
| 		/* These do not make sense for us. */
 | |
| 		return EINVAL;
 | |
| 
 | |
| 	case DIOCGETP:
 | |
| 		memset(&sizepart, 0, sizeof(sizepart));
 | |
| 
 | |
| 		/* The presented disk size is the raw partition size,
 | |
| 		 * corrected for space needed for checksums.
 | |
| 		 */
 | |
| 		sizepart.size = convert(get_raw_size());
 | |
| 
 | |
| 		if(sys_safecopyto(who_e, (vir_bytes) grant_id, 0,
 | |
| 				(vir_bytes) &sizepart,
 | |
| 				sizeof(struct partition)) != OK) {
 | |
| 			printf("Filter: DIOCGETP safecopyto failed\n");
 | |
| 			return EIO;
 | |
| 		}
 | |
| 		break;
 | |
| 
 | |
| 	default:
 | |
| 		printf("Filter: unknown ioctl request: %d!\n",
 | |
| 			m->BDEV_REQUEST);
 | |
| 		return EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	return OK;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				parse_arguments				     *
 | |
|  *===========================================================================*/
 | |
| static int parse_arguments(int argc, char *argv[])
 | |
| {
 | |
| 
 | |
| 	if(argc != 2)
 | |
| 		return EINVAL;
 | |
| 
 | |
| 	optset_parse(optset_table, argv[1]);
 | |
| 
 | |
| 	if (MAIN_LABEL[0] == 0 || MAIN_MINOR < 0 || MAIN_MINOR > 255)
 | |
| 		return EINVAL;
 | |
| 	if (USE_MIRROR && (BACKUP_LABEL[0] == 0 ||
 | |
| 			BACKUP_MINOR < 0 || BACKUP_MINOR > 255))
 | |
| 		return EINVAL;
 | |
| 
 | |
| 	/* Checksumming implies a checksum layout. */
 | |
| 	if (USE_CHECKSUM)
 | |
| 		USE_SUM_LAYOUT = 1;
 | |
| 
 | |
| 	/* Determine the checksum size for the chosen checksum type. */
 | |
| 	switch (SUM_TYPE) {
 | |
| 	case ST_NIL:
 | |
| 		SUM_SIZE = 4;	/* for the sector number */
 | |
| 		break;
 | |
| 	case ST_XOR:
 | |
| 		SUM_SIZE = 16;	/* compatibility */
 | |
| 		break;
 | |
| 	case ST_CRC:
 | |
| 		SUM_SIZE = 4;
 | |
| 		break;
 | |
| 	case ST_MD5:
 | |
| 		SUM_SIZE = 16;
 | |
| 		break;
 | |
| 	default:
 | |
| 		return EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	if (NR_SUM_SEC <= 0 || SUM_SIZE * NR_SUM_SEC > SECTOR_SIZE)
 | |
| 		return EINVAL;
 | |
| 
 | |
| #if DEBUG
 | |
| 	printf("Filter starting. Configuration:\n");
 | |
| 	printf("  USE_CHECKSUM :   %3s ", USE_CHECKSUM ? "yes" : "no");
 | |
| 	printf("  USE_MIRROR : %3s\n", USE_MIRROR ? "yes" : "no");
 | |
| 
 | |
| 	if (USE_CHECKSUM) {
 | |
| 		printf("  BAD_SUM_ERROR :  %3s ",
 | |
| 			BAD_SUM_ERROR ? "yes" : "no");
 | |
| 		printf("  NR_SUM_SEC : %3d\n", NR_SUM_SEC);
 | |
| 
 | |
| 		printf("  SUM_TYPE :       ");
 | |
| 
 | |
| 		switch (SUM_TYPE) {
 | |
| 		case ST_NIL: printf("nil"); break;
 | |
| 		case ST_XOR: printf("xor"); break;
 | |
| 		case ST_CRC: printf("crc"); break;
 | |
| 		case ST_MD5: printf("md5"); break;
 | |
| 		}
 | |
| 
 | |
| 		printf("   SUM_SIZE :   %3d\n", SUM_SIZE);
 | |
| 	}
 | |
| 	else printf("  USE_SUM_LAYOUT : %3s\n", USE_SUM_LAYOUT ? "yes" : "no");
 | |
| 
 | |
| 	printf("  N : %3dx       M : %3dx        T : %3ds\n",
 | |
| 		NR_RETRIES, NR_RESTARTS, DRIVER_TIMEOUT);
 | |
| 
 | |
| 	printf("  MAIN_LABEL / MAIN_MINOR : %19s / %d\n",
 | |
| 		MAIN_LABEL, MAIN_MINOR);
 | |
| 	if (USE_MIRROR) {
 | |
| 		printf("  BACKUP_LABEL / BACKUP_MINOR : %15s / %d\n",	
 | |
| 			BACKUP_LABEL, BACKUP_MINOR);
 | |
| 	}
 | |
| 
 | |
| #endif
 | |
| 
 | |
| 	/* Convert timeout seconds to ticks. */
 | |
| 	DRIVER_TIMEOUT *= sys_hz();
 | |
| 
 | |
| 	return OK;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				main					     *
 | |
|  *===========================================================================*/
 | |
| int main(int argc, char *argv[])
 | |
| {
 | |
| 	message m_out;
 | |
| 	int r, ipc_status;
 | |
| 	size_t size;
 | |
| 
 | |
| 	/* SEF local startup. */
 | |
| 	env_setargs(argc, argv);
 | |
| 	sef_local_startup();
 | |
| 
 | |
| 	for (;;) {
 | |
| 		/* Wait for request. */
 | |
| 		if(driver_receive(ANY, &m_in, &ipc_status) != OK) {
 | |
| 			panic("driver_receive failed");
 | |
| 		}
 | |
| 
 | |
| #if DEBUG2
 | |
| 		printf("Filter: got request %d from %d\n",
 | |
| 			m_in.m_type, m_in.m_source);
 | |
| #endif
 | |
| 
 | |
| 		if(m_in.m_source == DS_PROC_NR && is_ipc_notify(ipc_status)) {
 | |
| 			ds_event();
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		who_e = m_in.m_source;
 | |
| 		req_id = m_in.BDEV_ID;
 | |
| 		grant_id = m_in.BDEV_GRANT;
 | |
| 		size = 0;
 | |
| 
 | |
| 		/* Forword the request message to the drivers. */
 | |
| 		switch(m_in.m_type) {
 | |
| 		case BDEV_OPEN:		/* open/close is a noop for filter. */
 | |
| 		case BDEV_CLOSE:	r = OK;				break;
 | |
| 		case BDEV_READ:		r = do_rdwt(FLT_READ);		break;
 | |
| 		case BDEV_WRITE:	r = do_rdwt(FLT_WRITE);		break;
 | |
| 		case BDEV_GATHER:	r = do_vrdwt(FLT_READ);		break;
 | |
| 		case BDEV_SCATTER:	r = do_vrdwt(FLT_WRITE);	break;
 | |
| 		case BDEV_IOCTL:	r = do_ioctl(&m_in);		break;
 | |
| 
 | |
| 		default:
 | |
| 			printf("Filter: ignoring unknown request %d from %d\n", 
 | |
| 				m_in.m_type, m_in.m_source);
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| #if DEBUG2
 | |
| 		printf("Filter: replying with code %d\n", r);
 | |
| #endif
 | |
| 
 | |
| 		/* Send back reply message. */
 | |
| 		m_out.m_type = BDEV_REPLY;
 | |
| 		m_out.BDEV_ID = req_id;
 | |
| 		m_out.BDEV_STATUS = r;
 | |
| 		send(who_e, &m_out);
 | |
| 	}
 | |
| 
 | |
| 	return 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);
 | |
| 
 | |
| 	/* No live update support for now. */
 | |
| 
 | |
| 	/* Register signal callbacks. */
 | |
| 	sef_setcb_signal_handler(sef_cb_signal_handler);
 | |
| 
 | |
| 	/* Let SEF perform startup. */
 | |
| 	sef_startup();
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *		            sef_cb_init_fresh                                *
 | |
|  *===========================================================================*/
 | |
| static int sef_cb_init_fresh(int type, sef_init_info_t *UNUSED(info))
 | |
| {
 | |
| 	/* Initialize the filter driver. */
 | |
| 	int r;
 | |
| 
 | |
| 	r = parse_arguments(env_argc, env_argv);
 | |
| 	if(r != OK) {
 | |
| 		printf("Filter: wrong argument!\n");
 | |
| 		return 1;
 | |
| 	}
 | |
| 
 | |
| 	if ((buf_array = flt_malloc(BUF_SIZE, NULL, 0)) == NULL)
 | |
| 		panic("no memory available");
 | |
| 
 | |
| 	sum_init();
 | |
| 
 | |
| 	driver_init();
 | |
| 
 | |
| 	/* Subscribe to block driver events. */
 | |
| 	r = ds_subscribe("drv\\.blk\\..*", DSF_INITIAL | DSF_OVERWRITE);
 | |
| 	if(r != OK) {
 | |
| 		panic("Filter: can't subscribe to driver events");
 | |
| 	}
 | |
| 
 | |
| 	/* Announce we are up! */
 | |
| 	blockdriver_announce(type);
 | |
| 
 | |
| 	return(OK);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *		           sef_cb_signal_handler                             *
 | |
|  *===========================================================================*/
 | |
| static void sef_cb_signal_handler(int signo)
 | |
| {
 | |
| 	/* Only check for termination signal, ignore anything else. */
 | |
| 	if (signo != SIGTERM) return;
 | |
| 
 | |
| 	/* If so, shut down this driver. */
 | |
| #if DEBUG
 | |
| 	printf("Filter: shutdown...\n");
 | |
| #endif
 | |
| 
 | |
| 	driver_shutdown();
 | |
| 
 | |
| 	exit(0);
 | |
| }
 | |
| 
 |