void *thread_body(void *arg) { int ret; int nperiods; struct sched_param param; timing_point_t *timings; pid_t tid; struct sched_attr attr; unsigned int flags = 0; struct timespec t, t_next; timing_point_t tmp_timing; timing_point_t *curr_timing; unsigned long t_start_usec; int i = 0; thread_data_t *data = (thread_data_t*) arg; /* set thread affinity */ if (data->cpuset != NULL) { log_notice("[%d] setting cpu affinity to CPU(s) %s", data->ind, data->cpuset_str); ret = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), data->cpuset); if (ret < 0) { errno = ret; perror("pthread_setaffinity_np"); exit(EXIT_FAILURE); } } /* set scheduling policy and print pretty info on stdout */ log_notice("[%d] Using %s policy:", data->ind, data->sched_policy_descr); switch (data->sched_policy) { case rr: case fifo: fprintf(data->log_handler, "# Policy : %s\n", (data->sched_policy == rr ? "SCHED_RR" : "SCHED_FIFO")); param.sched_priority = data->sched_prio; ret = pthread_setschedparam(pthread_self(), data->sched_policy, ¶m); if (ret != 0) { errno = ret; perror("pthread_setschedparam"); exit(EXIT_FAILURE); } log_notice("[%d] starting thread with period: %" PRIu64 ", exec: %" PRIu64 ",""deadline: %" PRIu64 ", priority: %d", data->ind, timespec_to_usec(&data->period), timespec_to_usec(&data->min_et), timespec_to_usec(&data->deadline), data->sched_prio ); break; case other: fprintf(data->log_handler, "# Policy : SCHED_OTHER\n"); log_notice("[%d] starting thread with period: %" PRIu64 ", exec: %" PRIu64 ",""deadline: %" PRIu64 "", data->ind, timespec_to_usec(&data->period), timespec_to_usec(&data->min_et), timespec_to_usec(&data->deadline) ); data->lock_pages = 0; /* forced off for SCHED_OTHER */ break; case deadline: fprintf(data->log_handler, "# Policy : SCHED_DEADLINE\n"); tid = gettid(); attr.size = sizeof(attr); attr.sched_flags = data->sched_flags; if (data->sched_flags && SCHED_FLAG_SOFT_RSV) fprintf(data->log_handler, "# Type : SOFT_RSV\n"); else fprintf(data->log_handler, "# Type : HARD_RSV\n"); attr.sched_policy = SCHED_DEADLINE; attr.sched_priority = 0; attr.sched_runtime = timespec_to_nsec(&data->max_et) + (timespec_to_nsec(&data->max_et) /100) * BUDGET_OVERP; attr.sched_deadline = timespec_to_nsec(&data->period); attr.sched_period = timespec_to_nsec(&data->period); break; default: log_error("Unknown scheduling policy %d", data->sched_policy); exit(EXIT_FAILURE); } if (data->lock_pages == 1) { log_notice("[%d] Locking pages in memory", data->ind); ret = mlockall(MCL_CURRENT | MCL_FUTURE); if (ret < 0) { errno = ret; perror("mlockall"); exit(EXIT_FAILURE); } } /* if we know the duration we can calculate how many periods we will * do at most, and the log to memory, instead of logging to file. */ timings = NULL; if (data->duration > 0) { nperiods = (int) ceil( (data->duration * 10e6) / (double) timespec_to_usec(&data->period)); timings = malloc ( nperiods * sizeof(timing_point_t)); } fprintf(data->log_handler, "#idx\tperiod\tmin_et\tmax_et\trel_st\tstart" "\t\tend\t\tdeadline\tdur.\tslack\tresp_t" "\tBudget\tUsed Budget\n"); if (data->ind == 0) { clock_gettime(CLOCK_MONOTONIC, &t_zero); #ifdef TRACE_SETS_ZERO_TIME if (opts.ftrace) log_ftrace(ft_data.marker_fd, "[%d] sets zero time", data->ind); #endif } pthread_barrier_wait(&threads_barrier); /* * Set the task to SCHED_DEADLINE as far as possible touching its * budget as little as possible for the first iteration. */ if (data->sched_policy == SCHED_DEADLINE) { ret = sched_setattr(tid, &attr, flags); if (ret != 0) { log_critical("[%d] sched_setattr " "returned %d", data->ind, ret); errno = ret; perror("sched_setattr"); exit(EXIT_FAILURE); } } t = t_zero; t_next = msec_to_timespec(1000LL); t_next = timespec_add(&t, &t_next); clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &t_next, NULL); data->deadline = timespec_add(&t_next, &data->deadline); while (continue_running) { int pn; struct timespec t_start, t_end, t_diff, t_slack, t_resp; /* Thread numeration reported starts with 1 */ #ifdef TRACE_BEGINS_LOOP if (opts.ftrace) log_ftrace(ft_data.marker_fd, "[%d] begins job %d", data->ind+1, i); #endif clock_gettime(CLOCK_MONOTONIC, &t_start); if (data->nphases == 0) { compute(data->ind, &data->min_et, NULL, 0); } else { for (pn = 0; pn < data->nphases; pn++) { log_notice("[%d] phase %d start", data->ind+1, pn); exec_phase(data, pn); log_notice("[%d] phase %d end", data->ind+1, pn); } } clock_gettime(CLOCK_MONOTONIC, &t_end); t_diff = timespec_sub(&t_end, &t_start); t_slack = timespec_sub(&data->deadline, &t_end); t_resp = timespec_sub(&t_end, &t_next); t_start_usec = timespec_to_usec(&t_start); if (i < nperiods) { if (timings) curr_timing = &timings[i]; else curr_timing = &tmp_timing; curr_timing->ind = data->ind; curr_timing->period = timespec_to_usec(&data->period); curr_timing->min_et = timespec_to_usec(&data->min_et); curr_timing->max_et = timespec_to_usec(&data->max_et); curr_timing->rel_start_time = t_start_usec - timespec_to_usec(&data->main_app_start); curr_timing->abs_start_time = t_start_usec; curr_timing->end_time = timespec_to_usec(&t_end); curr_timing->deadline = timespec_to_usec(&data->deadline); curr_timing->duration = timespec_to_usec(&t_diff); curr_timing->slack = timespec_to_lusec(&t_slack); curr_timing->resp_time = timespec_to_usec(&t_resp); } if (!timings) log_timing(data->log_handler, curr_timing); t_next = timespec_add(&t_next, &data->period); data->deadline = timespec_add(&data->deadline, &data->period); #ifdef TRACE_END_LOOP if (opts.ftrace) log_ftrace(ft_data.marker_fd, "[%d] end loop %d", data->ind, i); #endif if (curr_timing->slack < 0) log_notice("[%d] DEADLINE MISS !!!", data->ind+1); i++; } free(timings); }
void* trans_main(void* arg){ gd_thread_data_t *tdata = (gd_thread_data_t *) arg; int id = tdata->ind; thread_common(pthread_self(), tdata); unsigned long abs_period_start = timespec_to_usec(&tdata->main_start); gd_timing_meta_t *timings; long duration_usec = (tdata->duration * 1e6); int nperiods = (int) ceil( duration_usec / (double) timespec_to_usec(&tdata->period)); timings = (gd_timing_meta_t*) malloc ( nperiods * sizeof(gd_timing_meta_t)); gd_timing_meta_t* timing; struct timespec t_next, t_deadline, trans_start, trans_end, t_temp, t_now; t_next = tdata->main_start; int period = 0; int bs_id = ((int)(id/num_ants)); int subframe_id; while(running && (period < nperiods)){ subframe_id = period%(num_cores_bs); // get current deadline and next period t_deadline = timespec_add(&t_next, &tdata->deadline); t_next = timespec_add(&t_next, &tdata->period); clock_gettime(CLOCK_MONOTONIC, &trans_start); /******* Main transport ******/ if (debug_trans==1) { int j, k; for(j=0; j <60000; j++){k=k+1;} } else { gd_trans_read(tdata->conn_desc); } /******* Main transport ******/ pthread_mutex_lock(&subframe_mutex[bs_id*num_cores_bs + subframe_id]); // subframe_avail[bs_id*num_cores_bs + subframe_id] = (subframe_avail[bs_id*num_cores_bs + subframe_id]+1)%(num_ants); subframe_avail[bs_id*num_cores_bs + subframe_id] ++; // printf("subframe_avail:%d %d\n",bs_id*num_cores_bs + subframe_id,subframe_avail[bs_id*num_cores_bs + subframe_id]); // hanging fix -- if trans misses a proc, reset the subframe available counter if (subframe_avail[bs_id*num_cores_bs + subframe_id] == (num_ants+1)) { subframe_avail[bs_id*num_cores_bs + subframe_id] = 1; } pthread_cond_signal(&subframe_cond[bs_id*num_cores_bs + subframe_id]); pthread_mutex_unlock(&subframe_mutex[bs_id*num_cores_bs + subframe_id]); clock_gettime(CLOCK_MONOTONIC, &trans_end); /*****************************/ timing = &timings[period]; timing->ind = id; timing->period = period; timing->abs_period_time = timespec_to_usec(&t_next); timing->rel_period_time = timing->abs_period_time - abs_period_start; timing->abs_start_time = timespec_to_usec(&trans_start); timing->rel_start_time = timing->abs_start_time - abs_period_start; timing->abs_end_time = timespec_to_usec(&trans_end); timing->rel_end_time = timing->abs_end_time - abs_period_start; timing->abs_deadline = timespec_to_usec(&t_deadline); timing->rel_deadline = timing->abs_deadline - abs_period_start; timing->actual_duration = timing->rel_end_time - timing->rel_start_time; timing->miss = (timing->rel_deadline - timing->rel_end_time >= 0) ? 0 : 1; if (timing->actual_duration > 1000){ // log_critical("Transport overload. Thread[%d] Duration= %lu us. Reduce samples or increase threads", // tdata->ind, timing->actual_duration); } clock_gettime(CLOCK_MONOTONIC, &t_now); // check if deadline was missed if (timespec_lower(&t_now, &t_next)){ // sleep for remaining time clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &t_next, NULL); }else{ printf("Transport %d is too slow\n", id); } period ++; } clock_gettime(CLOCK_MONOTONIC, &t_temp); log_notice("Trans thread [%d] ran for %f s", id, ((float) (timespec_to_usec(&t_temp)-abs_period_start))/1e6); fprintf(tdata->log_handler, "#idx\t\tabs_period\t\tabs_deadline\t\tabs_start\t\tabs_end" "\t\trel_period\t\trel_start\t\trel_end\t\tduration\t\tmiss\n"); int i; for (i=0; i < nperiods; i++){ log_timing(tdata->log_handler, &timings[i]); } fclose(tdata->log_handler); log_notice("Exit trans thread %d", id); running = 0; for (i=0;i<proc_nthreads;i++) { pthread_mutex_lock(&subframe_mutex[i]); subframe_avail[i]=-1; pthread_cond_signal(&subframe_cond[i]); pthread_mutex_unlock(&subframe_mutex[i]); } pthread_exit(NULL); }
/* * Transmit a packet (called by the kernel) */ int virt_tx(struct sk_buff *skb, struct net_device *dev) { int rtn = 0; struct virt_priv *priv = netdev_priv(dev); struct device_node *slave = NULL; int payload_len; struct packet *pkt = NULL; #ifndef VIRT_USE_RTABLE struct net_device *out_dev = NULL; #endif //VIRT_DBG("in virt_tx...\n"); if( use_timing() ) log_timing(TIMING_TX_START); // if no slaves default off => drop packet if(get_slave_list_head() == NULL) goto drop; /* For flow byte counts, we are interested in network layer and above. */ payload_len = skb->len; dev->trans_start = jiffies; /* save the timestamp */ if( use_timing() ) log_timing(TIMING_TX_SETUP); //malloc packet struct and flow struct pkt = kmalloc(sizeof(struct packet), GFP_ATOMIC); if(!pkt) goto drop; memset(pkt, 0, sizeof(struct packet)); pkt->key = kmalloc(sizeof(struct flow_tuple), GFP_ATOMIC); if(!pkt->key) goto drop_and_free; memset(pkt->key, 0, sizeof(struct flow_tuple)); pkt->hdr_ptrs = kmalloc(sizeof(struct hdr_ptrs), GFP_ATOMIC); if(!pkt->hdr_ptrs) goto drop_and_free; memset(pkt->hdr_ptrs, 0, sizeof(struct hdr_ptrs)); pkt->master = dev; pkt->skb = skb; if( use_timing() ) log_timing(TIMING_TX_PARSE); rtn = virt_parse_egress_pkt(pkt); if( ntohs(pkt->key->net_proto) == ETH_P_ARP ) goto drop_and_free; if( use_timing() ) log_timing(TIMING_TX_LOOKUP); // lookup flow route info if(virt_egress_lookup_flow(priv, pkt) < 0) goto drop_and_free; /* Update policy hit statistics. */ if(pkt->policy) { struct policy_stats *stats = &pkt->policy->stats; stats->tx_packets++; stats->tx_bytes += skb->len; } // route the packet if( use_timing() ) log_timing(TIMING_TX_MANGLE); switch(POLICY_ACTION(pkt->policy->action)) { case POLICY_ACT_PASS: slave = select_local_interface(priv, pkt->ftable_entry, NULL, get_slave_list_head()); if(!slave) goto drop_and_free; VIRT_DBG("egress PASS this packet\n"); /* TODO: It would be nice if PASSed packets would still go through * netfilter, especially the post-routing hook, so that we could * make use of netfilter's NAT function. However, the netfilter * hooks rely on routing information being filled in, so we would * have to play along with the routing code. * * if(nf_hook(NFPROTO_IPV4, NF_INET_POST_ROUTING, skb, NULL, * slave->dev, dev_queue_xmit) == NF_ACCEPT) * dev_queue_xmit(skb); */ break; case POLICY_ACT_LISP: case POLICY_ACT_NAT: slave = select_local_interface(priv, pkt->ftable_entry, NULL, get_slave_list_head()); if(!slave) goto drop_and_free; VIRT_DBG("egress NAT this packet\n"); virt_nat_egress_pkt(pkt, slave); break; case POLICY_ACT_ENCAP: { struct remote_node *rnode = pkt->ftable_entry->rnode; if(!rnode) { rnode = select_remote_node(priv, pkt); if(!rnode) { VIRT_DBG("routing packet failed\n"); goto drop_and_free; } pkt->ftable_entry->rnode = rnode; } virt_queue_tx(priv, pkt, rnode); return NETDEV_TX_OK; } case POLICY_ACT_DROP: VIRT_DBG("egress DROP this packet\n"); goto drop_and_free; default: // unknown route policy VIRT_DBG("egress ERROR: unknown policy action (%x)\n", pkt->policy->action); } // update flow statistics pkt->flow_stats->tx_packets++; pkt->flow_stats->tx_bytes += payload_len; pkt->flow_stats->last_tx_dev = slave->dev->ifindex; // update device statistics priv->stats.tx_packets++; priv->stats.tx_bytes += skb->len; // update link statistics slave->stats.tx_packets++; slave->stats.tx_bytes += skb->len; VIRT_DBG("transmitting the packet...\n\n"); if( use_timing() ) log_timing(TIMING_TX_END); #ifdef VIRT_USE_RTABLE uint32_t gw_ip = 0x020FA8C0; struct rtable *rt = skb_rtable(skb); struct iphdr *ip = ip_hdr(skb); VIRT_DBG("routing %x->%x (%s)\n", ntohl(ip->saddr), ntohl(ip->daddr), slave->dev->name); struct flowi fl = { // .oif = pkt->slave->dev->iflink, .oif = slave->dev->ifindex, .nl_u = { .ip4_u = { .daddr = ip->daddr, .saddr = ip->saddr, .tos = 0, }, }, .proto = IPPROTO_IP, };