1 /* LWIP service - ndev.c - network driver communication module */ 2 /* 3 * There is almost a one-to-one mapping between network device driver (ndev) 4 * objects and ethernet interface (ethif) objects, with as major difference 5 * that there may be an ndev object but not an ethif object for a driver that 6 * is known to exist but has not yet replied to our initialization request: 7 * without the information from the initialization request, there is no point 8 * creating an ethif object just yet, while we do need to track the driver 9 * process. TODO: it would be nice if unanswered init requests timed out and 10 * caused the removal of the ndev object after a while. 11 * 12 * Beyond that, this module aims to abstract away the low-level details of 13 * communication, memory grants, and driver restarts. Driver restarts are not 14 * fully transparent to the ethif module because it needs to reinitialize 15 * driver state only it knows about after a restart. Drivers that are in the 16 * process of restarting and therefore not operational are said to be disabled. 17 * 18 * From this module's point of view, a network driver is one of two states: 19 * initializing, where it has yet to respond to our initialization request, and 20 * active, where it is expected to accept and respond to all other requests. 21 * This module does not keep track of higher-level states and rules however; 22 * that is left to the ethif layer on one side, and the network driver itself 23 * on the other side. One important example is the interface being up or down: 24 * the ndev layer will happily forward send and receive requests when the 25 * interface is down, but these requests will be (resp.) dropped and rejected 26 * by the network driver in that state, and will not be generated by the ethif 27 * layer when the layer is down. Imposing barriers between configure and send 28 * requests is also left to the other parties. 29 * 30 * In this module, each active network driver has a send queue and a receive 31 * queue. The send queue is shared for packet send requests and configuration 32 * change requests. The receive queue is used for packet receive requests 33 * only. Each queue has a maximum depth, which is the minimum of a value 34 * provided by the network driver during initialization and local restrictions. 35 * These local restrictions are different for the two queue types: the receive 36 * queue is always bounded to a hardcoded value, while the send queue has a 37 * guaranteed minimum depth but may use up to the driver's maximum using spare 38 * entries. For both, a minimum depth is always available, since it is not 39 * possible to cancel individual send or receive requests after they have been 40 * sent to a particular driver. This does mean that we necessarily waste a 41 * large number of request structures in the common case. 42 * 43 * The general API model does not support the notion of blocking calls. While 44 * it would make sense to retrieve e.g. error statistics from the driver only 45 * when requested by userland, implementing this without threads would be 46 * seriously complicated, because such requests can have many origins (ioctl, 47 * PF_ROUTE message, sysctl). Instead, we rely on drivers updating us with the 48 * latest information on everything at all times, so that we can hand over a 49 * cached copy of (e.g.) those error statistics right away. We provide a means 50 * for drivers to perform rate limiting of such status updates (to prevent 51 * overflowing asynsend queues), by replying to these status messages. That 52 * means that there is a request-response combo going in the opposite direction 53 * of the regular messages. 54 * 55 * TODO: in the future we will want to obtain the list of supported media modes 56 * (IFM_) from drivers, so that userland can view the list. Given the above 57 * model, the easiest way would be to obtain a copy of the full list, limited 58 * to a configured number of entries, at driver initialization time. This 59 * would require that the initialization request also involve a memory grant. 60 * 61 * If necessary, it would not be too much work to split off this module into 62 * its own libndev library. For now, there is no point in doing this and the 63 * tighter coupling allows us to optimize just a little but (see pbuf usage). 64 */ 65 66 #include "lwip.h" 67 #include "ndev.h" 68 #include "ethif.h" 69 70 #define LABEL_MAX 16 /* FIXME: this should be in a system header */ 71 72 #define NDEV_SENDQ 2 /* minimum guaranteed send queue depth */ 73 #define NDEV_RECVQ 2 /* guaranteed receive queue depth */ 74 #define NREQ_SPARES 8 /* spare send queue (request) objects */ 75 #define NR_NREQ ((NDEV_SENDQ + NDEV_RECVQ) * NR_NDEV + NREQ_SPARES) 76 77 static SIMPLEQ_HEAD(, ndev_req) nreq_freelist; 78 79 static struct ndev_req { 80 SIMPLEQ_ENTRY(ndev_req) nreq_next; /* next request in queue */ 81 int nreq_type; /* type of request message */ 82 cp_grant_id_t nreq_grant[NDEV_IOV_MAX]; /* grants for request */ 83 } nreq_array[NR_NREQ]; 84 85 static unsigned int nreq_spares; /* number of free spare objects */ 86 87 struct ndev_queue { 88 uint32_t nq_head; /* ID of oldest pending request */ 89 uint8_t nq_count; /* current nr of pending requests */ 90 uint8_t nq_max; /* maximum nr of pending requests */ 91 SIMPLEQ_HEAD(, ndev_req) nq_req; /* queue of pending requests */ 92 }; 93 94 static struct ndev { 95 endpoint_t ndev_endpt; /* driver endpoint */ 96 char ndev_label[LABEL_MAX]; /* driver label */ 97 struct ethif *ndev_ethif; /* ethif object, or NULL if init'ing */ 98 struct ndev_queue ndev_sendq; /* packet send and configure queue */ 99 struct ndev_queue ndev_recvq; /* packet receive queue */ 100 } ndev_array[NR_NDEV]; 101 102 static ndev_id_t ndev_max; /* highest driver count ever seen */ 103 104 /* 105 * This macro checks whether the network driver is active rather than 106 * initializing. See above for more information. 107 */ 108 #define NDEV_ACTIVE(ndev) ((ndev)->ndev_sendq.nq_max > 0) 109 110 static int ndev_pending; /* number of initializing drivers */ 111 112 /* The CTL_MINIX MINIX_LWIP "drivers" subtree. Dynamically numbered. */ 113 static struct rmib_node minix_lwip_drivers_table[] = { 114 RMIB_INTPTR(RMIB_RO, &ndev_pending, "pending", 115 "Number of drivers currently initializing"), 116 }; 117 118 static struct rmib_node minix_lwip_drivers_node = 119 RMIB_NODE(RMIB_RO, minix_lwip_drivers_table, "drivers", 120 "Network driver information"); 121 122 /* 123 * Initialize the network driver communication module. 124 */ 125 void 126 ndev_init(void) 127 { 128 unsigned int slot; 129 int r; 130 131 /* Initialize local variables. */ 132 ndev_max = 0; 133 134 SIMPLEQ_INIT(&nreq_freelist); 135 136 for (slot = 0; slot < __arraycount(nreq_array); slot++) 137 SIMPLEQ_INSERT_TAIL(&nreq_freelist, &nreq_array[slot], 138 nreq_next); 139 140 nreq_spares = NREQ_SPARES; 141 142 /* 143 * Preallocate the total number of grants that we could possibly need 144 * concurrently. Even though it is extremely unlikely that we will 145 * ever need that many grants in practice, the alternative is runtime 146 * dynamic memory (re)allocation which is something we prefer to avoid 147 * altogether. At time of writing, we end up preallocating 320 grants 148 * using up a total of a bit under 9KB of memory. 149 */ 150 cpf_prealloc(NR_NREQ * NDEV_IOV_MAX); 151 152 153 /* 154 * Not needed, just for ultimate safety: start off all queues with 155 * wildly different request sequence numbers, to minimize the chance 156 * that any two replies will ever be confused. 157 */ 158 for (slot = 0; slot < __arraycount(ndev_array); slot++) { 159 ndev_array[slot].ndev_sendq.nq_head = slot << 21; 160 ndev_array[slot].ndev_recvq.nq_head = (slot * 2 + 1) << 20; 161 } 162 163 /* Subscribe to Data Store (DS) events from network drivers. */ 164 if ((r = ds_subscribe("drv\\.net\\..*", 165 DSF_INITIAL | DSF_OVERWRITE)) != OK) 166 panic("unable to subscribe to driver events: %d", r); 167 168 /* 169 * Keep track of how many drivers are in "pending" state, which means 170 * that they have not yet replied to our initialization request. 171 */ 172 ndev_pending = 0; 173 174 /* Register the minix.lwip.drivers subtree. */ 175 mibtree_register_lwip(&minix_lwip_drivers_node); 176 } 177 178 /* 179 * Initialize a queue for first use. 180 */ 181 static void 182 ndev_queue_init(struct ndev_queue * nq) 183 { 184 185 /* 186 * Only ever increase sequence numbers, to minimize the chance that 187 * two (e.g. from different driver instances) happen to be the same. 188 */ 189 nq->nq_head++; 190 191 nq->nq_count = 0; 192 nq->nq_max = 0; 193 SIMPLEQ_INIT(&nq->nq_req); 194 } 195 196 /* 197 * Advance the given request queue, freeing up the request at the head of the 198 * queue including any grants in use for it. 199 */ 200 static void 201 ndev_queue_advance(struct ndev_queue * nq) 202 { 203 struct ndev_req * nreq; 204 cp_grant_id_t grant; 205 unsigned int i; 206 207 nreq = SIMPLEQ_FIRST(&nq->nq_req); 208 209 for (i = 0; i < __arraycount(nreq->nreq_grant); i++) { 210 grant = nreq->nreq_grant[i]; 211 212 if (!GRANT_VALID(grant)) 213 break; 214 215 /* TODO: make the safecopies code stop using errno. */ 216 if (cpf_revoke(grant) != 0) 217 panic("unable to revoke grant: %d", -errno); 218 } 219 220 if (nreq->nreq_type != NDEV_RECV && nq->nq_count > NDEV_SENDQ) { 221 nreq_spares++; 222 223 assert(nreq_spares <= NREQ_SPARES); 224 } 225 226 SIMPLEQ_REMOVE_HEAD(&nq->nq_req, nreq_next); 227 228 SIMPLEQ_INSERT_HEAD(&nreq_freelist, nreq, nreq_next); 229 230 nq->nq_head++; 231 nq->nq_count--; 232 } 233 234 /* 235 * Clear any outstanding requests from the given queue and reset it to a 236 * pre-initialization state. 237 */ 238 static void 239 ndev_queue_reset(struct ndev_queue * nq) 240 { 241 242 while (nq->nq_count > 0) { 243 assert(!SIMPLEQ_EMPTY(&nq->nq_req)); 244 245 ndev_queue_advance(nq); 246 } 247 248 nq->nq_max = 0; 249 } 250 251 /* 252 * Obtain a request object for use in a new request. Return the request 253 * object, with its request type field set to 'type', and with the request 254 * sequence ID returned in 'seq'. Return NULL if no request objects are 255 * available for the given request type. If the caller does send off the 256 * request, a call to ndev_queue_add() must follow immediately after. If the 257 * caller fails to send off the request for other reasons, it need not do 258 * anything: this function does not perform any actions that need to be undone. 259 */ 260 static struct ndev_req * 261 ndev_queue_get(struct ndev_queue * nq, int type, uint32_t * seq) 262 { 263 struct ndev_req *nreq; 264 265 /* Has the hard queue depth limit been reached? */ 266 if (nq->nq_count == nq->nq_max) 267 return NULL; 268 269 /* 270 * For send requests, we may use request objects from a shared "spares" 271 * pool, if available. 272 */ 273 if (type != NDEV_RECV && nq->nq_count >= NDEV_SENDQ && 274 nreq_spares == 0) 275 return NULL; 276 277 assert(!SIMPLEQ_EMPTY(&nreq_freelist)); 278 nreq = SIMPLEQ_FIRST(&nreq_freelist); 279 280 nreq->nreq_type = type; 281 282 *seq = nq->nq_head + nq->nq_count; 283 284 return nreq; 285 } 286 287 /* 288 * Add a successfully sent request to the given queue. The request must have 289 * been obtained using ndev_queue_get() directly before the call to this 290 * function. This function never fails. 291 */ 292 static void 293 ndev_queue_add(struct ndev_queue * nq, struct ndev_req * nreq) 294 { 295 296 if (nreq->nreq_type != NDEV_RECV && nq->nq_count >= NDEV_SENDQ) { 297 assert(nreq_spares > 0); 298 299 nreq_spares--; 300 } 301 302 SIMPLEQ_REMOVE_HEAD(&nreq_freelist, nreq_next); 303 304 SIMPLEQ_INSERT_TAIL(&nq->nq_req, nreq, nreq_next); 305 306 nq->nq_count++; 307 } 308 309 /* 310 * Remove the head of the given request queue, but only if it matches the given 311 * request type and sequence ID. Return TRUE if the head was indeed removed, 312 * or FALSE if the head of the request queue (if any) did not match the given 313 * type and/or sequence ID. 314 */ 315 static int 316 ndev_queue_remove(struct ndev_queue * nq, int type, uint32_t seq) 317 { 318 struct ndev_req *nreq; 319 320 if (nq->nq_count < 1 || nq->nq_head != seq) 321 return FALSE; 322 323 assert(!SIMPLEQ_EMPTY(&nq->nq_req)); 324 nreq = SIMPLEQ_FIRST(&nq->nq_req); 325 326 if (nreq->nreq_type != type) 327 return FALSE; 328 329 ndev_queue_advance(nq); 330 331 return TRUE; 332 } 333 334 /* 335 * Send an initialization request to a driver. If this is a new driver, the 336 * ethif module does not get to know about the driver until it answers to this 337 * request, as the ethif module needs much of what the reply contains. On the 338 * other hand, if this is a restarted driver, it will stay disabled until the 339 * init reply comes in. 340 */ 341 static void 342 ndev_send_init(struct ndev * ndev) 343 { 344 message m; 345 int r; 346 347 memset(&m, 0, sizeof(m)); 348 m.m_type = NDEV_INIT; 349 m.m_ndev_netdriver_init.id = ndev->ndev_sendq.nq_head; 350 351 if ((r = asynsend3(ndev->ndev_endpt, &m, AMF_NOREPLY)) != OK) 352 panic("asynsend to driver failed: %d", r); 353 } 354 355 /* 356 * A network device driver has been started or restarted. 357 */ 358 static void 359 ndev_up(const char * label, endpoint_t endpt) 360 { 361 static int reported = FALSE; 362 struct ndev *ndev; 363 ndev_id_t slot; 364 365 /* 366 * First see if we already had an entry for this driver. If so, it has 367 * been restarted, and we need to report it as not running to ethif. 368 */ 369 ndev = NULL; 370 371 for (slot = 0; slot < ndev_max; slot++) { 372 if (ndev_array[slot].ndev_endpt == NONE) { 373 if (ndev == NULL) 374 ndev = &ndev_array[slot]; 375 376 continue; 377 } 378 379 if (!strcmp(ndev_array[slot].ndev_label, label)) { 380 /* Cancel any ongoing requests. */ 381 ndev_queue_reset(&ndev_array[slot].ndev_sendq); 382 ndev_queue_reset(&ndev_array[slot].ndev_recvq); 383 384 if (ndev_array[slot].ndev_ethif != NULL) { 385 ethif_disable(ndev_array[slot].ndev_ethif); 386 387 ndev_pending++; 388 } 389 390 ndev_array[slot].ndev_endpt = endpt; 391 392 /* Attempt to resume communication. */ 393 ndev_send_init(&ndev_array[slot]); 394 395 return; 396 } 397 } 398 399 if (ndev == NULL) { 400 /* 401 * If there is no free slot for this driver in our table, we 402 * necessarily have to ignore the driver altogether. We report 403 * such cases once, so that the user can recompile if desired. 404 */ 405 if (ndev_max == __arraycount(ndev_array)) { 406 if (!reported) { 407 printf("LWIP: not enough ndev slots!\n"); 408 409 reported = TRUE; 410 } 411 return; 412 } 413 414 ndev = &ndev_array[ndev_max++]; 415 } 416 417 /* Initialize the slot. */ 418 ndev->ndev_endpt = endpt; 419 strlcpy(ndev->ndev_label, label, sizeof(ndev->ndev_label)); 420 ndev->ndev_ethif = NULL; 421 ndev_queue_init(&ndev->ndev_sendq); 422 ndev_queue_init(&ndev->ndev_recvq); 423 424 ndev_send_init(ndev); 425 426 ndev_pending++; 427 } 428 429 /* 430 * A network device driver has been terminated. 431 */ 432 static void 433 ndev_down(struct ndev * ndev) 434 { 435 436 /* Cancel any ongoing requests. */ 437 ndev_queue_reset(&ndev->ndev_sendq); 438 ndev_queue_reset(&ndev->ndev_recvq); 439 440 /* 441 * If this ndev object had a corresponding ethif object, tell the ethif 442 * layer that the device is really gone now. 443 */ 444 if (ndev->ndev_ethif != NULL) 445 ethif_remove(ndev->ndev_ethif); 446 else 447 ndev_pending--; 448 449 /* Remove the driver from our own administration. */ 450 ndev->ndev_endpt = NONE; 451 452 while (ndev_max > 0 && ndev_array[ndev_max - 1].ndev_endpt == NONE) 453 ndev_max--; 454 } 455 456 /* 457 * The DS service has notified us of changes to our subscriptions. That means 458 * that network drivers may have been started, restarted, and/or shut down. 459 * Find out what has changed, and act accordingly. 460 */ 461 void 462 ndev_check(void) 463 { 464 static const char *prefix = "drv.net."; 465 char key[DS_MAX_KEYLEN], *label; 466 size_t prefixlen; 467 endpoint_t endpt; 468 uint32_t val; 469 ndev_id_t slot; 470 int r; 471 472 prefixlen = strlen(prefix); 473 474 /* Check whether any drivers have been (re)started. */ 475 while ((r = ds_check(key, NULL, &endpt)) == OK) { 476 if (strncmp(key, prefix, prefixlen) != 0 || endpt == NONE) 477 continue; 478 479 if (ds_retrieve_u32(key, &val) != OK || val != DS_DRIVER_UP) 480 continue; 481 482 label = &key[prefixlen]; 483 if (label[0] == '\0' || memchr(label, '\0', LABEL_MAX) == NULL) 484 continue; 485 486 ndev_up(label, endpt); 487 } 488 489 if (r != ENOENT) 490 printf("LWIP: DS check failed (%d)\n", r); 491 492 /* 493 * Check whether the drivers we currently know about are still up. The 494 * ones that are not are really gone. It is no problem that we recheck 495 * any drivers that have just been reported by ds_check() above. 496 * However, we cannot check the same key: while the driver is being 497 * restarted, its driver status is already gone from DS. Instead, see 498 * if there is still an entry for its label, as that entry remains in 499 * existence during the restart. The associated endpoint may still 500 * change however, so do not check that part: in such cases we will get 501 * a driver-up announcement later anyway. 502 */ 503 for (slot = 0; slot < ndev_max; slot++) { 504 if (ndev_array[slot].ndev_endpt == NONE) 505 continue; 506 507 if (ds_retrieve_label_endpt(ndev_array[slot].ndev_label, 508 &endpt) != OK) 509 ndev_down(&ndev_array[slot]); 510 } 511 } 512 513 /* 514 * A network device driver has sent a reply to our initialization request. 515 */ 516 static void 517 ndev_init_reply(struct ndev * ndev, const message * m_ptr) 518 { 519 struct ndev_hwaddr hwaddr; 520 uint8_t hwaddr_len, max_send, max_recv; 521 const char *name; 522 int enabled; 523 524 /* 525 * Make sure that we were waiting for a reply to an initialization 526 * request, and that this is the reply to that request. 527 */ 528 if (NDEV_ACTIVE(ndev) || 529 m_ptr->m_netdriver_ndev_init_reply.id != ndev->ndev_sendq.nq_head) 530 return; 531 532 /* 533 * Do just enough sanity checking on the data to pass it up to the 534 * ethif layer, which will check the rest (e.g., name duplicates). 535 */ 536 if (memchr(m_ptr->m_netdriver_ndev_init_reply.name, '\0', 537 sizeof(m_ptr->m_netdriver_ndev_init_reply.name)) == NULL || 538 m_ptr->m_netdriver_ndev_init_reply.name[0] == '\0') { 539 printf("LWIP: driver %d provided invalid name\n", 540 m_ptr->m_source); 541 542 ndev_down(ndev); 543 544 return; 545 } 546 547 hwaddr_len = m_ptr->m_netdriver_ndev_init_reply.hwaddr_len; 548 if (hwaddr_len < 1 || hwaddr_len > __arraycount(hwaddr.nhwa_addr)) { 549 printf("LWIP: driver %d provided invalid HW-addr length\n", 550 m_ptr->m_source); 551 552 ndev_down(ndev); 553 554 return; 555 } 556 557 if ((max_send = m_ptr->m_netdriver_ndev_init_reply.max_send) < 1 || 558 (max_recv = m_ptr->m_netdriver_ndev_init_reply.max_recv) < 1) { 559 printf("LWIP: driver %d provided invalid queue maximum\n", 560 m_ptr->m_source); 561 562 ndev_down(ndev); 563 564 return; 565 } 566 567 /* 568 * If the driver is new, allocate a new ethif object for it. On 569 * success, or if the driver was restarted, (re)enable the interface. 570 * Both calls may fail, in which case we should forget about the 571 * driver. It may continue to send us messages, which we should then 572 * discard. 573 */ 574 name = m_ptr->m_netdriver_ndev_init_reply.name; 575 576 if (ndev->ndev_ethif == NULL) { 577 ndev->ndev_ethif = ethif_add((ndev_id_t)(ndev - ndev_array), 578 name, m_ptr->m_netdriver_ndev_init_reply.caps); 579 name = NULL; 580 } 581 582 if (ndev->ndev_ethif != NULL) { 583 /* 584 * Set the maximum numbers of pending requests (for each 585 * direction) first, because enabling the interface may cause 586 * the ethif layer to start sending requests immediately. 587 */ 588 ndev->ndev_sendq.nq_max = max_send; 589 ndev->ndev_sendq.nq_head++; 590 591 /* 592 * Limit the maximum number of concurrently pending receive 593 * requests to our configured maximum. For send requests, we 594 * use a more dynamic approach with spare request objects. 595 */ 596 if (max_recv > NDEV_RECVQ) 597 max_recv = NDEV_RECVQ; 598 ndev->ndev_recvq.nq_max = max_recv; 599 ndev->ndev_recvq.nq_head++; 600 601 memset(&hwaddr, 0, sizeof(hwaddr)); 602 memcpy(hwaddr.nhwa_addr, 603 m_ptr->m_netdriver_ndev_init_reply.hwaddr, hwaddr_len); 604 605 /* 606 * Provide a NULL pointer for the name if we have only just 607 * added the interface at all. The callee may use this to 608 * determine whether the driver is new or has been restarted. 609 */ 610 enabled = ethif_enable(ndev->ndev_ethif, name, &hwaddr, 611 m_ptr->m_netdriver_ndev_init_reply.hwaddr_len, 612 m_ptr->m_netdriver_ndev_init_reply.caps, 613 m_ptr->m_netdriver_ndev_init_reply.link, 614 m_ptr->m_netdriver_ndev_init_reply.media); 615 } else 616 enabled = FALSE; 617 618 /* 619 * If we did not manage to enable the interface, remove it again, 620 * possibly also from the ethif layer. 621 */ 622 if (!enabled) 623 ndev_down(ndev); 624 else 625 ndev_pending--; 626 } 627 628 /* 629 * Request that a network device driver change its configuration. This 630 * function allows for configuration of various different driver and device 631 * aspects: the I/O mode (and multicast receipt list), the enabled (sub)set of 632 * capabilities, the driver-specific flags, and the hardware address. Each of 633 * these settings may be changed by setting the corresponding NDEV_SET_ flag in 634 * the 'set' field of the given configuration structure. It is explicitly 635 * allowed to generate a request with no NDEV_SET_ flags; such a request will 636 * be sent to the driver and ultimately generate a response. Return OK if the 637 * configuration request was sent to the driver, EBUSY if no (more) requests 638 * can be sent to the driver right now, or ENOMEM on grant allocation failure. 639 */ 640 int 641 ndev_conf(ndev_id_t id, const struct ndev_conf * nconf) 642 { 643 struct ndev *ndev; 644 struct ndev_req *nreq; 645 uint32_t seq; 646 message m; 647 cp_grant_id_t grant; 648 int r; 649 650 assert(id < __arraycount(ndev_array)); 651 ndev = &ndev_array[id]; 652 653 assert(ndev->ndev_endpt != NONE); 654 assert(NDEV_ACTIVE(ndev)); 655 656 if ((nreq = ndev_queue_get(&ndev->ndev_sendq, NDEV_CONF, 657 &seq)) == NULL) 658 return EBUSY; 659 660 memset(&m, 0, sizeof(m)); 661 m.m_type = NDEV_CONF; 662 m.m_ndev_netdriver_conf.id = seq; 663 m.m_ndev_netdriver_conf.set = nconf->nconf_set; 664 665 grant = GRANT_INVALID; 666 667 if (nconf->nconf_set & NDEV_SET_MODE) { 668 m.m_ndev_netdriver_conf.mode = nconf->nconf_mode; 669 670 if (nconf->nconf_mode & NDEV_MODE_MCAST_LIST) { 671 assert(nconf->nconf_mclist != NULL); 672 assert(nconf->nconf_mccount != 0); 673 674 grant = cpf_grant_direct(ndev->ndev_endpt, 675 (vir_bytes)nconf->nconf_mclist, 676 sizeof(nconf->nconf_mclist[0]) * 677 nconf->nconf_mccount, CPF_READ); 678 679 if (!GRANT_VALID(grant)) 680 return ENOMEM; 681 682 m.m_ndev_netdriver_conf.mcast_count = 683 nconf->nconf_mccount; 684 } 685 } 686 687 m.m_ndev_netdriver_conf.mcast_grant = grant; 688 689 if (nconf->nconf_set & NDEV_SET_CAPS) 690 m.m_ndev_netdriver_conf.caps = nconf->nconf_caps; 691 692 if (nconf->nconf_set & NDEV_SET_FLAGS) 693 m.m_ndev_netdriver_conf.flags = nconf->nconf_flags; 694 695 if (nconf->nconf_set & NDEV_SET_MEDIA) 696 m.m_ndev_netdriver_conf.media = nconf->nconf_media; 697 698 if (nconf->nconf_set & NDEV_SET_HWADDR) 699 memcpy(m.m_ndev_netdriver_conf.hwaddr, 700 nconf->nconf_hwaddr.nhwa_addr, 701 __arraycount(m.m_ndev_netdriver_conf.hwaddr)); 702 703 if ((r = asynsend3(ndev->ndev_endpt, &m, AMF_NOREPLY)) != OK) 704 panic("asynsend to driver failed: %d", r); 705 706 nreq->nreq_grant[0] = grant; /* may also be invalid */ 707 nreq->nreq_grant[1] = GRANT_INVALID; 708 709 ndev_queue_add(&ndev->ndev_sendq, nreq); 710 711 return OK; 712 } 713 714 /* 715 * The network device driver has sent a reply to a configuration request. 716 */ 717 static void 718 ndev_conf_reply(struct ndev * ndev, const message * m_ptr) 719 { 720 721 /* 722 * Was this the request we were waiting for? If so, remove it from the 723 * send queue. Otherwise, ignore this reply message. 724 */ 725 if (!NDEV_ACTIVE(ndev) || !ndev_queue_remove(&ndev->ndev_sendq, 726 NDEV_CONF, m_ptr->m_netdriver_ndev_reply.id)) 727 return; 728 729 /* Tell the ethif layer about the updated configuration. */ 730 assert(ndev->ndev_ethif != NULL); 731 732 ethif_configured(ndev->ndev_ethif, 733 m_ptr->m_netdriver_ndev_reply.result); 734 } 735 736 /* 737 * Construct a packet send or receive request and send it off to a network 738 * driver. The given pbuf chain may be part of a queue. Return OK if the 739 * request was successfully sent, or ENOMEM on grant allocation failure. 740 */ 741 static int 742 ndev_transfer(struct ndev * ndev, const struct pbuf * pbuf, int do_send, 743 uint32_t seq, struct ndev_req * nreq) 744 { 745 cp_grant_id_t grant; 746 message m; 747 unsigned int i; 748 size_t left; 749 int r; 750 751 memset(&m, 0, sizeof(m)); 752 m.m_type = (do_send) ? NDEV_SEND : NDEV_RECV; 753 m.m_ndev_netdriver_transfer.id = seq; 754 755 left = pbuf->tot_len; 756 757 for (i = 0; left > 0; i++) { 758 assert(i < NDEV_IOV_MAX); 759 760 grant = cpf_grant_direct(ndev->ndev_endpt, 761 (vir_bytes)pbuf->payload, pbuf->len, 762 (do_send) ? CPF_READ : CPF_WRITE); 763 764 if (!GRANT_VALID(grant)) { 765 while (i-- > 0) 766 (void)cpf_revoke(nreq->nreq_grant[i]); 767 768 return ENOMEM; 769 } 770 771 m.m_ndev_netdriver_transfer.grant[i] = grant; 772 m.m_ndev_netdriver_transfer.len[i] = pbuf->len; 773 774 nreq->nreq_grant[i] = grant; 775 776 assert(left >= pbuf->len); 777 left -= pbuf->len; 778 pbuf = pbuf->next; 779 } 780 781 m.m_ndev_netdriver_transfer.count = i; 782 783 /* 784 * Unless the array is full, an invalid grant marks the end of the list 785 * of invalid grants. 786 */ 787 if (i < __arraycount(nreq->nreq_grant)) 788 nreq->nreq_grant[i] = GRANT_INVALID; 789 790 if ((r = asynsend3(ndev->ndev_endpt, &m, AMF_NOREPLY)) != OK) 791 panic("asynsend to driver failed: %d", r); 792 793 return OK; 794 } 795 796 /* 797 * Send a packet to the given network driver. Return OK if the packet is sent 798 * off to the driver, EBUSY if no (more) packets can be sent to the driver at 799 * this time, or ENOMEM on grant allocation failure. 800 * 801 * The use of 'pbuf' in this interface is a bit ugly, but it saves us from 802 * having to go through an intermediate representation (e.g. an iovec array) 803 * for the data being sent. The same applies to ndev_receive(). 804 */ 805 int 806 ndev_send(ndev_id_t id, const struct pbuf * pbuf) 807 { 808 struct ndev *ndev; 809 struct ndev_req *nreq; 810 uint32_t seq; 811 int r; 812 813 assert(id < __arraycount(ndev_array)); 814 ndev = &ndev_array[id]; 815 816 assert(ndev->ndev_endpt != NONE); 817 assert(NDEV_ACTIVE(ndev)); 818 819 if ((nreq = ndev_queue_get(&ndev->ndev_sendq, NDEV_SEND, 820 &seq)) == NULL) 821 return EBUSY; 822 823 if ((r = ndev_transfer(ndev, pbuf, TRUE /*do_send*/, seq, nreq)) != OK) 824 return r; 825 826 ndev_queue_add(&ndev->ndev_sendq, nreq); 827 828 return OK; 829 } 830 831 /* 832 * The network device driver has sent a reply to a send request. 833 */ 834 static void 835 ndev_send_reply(struct ndev * ndev, const message * m_ptr) 836 { 837 838 /* 839 * Was this the request we were waiting for? If so, remove it from the 840 * send queue. Otherwise, ignore this reply message. 841 */ 842 if (!NDEV_ACTIVE(ndev) || !ndev_queue_remove(&ndev->ndev_sendq, 843 NDEV_SEND, m_ptr->m_netdriver_ndev_reply.id)) 844 return; 845 846 /* Tell the ethif layer about the result of the transmission. */ 847 assert(ndev->ndev_ethif != NULL); 848 849 ethif_sent(ndev->ndev_ethif, 850 m_ptr->m_netdriver_ndev_reply.result); 851 } 852 853 /* 854 * Return TRUE if a new receive request can be spawned for a particular network 855 * driver, or FALSE if its queue of receive requests is full. This call exists 856 * merely to avoid needless buffer allocatin in the case that ndev_recv() is 857 * going to return EBUSY anyway. 858 */ 859 int 860 ndev_can_recv(ndev_id_t id) 861 { 862 struct ndev *ndev; 863 864 assert(id < __arraycount(ndev_array)); 865 ndev = &ndev_array[id]; 866 867 assert(ndev->ndev_endpt != NONE); 868 assert(NDEV_ACTIVE(ndev)); 869 870 return (ndev->ndev_recvq.nq_count < ndev->ndev_recvq.nq_max); 871 } 872 873 /* 874 * Start the process of receiving a packet from a network driver. The packet 875 * will be stored in the given pbuf chain upon completion. Return OK if the 876 * receive request is sent to the driver, EBUSY if the maximum number of 877 * concurrent receive requests has been reached for this driver, or ENOMEM on 878 * grant allocation failure. 879 */ 880 int 881 ndev_recv(ndev_id_t id, struct pbuf * pbuf) 882 { 883 struct ndev *ndev; 884 struct ndev_req *nreq; 885 uint32_t seq; 886 int r; 887 888 assert(id < __arraycount(ndev_array)); 889 ndev = &ndev_array[id]; 890 891 assert(ndev->ndev_endpt != NONE); 892 assert(NDEV_ACTIVE(ndev)); 893 894 if ((nreq = ndev_queue_get(&ndev->ndev_recvq, NDEV_RECV, 895 &seq)) == NULL) 896 return EBUSY; 897 898 if ((r = ndev_transfer(ndev, pbuf, FALSE /*do_send*/, seq, 899 nreq)) != OK) 900 return r; 901 902 ndev_queue_add(&ndev->ndev_recvq, nreq); 903 904 return OK; 905 } 906 907 /* 908 * The network device driver has sent a reply to a receive request. 909 */ 910 static void 911 ndev_recv_reply(struct ndev * ndev, const message * m_ptr) 912 { 913 914 /* 915 * Was this the request we were waiting for? If so, remove it from the 916 * receive queue. Otherwise, ignore this reply message. 917 */ 918 if (!NDEV_ACTIVE(ndev) || !ndev_queue_remove(&ndev->ndev_recvq, 919 NDEV_RECV, m_ptr->m_netdriver_ndev_reply.id)) 920 return; 921 922 /* Tell the ethif layer about the result of the receipt. */ 923 assert(ndev->ndev_ethif != NULL); 924 925 ethif_received(ndev->ndev_ethif, 926 m_ptr->m_netdriver_ndev_reply.result); 927 } 928 929 /* 930 * A network device driver sent a status report to us. Process it and send a 931 * reply. 932 */ 933 static void 934 ndev_status(struct ndev * ndev, const message * m_ptr) 935 { 936 message m; 937 int r; 938 939 if (!NDEV_ACTIVE(ndev)) 940 return; 941 942 /* Tell the ethif layer about the status update. */ 943 assert(ndev->ndev_ethif != NULL); 944 945 ethif_status(ndev->ndev_ethif, m_ptr->m_netdriver_ndev_status.link, 946 m_ptr->m_netdriver_ndev_status.media, 947 m_ptr->m_netdriver_ndev_status.oerror, 948 m_ptr->m_netdriver_ndev_status.coll, 949 m_ptr->m_netdriver_ndev_status.ierror, 950 m_ptr->m_netdriver_ndev_status.iqdrop); 951 952 /* 953 * Send a reply, so that the driver knows it can send a new status 954 * update without risking asynsend queue overflows. The ID of these 955 * messages is chosen by the driver and and we simply echo it. 956 */ 957 memset(&m, 0, sizeof(m)); 958 m.m_type = NDEV_STATUS_REPLY; 959 m.m_ndev_netdriver_status_reply.id = m_ptr->m_netdriver_ndev_status.id; 960 961 if ((r = asynsend(m_ptr->m_source, &m)) != OK) 962 panic("asynsend to driver failed: %d", r); 963 } 964 965 /* 966 * Process a network driver reply message. 967 */ 968 void 969 ndev_process(const message * m_ptr, int ipc_status) 970 { 971 struct ndev *ndev; 972 endpoint_t endpt; 973 ndev_id_t slot; 974 975 /* Find the slot of the driver that sent the message, if any. */ 976 endpt = m_ptr->m_source; 977 978 for (slot = 0, ndev = ndev_array; slot < ndev_max; slot++, ndev++) 979 if (ndev->ndev_endpt == endpt) 980 break; 981 982 /* 983 * If we cannot find a slot for the driver, drop the message. We may 984 * be ignoring the driver because it misbehaved or we are out of slots. 985 */ 986 if (slot == ndev_max) 987 return; 988 989 /* 990 * Process the reply message. For future compatibility, ignore any 991 * unrecognized message types. 992 */ 993 switch (m_ptr->m_type) { 994 case NDEV_INIT_REPLY: 995 ndev_init_reply(ndev, m_ptr); 996 997 break; 998 999 case NDEV_CONF_REPLY: 1000 ndev_conf_reply(ndev, m_ptr); 1001 1002 break; 1003 1004 case NDEV_SEND_REPLY: 1005 ndev_send_reply(ndev, m_ptr); 1006 1007 break; 1008 1009 case NDEV_RECV_REPLY: 1010 ndev_recv_reply(ndev, m_ptr); 1011 1012 break; 1013 1014 case NDEV_STATUS: 1015 ndev_status(ndev, m_ptr); 1016 1017 break; 1018 } 1019 } 1020