xref: /minix/minix/drivers/storage/filter/main.c (revision 0a6a1f1d)
1 /* Filter driver - top layer - block interface */
2 
3 /* This is a filter driver, which lays above disk driver, and forwards
4  * messages between disk driver and its callers. The filter can detect
5  * corrupted data (toggled by USE_CHECKSUM) and recover it (toggled
6  * by USE_MIRROR). These two functions are independent from each other.
7  * The mirroring function requires two disks, on separate disk drivers.
8  */
9 
10 #include "inc.h"
11 
12 #define _POSIX_SOURCE 1
13 #include <signal.h>
14 
15 /* Global settings. */
16 int USE_CHECKSUM = 0;	/* enable checksumming */
17 int USE_MIRROR = 0;	/* enable mirroring */
18 
19 int BAD_SUM_ERROR = 1;	/* bad checksums are considered a driver error */
20 
21 int USE_SUM_LAYOUT = 0;	/* use checksumming layout on disk */
22 int NR_SUM_SEC = 8;	/* number of checksums per checksum sector */
23 
24 int SUM_TYPE = ST_CRC;	/* use NIL, XOR, CRC, or MD5 */
25 int SUM_SIZE = 0;	/* size of the stored checksum */
26 
27 int NR_RETRIES = 3;	/* number of times the request will be retried (N) */
28 int NR_RESTARTS = 3;	/* number of times a driver will be restarted (M) */
29 int DRIVER_TIMEOUT = 5;	/* timeout in seconds to declare a driver dead (T) */
30 
31 int CHUNK_SIZE = 0;	/* driver requests will be vectorized at this size */
32 
33 char MAIN_LABEL[LABEL_SIZE] = "";		/* main disk driver label */
34 char BACKUP_LABEL[LABEL_SIZE] = "";		/* backup disk driver label */
35 int MAIN_MINOR = -1;				/* main partition minor nr */
36 int BACKUP_MINOR = -1;				/* backup partition minor nr */
37 
38 static struct optset optset_table[] = {
39   { "label0",	OPT_STRING,	MAIN_LABEL,		LABEL_SIZE	},
40   { "label1",	OPT_STRING,	BACKUP_LABEL,		LABEL_SIZE	},
41   { "minor0",	OPT_INT,	&MAIN_MINOR,		10		},
42   { "minor1",	OPT_INT,	&BACKUP_MINOR,		10		},
43   { "sum_sec",	OPT_INT,	&NR_SUM_SEC,		10		},
44   { "layout",	OPT_BOOL,	&USE_SUM_LAYOUT,	1		},
45   { "nolayout",	OPT_BOOL,	&USE_SUM_LAYOUT,	0		},
46   { "sum",	OPT_BOOL,	&USE_CHECKSUM,		1		},
47   { "nosum",	OPT_BOOL,	&USE_CHECKSUM,		0		},
48   { "mirror",	OPT_BOOL,	&USE_MIRROR,		1		},
49   { "nomirror",	OPT_BOOL,	&USE_MIRROR,		0		},
50   { "nil",	OPT_BOOL,	&SUM_TYPE,		ST_NIL		},
51   { "xor",	OPT_BOOL,	&SUM_TYPE,		ST_XOR		},
52   { "crc",	OPT_BOOL,	&SUM_TYPE,		ST_CRC		},
53   { "md5",	OPT_BOOL,	&SUM_TYPE,		ST_MD5		},
54   { "sumerr",	OPT_BOOL,	&BAD_SUM_ERROR,		1		},
55   { "nosumerr",	OPT_BOOL,	&BAD_SUM_ERROR,		0		},
56   { "retries",	OPT_INT,	&NR_RETRIES,		10		},
57   { "N",	OPT_INT,	&NR_RETRIES,		10		},
58   { "restarts",	OPT_INT,	&NR_RESTARTS,		10		},
59   { "M",	OPT_INT,	&NR_RESTARTS,		10		},
60   { "timeout",	OPT_INT,	&DRIVER_TIMEOUT,	10		},
61   { "T",	OPT_INT,	&DRIVER_TIMEOUT,	10		},
62   { "chunk",	OPT_INT,	&CHUNK_SIZE,		10		},
63   { NULL,	0,		NULL,			0		}
64 };
65 
66 /* Data buffers. */
67 static char *buf_array, *buffer;		/* contiguous buffer */
68 
69 /* SEF functions and variables. */
70 static void sef_local_startup(void);
71 static int sef_cb_init_fresh(int type, sef_init_info_t *info);
72 static void sef_cb_signal_handler(int signo);
73 
74 static int filter_open(devminor_t minor, int access);
75 static int filter_close(devminor_t minor);
76 static ssize_t filter_transfer(devminor_t minor, int do_write, u64_t pos,
77 	endpoint_t endpt, iovec_t *iov, unsigned int count, int flags);
78 static int filter_ioctl(devminor_t minor, unsigned long request,
79 	endpoint_t endpt, cp_grant_id_t grant, endpoint_t user_endpt);
80 static void filter_other(message *m, int ipc_status);
81 
82 static struct blockdriver filter_tab = {
83 	.bdr_type	= BLOCKDRIVER_TYPE_OTHER,
84 	.bdr_open	= filter_open,
85 	.bdr_close	= filter_close,
86 	.bdr_transfer	= filter_transfer,
87 	.bdr_ioctl	= filter_ioctl,
88 	.bdr_other	= filter_other
89 };
90 
91 /*===========================================================================*
92  *				filter_open				     *
93  *===========================================================================*/
94 static int filter_open(devminor_t UNUSED(minor), int UNUSED(access))
95 {
96 	/* Open is a noop for filter. */
97 	return OK;
98 }
99 
100 /*===========================================================================*
101  *				filter_close				     *
102  *===========================================================================*/
103 static int filter_close(devminor_t UNUSED(minor))
104 {
105 	/* Close is a noop for filter. */
106 	return OK;
107 }
108 
109 /*===========================================================================*
110  *				vcarry					     *
111  *===========================================================================*/
112 static int vcarry(endpoint_t endpt, unsigned int grants, iovec_t *iov,
113 	int do_write, size_t size)
114 {
115 	/* Carry data between caller proc and filter, through grant-vector.
116 	 */
117 	char *bufp;
118 	int i, r;
119 	size_t bytes;
120 
121 	bufp = buffer;
122 	for(i = 0; i < grants && size > 0; i++) {
123 		bytes = MIN(size, iov[i].iov_size);
124 
125 		if (do_write)
126 			r = sys_safecopyfrom(endpt,
127 				(vir_bytes) iov[i].iov_addr, 0,
128 				(vir_bytes) bufp, bytes);
129 		else
130 			r = sys_safecopyto(endpt,
131 				(vir_bytes) iov[i].iov_addr, 0,
132 				(vir_bytes) bufp, bytes);
133 
134 		if(r != OK)
135 			return r;
136 
137 		bufp += bytes;
138 		size -= bytes;
139 	}
140 
141 	return OK;
142 }
143 
144 
145 /*===========================================================================*
146  *				filter_transfer				     *
147  *===========================================================================*/
148 static ssize_t filter_transfer(devminor_t UNUSED(minor), int do_write,
149 	u64_t pos, endpoint_t endpt, iovec_t *iov, unsigned int count,
150 	int UNUSED(flags))
151 {
152 	size_t size, size_ret;
153 	int r, i;
154 
155 	for(size = 0, i = 0; i < count; i++)
156 		size += iov[i].iov_size;
157 
158 	if (pos % SECTOR_SIZE != 0 || size % SECTOR_SIZE != 0) {
159 		printf("Filter: unaligned request from caller!\n");
160 		return EINVAL;
161 	}
162 
163 	buffer = flt_malloc(size, buf_array, BUF_SIZE);
164 
165 	if (do_write)
166 		vcarry(endpt, count, iov, do_write, size);
167 
168 	reset_kills();
169 
170 	for (;;) {
171 		size_ret = size;
172 		r = transfer(pos, buffer, &size_ret,
173 			do_write ? FLT_WRITE : FLT_READ);
174 		if(r != RET_REDO)
175 			break;
176 
177 #if DEBUG
178 		printf("Filter: transfer yielded RET_REDO, checking drivers\n");
179 #endif
180 		if((r = check_driver(DRIVER_MAIN)) != OK) break;
181 		if((r = check_driver(DRIVER_BACKUP)) != OK) break;
182 	}
183 
184 	if(r != OK) {
185 		flt_free(buffer, size, buf_array);
186 		return r;
187 	}
188 
189 	if (!do_write)
190 		vcarry(endpt, count, iov, do_write, size_ret);
191 
192 	flt_free(buffer, size, buf_array);
193 
194 	return size_ret;
195 }
196 
197 /*===========================================================================*
198  *				filter_ioctl				     *
199  *===========================================================================*/
200 static int filter_ioctl(devminor_t UNUSED(minor), unsigned long request,
201 	endpoint_t endpt, cp_grant_id_t grant, endpoint_t UNUSED(user_endpt))
202 {
203 	struct part_geom sizepart;
204 
205 	switch (request) {
206 	case DIOCSETP:
207 	case DIOCTIMEOUT:
208 	case DIOCOPENCT:
209 		/* These do not make sense for us. */
210 		return EINVAL;
211 
212 	case DIOCGETP:
213 		memset(&sizepart, 0, sizeof(sizepart));
214 
215 		/* The presented disk size is the raw partition size,
216 		 * corrected for space needed for checksums.
217 		 */
218 		sizepart.size = convert(get_raw_size());
219 
220 		if (sys_safecopyto(endpt, grant, 0, (vir_bytes) &sizepart,
221 				sizeof(struct part_geom)) != OK) {
222 			printf("Filter: DIOCGETP safecopyto failed\n");
223 			return EIO;
224 		}
225 		break;
226 
227 	default:
228 		printf("Filter: unknown ioctl request: %ld!\n", request);
229 		return ENOTTY;
230 	}
231 
232 	return OK;
233 }
234 
235 /*===========================================================================*
236  *				filter_other				     *
237  *===========================================================================*/
238 static void filter_other(message *m, int ipc_status)
239 {
240 	/* Process other messages. */
241 	if (m->m_source == DS_PROC_NR && is_ipc_notify(ipc_status)) {
242 		ds_event();
243 	}
244 }
245 
246 /*===========================================================================*
247  *				parse_arguments				     *
248  *===========================================================================*/
249 static int parse_arguments(int argc, char *argv[])
250 {
251 
252 	if(argc != 2)
253 		return EINVAL;
254 
255 	optset_parse(optset_table, argv[1]);
256 
257 	if (MAIN_LABEL[0] == 0 || MAIN_MINOR < 0 || MAIN_MINOR > 255)
258 		return EINVAL;
259 	if (USE_MIRROR && (BACKUP_LABEL[0] == 0 ||
260 			BACKUP_MINOR < 0 || BACKUP_MINOR > 255))
261 		return EINVAL;
262 
263 	/* Checksumming implies a checksum layout. */
264 	if (USE_CHECKSUM)
265 		USE_SUM_LAYOUT = 1;
266 
267 	/* Determine the checksum size for the chosen checksum type. */
268 	switch (SUM_TYPE) {
269 	case ST_NIL:
270 		SUM_SIZE = 4;	/* for the sector number */
271 		break;
272 	case ST_XOR:
273 		SUM_SIZE = 16;	/* compatibility */
274 		break;
275 	case ST_CRC:
276 		SUM_SIZE = 4;
277 		break;
278 	case ST_MD5:
279 		SUM_SIZE = 16;
280 		break;
281 	default:
282 		return EINVAL;
283 	}
284 
285 	if (NR_SUM_SEC <= 0 || SUM_SIZE * NR_SUM_SEC > SECTOR_SIZE)
286 		return EINVAL;
287 
288 #if DEBUG
289 	printf("Filter starting. Configuration:\n");
290 	printf("  USE_CHECKSUM :   %3s ", USE_CHECKSUM ? "yes" : "no");
291 	printf("  USE_MIRROR : %3s\n", USE_MIRROR ? "yes" : "no");
292 
293 	if (USE_CHECKSUM) {
294 		printf("  BAD_SUM_ERROR :  %3s ",
295 			BAD_SUM_ERROR ? "yes" : "no");
296 		printf("  NR_SUM_SEC : %3d\n", NR_SUM_SEC);
297 
298 		printf("  SUM_TYPE :       ");
299 
300 		switch (SUM_TYPE) {
301 		case ST_NIL: printf("nil"); break;
302 		case ST_XOR: printf("xor"); break;
303 		case ST_CRC: printf("crc"); break;
304 		case ST_MD5: printf("md5"); break;
305 		}
306 
307 		printf("   SUM_SIZE :   %3d\n", SUM_SIZE);
308 	}
309 	else printf("  USE_SUM_LAYOUT : %3s\n", USE_SUM_LAYOUT ? "yes" : "no");
310 
311 	printf("  N : %3dx       M : %3dx        T : %3ds\n",
312 		NR_RETRIES, NR_RESTARTS, DRIVER_TIMEOUT);
313 
314 	printf("  MAIN_LABEL / MAIN_MINOR : %19s / %d\n",
315 		MAIN_LABEL, MAIN_MINOR);
316 	if (USE_MIRROR) {
317 		printf("  BACKUP_LABEL / BACKUP_MINOR : %15s / %d\n",
318 			BACKUP_LABEL, BACKUP_MINOR);
319 	}
320 
321 #endif
322 
323 	/* Convert timeout seconds to ticks. */
324 	DRIVER_TIMEOUT *= sys_hz();
325 
326 	return OK;
327 }
328 
329 /*===========================================================================*
330  *				main					     *
331  *===========================================================================*/
332 int main(int argc, char *argv[])
333 {
334 	message m_out;
335 	int r, ipc_status;
336 	size_t size;
337 
338 	/* SEF local startup. */
339 	env_setargs(argc, argv);
340 	sef_local_startup();
341 
342 	blockdriver_task(&filter_tab);
343 
344 	return 0;
345 }
346 
347 /*===========================================================================*
348  *			       sef_local_startup			     *
349  *===========================================================================*/
350 static void sef_local_startup(void)
351 {
352 	/* Register init callbacks. */
353 	sef_setcb_init_fresh(sef_cb_init_fresh);
354 	sef_setcb_init_restart(sef_cb_init_fresh);
355 
356 	/* Register signal callbacks. */
357 	sef_setcb_signal_handler(sef_cb_signal_handler);
358 
359 	/* Let SEF perform startup. */
360 	sef_startup();
361 }
362 
363 /*===========================================================================*
364  *		            sef_cb_init_fresh                                *
365  *===========================================================================*/
366 static int sef_cb_init_fresh(int type, sef_init_info_t *UNUSED(info))
367 {
368 	/* Initialize the filter driver. */
369 	int r;
370 
371 	r = parse_arguments(env_argc, env_argv);
372 	if(r != OK) {
373 		printf("Filter: wrong argument!\n");
374 		return 1;
375 	}
376 
377 	if ((buf_array = flt_malloc(BUF_SIZE, NULL, 0)) == NULL)
378 		panic("no memory available");
379 
380 	sum_init();
381 
382 	driver_init();
383 
384 	/* Subscribe to block driver events. */
385 	r = ds_subscribe("drv\\.blk\\..*", DSF_INITIAL | DSF_OVERWRITE);
386 	if(r != OK) {
387 		panic("Filter: can't subscribe to driver events");
388 	}
389 
390 	/* Announce we are up! */
391 	blockdriver_announce(type);
392 
393 	return(OK);
394 }
395 
396 /*===========================================================================*
397  *		           sef_cb_signal_handler                             *
398  *===========================================================================*/
399 static void sef_cb_signal_handler(int signo)
400 {
401 	/* Only check for termination signal, ignore anything else. */
402 	if (signo != SIGTERM) return;
403 
404 	/* If so, shut down this driver. */
405 #if DEBUG
406 	printf("Filter: shutdown...\n");
407 #endif
408 
409 	driver_shutdown();
410 
411 	exit(0);
412 }
413