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