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 *===========================================================================*/
filter_open(devminor_t UNUSED (minor),int UNUSED (access))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 *===========================================================================*/
filter_close(devminor_t UNUSED (minor))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 *===========================================================================*/
vcarry(endpoint_t endpt,unsigned int grants,iovec_t * iov,int do_write,size_t size)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 *===========================================================================*/
filter_transfer(devminor_t UNUSED (minor),int do_write,u64_t pos,endpoint_t endpt,iovec_t * iov,unsigned int count,int UNUSED (flags))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 *===========================================================================*/
filter_ioctl(devminor_t UNUSED (minor),unsigned long request,endpoint_t endpt,cp_grant_id_t grant,endpoint_t UNUSED (user_endpt))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 *===========================================================================*/
filter_other(message * m,int ipc_status)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 *===========================================================================*/
parse_arguments(int argc,char * argv[])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 *===========================================================================*/
main(int argc,char * argv[])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 *===========================================================================*/
sef_local_startup(void)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 *===========================================================================*/
sef_cb_init_fresh(int type,sef_init_info_t * UNUSED (info))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 *===========================================================================*/
sef_cb_signal_handler(int signo)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