Esempio n. 1
0
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);
}
Esempio n. 2
0
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);
}
Esempio n. 3
0
// 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);
}