void recv_pkt_arp_reply(struct arp_pkt *arp, struct arp_cache *arp_cache, struct thread_context *contexts) { uint32_t idx, i; struct arp_cache_entry *e; int rv; assert(arp_cache); assert(contexts); assert(arp); idx = ntohl(arp->spa.s_addr - arp_cache->baseline); e = &arp_cache->values[idx]; // update the arp cache memcpy(&e->mac, &arp->sha, sizeof(arp->sha)); e->retries = 0; rv = gettimeofday(&e->timestamp, NULL); assert(rv == 0); e->timestamp.tv_sec += ARP_CACHE_LIFETIME; // and notify threads that are waiting for this arp entry for (i=0; i < MAX_THREADS; i++) { if (bitmap_get(e->waiters, i)) send_msg_get_mac_reply(&contexts[i], &arp->spa, &arp->sha); } bitmap_clearall(e->waiters, MAX_THREADS); }
void *dispatcher(void *threadarg) { assert(threadarg); struct thread_context *context; struct thread_context *contexts; int rv; struct netmap_ring *rxring; struct ethernet_pkt *etherpkt; struct pollfd pfd; struct dispatcher_data *data; uint32_t *slots_used, *open_transactions; uint32_t i, arpd_idx, num_threads; char *buf; struct msg_hdr *msg; struct ether_addr *mac; context = (struct thread_context *)threadarg; contexts = context->shared->contexts; data = context->data; arpd_idx = context->shared->arpd_idx; mac = &context->shared->if_info->mac; num_threads = context->shared->num_threads; struct transaction *transactions[num_threads]; uint64_t dropped[num_threads]; for (i=0; i < num_threads; i++) { transactions[i] = NULL; dropped[i] = 0; } rv = dispatcher_init(context); if (!rv) { pthread_exit(NULL); } rxring = NETMAP_RXRING(data->nifp, 0); slots_used = bitmap_new(rxring->num_slots); if (!slots_used) pthread_exit(NULL); open_transactions = bitmap_new(num_threads); if (!open_transactions) pthread_exit(NULL); pfd.fd = data->fd; pfd.events = (POLLIN); printf("dispatcher[%d]: initialized\n", context->thread_id); // signal to main() that we are initialized atomic_store_explicit(&context->initialized, 1, memory_order_release); for (;;) { rv = poll(&pfd, 1, POLL_TIMEOUT); // read all packets from the ring if (rv > 0) { for (; rxring->avail > 0; rxring->avail--) { i = rxring->cur; rxring->cur = NETMAP_RING_NEXT(rxring, i); rxring->reserved++; buf = NETMAP_BUF(rxring, rxring->slot[i].buf_idx); etherpkt = (struct ethernet_pkt *)(void *)buf; // TODO: consider pushing this check to the workers if (!ethernet_is_valid(etherpkt, mac)) { if (rxring->reserved == 1) rxring->reserved = 0; continue; } // TODO: dispatch to n workers instead of just 0 switch (etherpkt->h.ether_type) { case IP4_ETHERTYPE: rv = tqueue_insert(contexts[0].pkt_recv_q, &transactions[0], (char *) NULL + i); switch (rv) { case TQUEUE_SUCCESS: bitmap_set(slots_used, i); bitmap_set(open_transactions, 0); break; case TQUEUE_TRANSACTION_FULL: bitmap_set(slots_used, i); bitmap_clear(open_transactions, 0); break; case TQUEUE_FULL: // just drop packet and do accounting dropped[0]++; if (rxring->reserved == 1) rxring->reserved = 0; break; } break; case ARP_ETHERTYPE: rv = tqueue_insert(contexts[arpd_idx].pkt_recv_q, &transactions[arpd_idx], (char *) NULL + i); switch (rv) { case TQUEUE_SUCCESS: tqueue_publish_transaction(contexts[arpd_idx].pkt_recv_q, &transactions[arpd_idx]); bitmap_set(slots_used, i); break; case TQUEUE_TRANSACTION_FULL: bitmap_set(slots_used, i); break; case TQUEUE_FULL: // just drop packet and do accounting dropped[arpd_idx]++; if (rxring->reserved == 1) rxring->reserved = 0; break; } break; default: printf("dispatcher[%d]: unknown/unsupported ethertype %hu\n", context->thread_id, etherpkt->h.ether_type); if (rxring->reserved == 1) rxring->reserved = 0; } // switch (ethertype) } // for (rxring) // publish any open transactions so that the worker can start on it for (i=0; i < num_threads; i++) { if (bitmap_get(open_transactions, i)) tqueue_publish_transaction(contexts[i].pkt_recv_q, &transactions[i]); } bitmap_clearall(open_transactions, num_threads); } // if (packets) // read the message queue rv = squeue_enter(context->msg_q, 1); if (!rv) continue; while ((msg = squeue_get_next_pop_slot(context->msg_q)) != NULL) { switch (msg->msg_type) { case MSG_TRANSACTION_UPDATE: update_slots_used(context->msg_q, slots_used, rxring); break; case MSG_TRANSACTION_UPDATE_SINGLE: update_slots_used_single((void *)msg, slots_used, rxring); break; default: printf("dispatcher: unknown message %hu\n", msg->msg_type); } } squeue_exit(context->msg_q); } // for(;;) pthread_exit(NULL); }
// public function definitions void *worker(void *threadarg) { assert(threadarg); struct thread_context *context; struct thread_context *contexts; struct thread_context *dispatcher; struct worker_data *data; int rv; struct transaction *transaction = NULL; struct netmap_ring *rxring; void *ring_idx; uint32_t *slots_read; pktbuff *pktbuff_in, *pktbuff_out; int pktbuff_used = 1; struct pcb *pcb; struct msg_hdr *msg_hdr; context = (struct thread_context *)threadarg; contexts = context->shared->contexts; data = context->data; dispatcher = &contexts[context->shared->dispatcher_idx]; rxring = data->rxring; slots_read = bitmap_new(rxring->num_slots); if (!slots_read) pthread_exit(NULL); rv = worker_init(context); if (!rv) { pthread_exit(NULL); } printf("worker[%d]: initialized\n", context->thread_id); // signal to main() that we are initialized atomic_store_explicit(&context->initialized, 1, memory_order_release); for (;;) { if (pktbuff_used) pktbuff_out = pktbuff_allocator_borrow(&data->pktbuff_allocator); if (pktbuff_out) { pktbuff_out->thread_id = context->thread_id; pktbuff_used = 0; // read all the incoming packets while ((rv = tqueue_remove(context->pkt_recv_q, &transaction, &ring_idx)) != TQUEUE_EMPTY) { pktbuff_in = (void *)NETMAP_BUF(rxring, rxring->slot[(uint32_t)ring_idx].buf_idx); recv_pktbuff(pktbuff_in, pktbuff_out, &pcb, &pktbuff_used, context); if (pktbuff_used) { if (send_pktbuff(pktbuff_out, pcb, context, data) < 0) pktbuff_used = 0; } bitmap_set(slots_read, (uint32_t)ring_idx); if (rv == TQUEUE_TRANSACTION_EMPTY) { send_msg_transaction_update(dispatcher, slots_read, rxring->num_slots); bitmap_clearall(slots_read, rxring->num_slots); } } // while (packets) } // pktbuff_out // read all the messages // TODO: handle MSG_ARPD_GET_MAC_REPLY, MSG_PACKET_SENT rv = squeue_enter(context->msg_q, 1); if (!rv) continue; while ((msg_hdr = squeue_get_next_pop_slot(context->msg_q)) != NULL) { switch (msg_hdr->msg_type) { case MSG_PACKET_SENT: pktbuff_allocator_return(&data->pktbuff_allocator, ((struct msg_packet_sent *)msg_hdr)->pktbuff); break; case MSG_ARPD_GET_MAC_REPLY: // for now, just ignore break; default: printf("worker[%d]: unknown message %hu\n", context->thread_id, msg_hdr->msg_type); } } squeue_exit(context->msg_q); usleep(1000); } // for (;;) pthread_exit(NULL); }