1 /* The device-independent network driver framework. */ 2 3 #include <minix/drivers.h> 4 #include <minix/endpoint.h> 5 #include <minix/netdriver.h> 6 #include <minix/ds.h> 7 #include <assert.h> 8 9 #include "netdriver.h" 10 11 static const struct netdriver *netdriver_table = NULL; 12 13 static int running; 14 15 static int conf_expected; 16 17 static endpoint_t pending_endpt; 18 static struct netdriver_data pending_recv, pending_send; 19 20 static int defer_reply; 21 static unsigned int pending_flags; 22 static size_t pending_size; 23 24 static ether_addr_t hw_addr; 25 26 /* 27 * Announce we are up after a fresh start or restart. 28 */ 29 void 30 netdriver_announce(void) 31 { 32 const char *driver_prefix = "drv.net."; 33 char label[DS_MAX_KEYLEN]; 34 char key[DS_MAX_KEYLEN]; 35 int r; 36 37 /* Publish a driver up event. */ 38 if ((r = ds_retrieve_label_name(label, sef_self())) != OK) 39 panic("netdriver: unable to get own label: %d", r); 40 41 snprintf(key, sizeof(key), "%s%s", driver_prefix, label); 42 if ((r = ds_publish_u32(key, DS_DRIVER_UP, DSF_OVERWRITE)) != OK) 43 panic("netdriver: unable to publish driver up event: %d", r); 44 } 45 46 /* 47 * Prepare for copying. Given a flat offset, return the vector element index 48 * and an offset into that element. Panic if the request does not fall 49 * entirely within the vector. 50 */ 51 size_t 52 netdriver_prepare_copy(struct netdriver_data * data, size_t off, size_t size, 53 unsigned int * indexp) 54 { 55 unsigned int i; 56 57 assert(data->size > 0); 58 59 /* 60 * In theory we could truncate when copying out, but this creates a 61 * problem for port-based I/O, where the size of the transfer is 62 * typically specified in advance. We could do extra port-based I/O 63 * to discard the extra bytes, but the driver is better off doing such 64 * truncation itself. Thus, we disallow copying (in and out) beyond 65 * the given data vector altogether. 66 */ 67 if (off + size > data->size) 68 panic("netdriver: request to copy beyond data size"); 69 70 /* 71 * Find the starting offset in the vector. If this turns out to be 72 * expensive, this can be adapted to store the last <element,offset> 73 * pair in the "data" structure (this is the reason it is not 'const'). 74 */ 75 for (i = 0; i < data->count; i++) { 76 assert(data->iovec[i].iov_size > 0); 77 78 if (off >= data->iovec[i].iov_size) 79 off -= data->iovec[i].iov_size; 80 else 81 break; 82 } 83 84 assert(i < data->count); 85 86 *indexp = i; 87 return off; 88 } 89 90 /* 91 * Copy in or out packet data from/to a vector of grants. 92 */ 93 static void 94 netdriver_copy(struct netdriver_data * data, size_t off, vir_bytes addr, 95 size_t size, int copyin) 96 { 97 struct vscp_vec vec[SCPVEC_NR]; 98 size_t chunk; 99 unsigned int i, v; 100 int r; 101 102 off = netdriver_prepare_copy(data, off, size, &i); 103 104 /* Generate a new vector with all the individual copies to make. */ 105 for (v = 0; size > 0; v++) { 106 chunk = data->iovec[i].iov_size - off; 107 if (chunk > size) 108 chunk = size; 109 assert(chunk > 0); 110 111 /* 112 * We should be able to fit the entire I/O request in a single 113 * copy vector. If not, MINIX3 has been misconfigured. 114 */ 115 if (v >= SCPVEC_NR) 116 panic("netdriver: invalid vector size constant"); 117 118 if (copyin) { 119 vec[v].v_from = data->endpt; 120 vec[v].v_to = SELF; 121 } else { 122 vec[v].v_from = SELF; 123 vec[v].v_to = data->endpt; 124 } 125 vec[v].v_gid = data->iovec[i].iov_grant; 126 vec[v].v_offset = off; 127 vec[v].v_addr = addr; 128 vec[v].v_bytes = chunk; 129 130 i++; 131 off = 0; 132 addr += chunk; 133 size -= chunk; 134 } 135 136 assert(v > 0 && v <= SCPVEC_NR); 137 138 /* 139 * If only one vector element was generated, use a direct copy. This 140 * saves the kernel from having to copy in the vector. 141 */ 142 if (v == 1) { 143 if (copyin) 144 r = sys_safecopyfrom(vec->v_from, vec->v_gid, 145 vec->v_offset, vec->v_addr, vec->v_bytes); 146 else 147 r = sys_safecopyto(vec->v_to, vec->v_gid, 148 vec->v_offset, vec->v_addr, vec->v_bytes); 149 } else 150 r = sys_vsafecopy(vec, v); 151 152 if (r != OK) 153 panic("netdriver: unable to copy data: %d", r); 154 } 155 156 /* 157 * Copy in packet data. 158 */ 159 void 160 netdriver_copyin(struct netdriver_data * __restrict data, size_t off, 161 void * __restrict ptr, size_t size) 162 { 163 164 netdriver_copy(data, off, (vir_bytes)ptr, size, TRUE /*copyin*/); 165 } 166 167 /* 168 * Copy out packet data. 169 */ 170 void 171 netdriver_copyout(struct netdriver_data * __restrict data, size_t off, 172 const void * __restrict ptr, size_t size) 173 { 174 175 netdriver_copy(data, off, (vir_bytes)ptr, size, FALSE /*copyin*/); 176 } 177 178 /* 179 * Send a reply to a request. 180 */ 181 static void 182 send_reply(endpoint_t endpt, message * m_ptr) 183 { 184 int r; 185 186 if ((r = ipc_send(endpt, m_ptr)) != OK) 187 panic("netdriver: unable to send to %d: %d", endpt, r); 188 } 189 190 /* 191 * Defer sending any replies to task requests until the next call to 192 * check_replies(). The purpose of this is aggregation of task replies to both 193 * send and receive requests into a single reply message, which saves on 194 * messages, in particular when processing interrupts. 195 */ 196 static void 197 defer_replies(void) 198 { 199 200 assert(netdriver_table != NULL); 201 assert(defer_reply == FALSE); 202 203 defer_reply = TRUE; 204 } 205 206 /* 207 * Check if we have to reply to earlier task (I/O) requests, and if so, send 208 * the reply. If deferred is FALSE and the call to this function was preceded 209 * by a call to defer_replies(), do not send a reply yet. If always_send is 210 * TRUE, send a reply even if no tasks have completed yet. 211 */ 212 static void 213 check_replies(int deferred, int always_send) 214 { 215 message m_reply; 216 217 if (defer_reply && !deferred) 218 return; 219 220 defer_reply = FALSE; 221 222 if (pending_flags == 0 && !always_send) 223 return; 224 225 assert(pending_endpt != NONE); 226 227 memset(&m_reply, 0, sizeof(m_reply)); 228 m_reply.m_type = DL_TASK_REPLY; 229 m_reply.m_netdrv_net_dl_task.flags = pending_flags; 230 m_reply.m_netdrv_net_dl_task.count = pending_size; 231 232 send_reply(pending_endpt, &m_reply); 233 234 pending_flags = 0; 235 pending_size = 0; 236 } 237 238 /* 239 * Resume receiving packets. In particular, if a receive request was pending, 240 * call the driver's receive function. If the call is successful, schedule 241 * sending a reply to the requesting party. 242 */ 243 void 244 netdriver_recv(void) 245 { 246 ssize_t r; 247 248 if (pending_recv.size == 0) 249 return; 250 251 assert(netdriver_table != NULL); 252 253 /* 254 * For convenience of driver writers: if the receive function returns 255 * zero, simply call it again, to simplify discarding invalid packets. 256 */ 257 do { 258 r = netdriver_table->ndr_recv(&pending_recv, 259 pending_recv.size); 260 261 /* 262 * The default policy is: drop undersized packets, panic on 263 * oversized packets. The driver may implement any other 264 * policy (e.g., pad small packets, drop or truncate large 265 * packets), but it should at least test against the given 266 * 'max' value. The reason that truncation should be 267 * implemented in the driver rather than here, is explained in 268 * an earlier comment about truncating copy operations. 269 */ 270 if (r >= 0 && r < ETH_MIN_PACK_SIZE) 271 r = 0; 272 else if (r > (ssize_t)pending_recv.size) 273 panic("netdriver: oversized packet returned: %zd", r); 274 } while (r == 0); 275 276 if (r == SUSPEND) 277 return; 278 if (r < 0) 279 panic("netdriver: driver reported receive failure: %d", r); 280 281 assert(r >= ETH_MIN_PACK_SIZE && (size_t)r <= pending_recv.size); 282 283 pending_flags |= DL_PACK_RECV; 284 pending_size = r; 285 286 pending_recv.size = 0; 287 288 check_replies(FALSE /*deferred*/, FALSE /*always_send*/); 289 } 290 291 /* 292 * Resume sending packets. In particular, if a send request was pending, call 293 * the driver's send function. If the call is successful, schedule sending a 294 * reply to the requesting party. This function relies on being called 295 * between init_pending() and check_pending(). 296 */ 297 void 298 netdriver_send(void) 299 { 300 int r; 301 302 if (pending_send.size == 0) 303 return; 304 305 assert(netdriver_table != NULL); 306 307 r = netdriver_table->ndr_send(&pending_send, pending_send.size); 308 309 if (r == SUSPEND) 310 return; 311 if (r < 0) 312 panic("netdriver: driver reported send failure: %d", r); 313 314 pending_flags |= DL_PACK_SEND; 315 316 pending_send.size = 0; 317 318 check_replies(FALSE /*deferred*/, FALSE /*always_send*/); 319 } 320 321 /* 322 * Process a request to receive or send a packet. 323 */ 324 static void 325 do_readwrite(const struct netdriver * __restrict ndp, endpoint_t endpt, 326 cp_grant_id_t grant, unsigned int count, int do_write) 327 { 328 struct netdriver_data *data; 329 unsigned int i; 330 int r; 331 332 /* Copy in the I/O vector. */ 333 data = (do_write) ? &pending_send : &pending_recv; 334 335 if (data->size != 0) 336 panic("netdriver: multiple concurrent requests"); 337 338 if (count == 0 || count > NR_IOREQS) 339 panic("netdriver: bad I/O vector count: %u", count); 340 341 data->endpt = endpt; 342 data->count = count; 343 344 if ((r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes)data->iovec, 345 sizeof(data->iovec[0]) * count)) != OK) 346 panic("netdriver: unable to copy in I/O vector: %d", r); 347 348 for (i = 0; i < count; i++) 349 data->size += data->iovec[i].iov_size; 350 351 if (data->size < ETH_MIN_PACK_SIZE || 352 (!do_write && data->size < ETH_MAX_PACK_SIZE_TAGGED)) 353 panic("netdriver: invalid I/O vector size: %zu\n", data->size); 354 355 /* Save the endpoint to which we should reply. */ 356 if (pending_endpt != NONE && pending_endpt != endpt) 357 panic("netdriver: multiple request sources"); 358 pending_endpt = endpt; 359 360 /* Resume sending or receiving. */ 361 defer_replies(); 362 363 if (do_write) 364 netdriver_send(); 365 else 366 netdriver_recv(); 367 368 /* Always send a reply in this case, even if no flags are set. */ 369 check_replies(TRUE /*deferred*/, TRUE /*always_send*/); 370 } 371 372 /* 373 * Process a request to configure the driver, by setting its mode and obtaining 374 * its ethernet hardware address. We already have the latter as a result of 375 * calling the ndr_init callback function. 376 */ 377 static void 378 do_conf(const struct netdriver * __restrict ndp, 379 const message * __restrict m_ptr) 380 { 381 message m_reply; 382 383 if (ndp->ndr_mode != NULL) 384 ndp->ndr_mode(m_ptr->m_net_netdrv_dl_conf.mode); 385 386 memset(&m_reply, 0, sizeof(m_reply)); 387 m_reply.m_type = DL_CONF_REPLY; 388 m_reply.m_netdrv_net_dl_conf.stat = OK; /* legacy */ 389 memcpy(&m_reply.m_netdrv_net_dl_conf.hw_addr, &hw_addr, 390 sizeof(m_reply.m_netdrv_net_dl_conf.hw_addr)); 391 392 send_reply(m_ptr->m_source, &m_reply); 393 } 394 395 /* 396 * Process a request to obtain statistics from the driver. 397 */ 398 static void 399 do_getstat(const struct netdriver * __restrict ndp, 400 const message * __restrict m_ptr) 401 { 402 message m_reply; 403 eth_stat_t st; 404 int r; 405 406 memset(&st, 0, sizeof(st)); 407 408 if (ndp->ndr_stat != NULL) 409 ndp->ndr_stat(&st); 410 411 if ((r = sys_safecopyto(m_ptr->m_source, 412 m_ptr->m_net_netdrv_dl_getstat_s.grant, 0, (vir_bytes)&st, 413 sizeof(st))) != OK) 414 panic("netdriver: unable to copy out statistics: %d", r); 415 416 memset(&m_reply, 0, sizeof(m_reply)); 417 m_reply.m_type = DL_STAT_REPLY; 418 419 send_reply(m_ptr->m_source, &m_reply); 420 } 421 422 /* 423 * Process an incoming message, and send a reply. 424 */ 425 void 426 netdriver_process(const struct netdriver * __restrict ndp, 427 const message * __restrict m_ptr, int ipc_status) 428 { 429 430 netdriver_table = ndp; 431 432 /* Check for notifications first. */ 433 if (is_ipc_notify(ipc_status)) { 434 defer_replies(); 435 436 switch (_ENDPOINT_P(m_ptr->m_source)) { 437 case HARDWARE: 438 if (ndp->ndr_intr != NULL) 439 ndp->ndr_intr(m_ptr->m_notify.interrupts); 440 break; 441 442 case CLOCK: 443 if (ndp->ndr_alarm != NULL) 444 ndp->ndr_alarm(m_ptr->m_notify.timestamp); 445 break; 446 447 default: 448 if (ndp->ndr_other != NULL) 449 ndp->ndr_other(m_ptr, ipc_status); 450 } 451 452 /* 453 * Any of the above calls may end up invoking netdriver_send() 454 * and/or netdriver_recv(), which may in turn have deferred 455 * sending a reply to an earlier request. See if we have to 456 * send the reply now. 457 */ 458 check_replies(TRUE /*deferred*/, FALSE /*always_send*/); 459 } 460 461 /* 462 * Discard datalink requests preceding a first DL_CONF request, so that 463 * after a driver restart, any in-flight request is discarded. This is 464 * a rather blunt approach and must be revised if the protocol is ever 465 * made less inefficient (i.e. not strictly serialized). Note that for 466 * correct driver operation it is important that non-datalink requests, 467 * interrupts in particular, do not go through this check. 468 */ 469 if (IS_DL_RQ(m_ptr->m_type) && conf_expected) { 470 if (m_ptr->m_type != DL_CONF) 471 return; /* do not send a reply */ 472 473 conf_expected = FALSE; 474 } 475 476 switch (m_ptr->m_type) { 477 case DL_CONF: 478 do_conf(ndp, m_ptr); 479 break; 480 481 case DL_GETSTAT_S: 482 do_getstat(ndp, m_ptr); 483 break; 484 485 case DL_READV_S: 486 do_readwrite(ndp, m_ptr->m_source, 487 m_ptr->m_net_netdrv_dl_readv_s.grant, 488 m_ptr->m_net_netdrv_dl_readv_s.count, FALSE /*do_write*/); 489 break; 490 491 case DL_WRITEV_S: 492 do_readwrite(ndp, m_ptr->m_source, 493 m_ptr->m_net_netdrv_dl_writev_s.grant, 494 m_ptr->m_net_netdrv_dl_writev_s.count, TRUE /*do_write*/); 495 break; 496 497 default: 498 defer_replies(); 499 500 if (ndp->ndr_other != NULL) 501 ndp->ndr_other(m_ptr, ipc_status); 502 503 /* As above: see if we have to send a reply now. */ 504 check_replies(TRUE /*deferred*/, FALSE /*always_send*/); 505 } 506 } 507 508 /* 509 * Perform initialization. Return OK or an error code. 510 */ 511 int 512 netdriver_init(const struct netdriver * ndp) 513 { 514 unsigned int instance; 515 long v; 516 int r; 517 518 /* Initialize global variables. */ 519 pending_recv.size = 0; 520 pending_send.size = 0; 521 pending_endpt = NONE; 522 defer_reply = FALSE; 523 pending_flags = 0; 524 pending_size = 0; 525 conf_expected = TRUE; 526 527 /* Get the card instance number. */ 528 v = 0; 529 (void)env_parse("instance", "d", 0, &v, 0, 255); 530 instance = (unsigned int)v; 531 532 /* Call the initialization routine. */ 533 memset(&hw_addr, 0, sizeof(hw_addr)); 534 535 if (ndp->ndr_init != NULL && 536 (r = ndp->ndr_init(instance, &hw_addr)) != OK) 537 return r; 538 539 /* Announce we are up! */ 540 netdriver_announce(); 541 542 return OK; 543 } 544 545 /* 546 * SEF initialization function. 547 */ 548 static int 549 do_init(int __unused type, sef_init_info_t * __unused info) 550 { 551 const struct netdriver *ndp; 552 553 ndp = netdriver_table; 554 assert(ndp != NULL); 555 556 return netdriver_init(ndp); 557 } 558 559 /* 560 * Break out of the main loop after finishing the current request. 561 */ 562 void 563 netdriver_terminate(void) 564 { 565 566 if (netdriver_table != NULL && netdriver_table->ndr_stop != NULL) 567 netdriver_table->ndr_stop(); 568 569 running = FALSE; 570 571 sef_cancel(); 572 } 573 574 /* 575 * The process has received a signal. See if we have to terminate. 576 */ 577 static void 578 got_signal(int sig) 579 { 580 581 if (sig != SIGTERM) 582 return; 583 584 netdriver_terminate(); 585 } 586 587 /* 588 * Main program of any network driver. 589 */ 590 void 591 netdriver_task(const struct netdriver * ndp) 592 { 593 message mess; 594 int r, ipc_status; 595 596 /* Perform SEF initialization. */ 597 sef_setcb_init_fresh(do_init); 598 sef_setcb_signal_handler(got_signal); 599 600 netdriver_table = ndp; 601 602 sef_startup(); 603 604 netdriver_table = NULL; 605 606 /* The main message loop. */ 607 running = TRUE; 608 609 while (running) { 610 if ((r = sef_receive_status(ANY, &mess, &ipc_status)) != OK) { 611 if (r == EINTR) 612 continue; /* sef_cancel() was called */ 613 614 panic("netdriver: sef_receive_status failed: %d", r); 615 } 616 617 netdriver_process(ndp, &mess, ipc_status); 618 } 619 } 620