Exemplo n.º 1
0
Arquivo: ehci.c Projeto: MWDD/osdev
// ------------------------------------------------------------------------------------------------
static void ehci_remove_qh(EHCI_QH* qh)
{
    EHCI_QH* prev = link_data(qh->qh_link.prev, EHCI_QH, qh_link);

    prev->qhlp = qh->qhlp;
    link_remove(&qh->qh_link);
}
Exemplo n.º 2
0
int main(void)
{
	//	link_head_init(&link_head);

	int a[5] = {1,2,3,4,5};
	int i;
	node_t *tmp = NULL;
	for (i = 0; i < 5; i++)
		link_head = link_insert_e(link_head, a[i]);
	link_print(link_head);
	//tmp = link_search(link_head, 4);
	//printf("%d\n", tmp->val);
	tmp = link_remove(link_head, 3);
	link_node_free(tmp);
	link_print(link_head);
	link_destroy(link_head);
	return 0;
}
Exemplo n.º 3
0
static void
processEvent_Apple_Network(struct kern_event_msg *ev_msg)
{
	const char *			eventName = NULL;
	int				dataLen = (ev_msg->total_size - KEV_MSG_HEADER_SIZE);
	void *				event_data = &ev_msg->event_data[0];
	Boolean				handled = TRUE;
	char				ifr_name[IFNAMSIZ];

	switch (ev_msg->kev_subclass) {
		case KEV_INET_SUBCLASS : {
			eventName = inetEventNameString(ev_msg->event_code);
			switch (ev_msg->event_code) {
				case KEV_INET_NEW_ADDR :
				case KEV_INET_CHANGED_ADDR :
				case KEV_INET_ADDR_DELETED :
				case KEV_INET_SIFDSTADDR :
				case KEV_INET_SIFBRDADDR :
				case KEV_INET_SIFNETMASK : {
					struct kev_in_data * ev;

					ev = (struct kev_in_data *)event_data;
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(&ev->link_data, ifr_name, sizeof(ifr_name));
					interface_update_ipv4(NULL, ifr_name);
					break;
				}
				case KEV_INET_ARPCOLLISION : {
					struct kev_in_collision * ev;

					ev = (struct kev_in_collision *)event_data;
					if ((dataLen < sizeof(*ev))
					    || (dataLen < (sizeof(*ev) + ev->hw_len))) {
						handled = FALSE;
						break;
					}
					copy_if_name(&ev->link_data, ifr_name, sizeof(ifr_name));
					interface_collision_ipv4(ifr_name,
								 ev->ia_ipaddr,
								 ev->hw_len,
								 ev->hw_addr);
					break;
				}
#if	!TARGET_OS_IPHONE
				case KEV_INET_PORTINUSE : {
					struct kev_in_portinuse	* ev;
					ev = (struct kev_in_portinuse *)event_data;
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					port_in_use_ipv4(ev->port, ev->req_pid);
					break;
				}
#endif	/* !TARGET_OS_IPHONE */
				default :
					handled = FALSE;
					break;
			}
			break;
		}
		case KEV_INET6_SUBCLASS : {
			struct kev_in6_data * ev;

			eventName = inet6EventNameString(ev_msg->event_code);
			ev = (struct kev_in6_data *)event_data;
			switch (ev_msg->event_code) {
				case KEV_INET6_NEW_USER_ADDR :
				case KEV_INET6_CHANGED_ADDR :
				case KEV_INET6_ADDR_DELETED :
				case KEV_INET6_NEW_LL_ADDR :
				case KEV_INET6_NEW_RTADV_ADDR :
				case KEV_INET6_DEFROUTER :
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(&ev->link_data, ifr_name, sizeof(ifr_name));
					interface_update_ipv6(NULL, ifr_name);
					break;

				default :
					handled = FALSE;
					break;
			}
			break;
		}
		case KEV_DL_SUBCLASS : {
			struct net_event_data * ev;

			eventName = dlEventNameString(ev_msg->event_code);
			ev = (struct net_event_data *)event_data;
			switch (ev_msg->event_code) {
				case KEV_DL_IF_ATTACHED :
					/*
					 * new interface added
					 */
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(ev, ifr_name, sizeof(ifr_name));
					link_add(ifr_name);
					break;

				case KEV_DL_IF_DETACHED :
					/*
					 * interface removed
					 */
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(ev, ifr_name, sizeof(ifr_name));
					link_remove(ifr_name);
					break;

				case KEV_DL_IF_DETACHING :
					/*
					 * interface detaching
					 */
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(ev, ifr_name, sizeof(ifr_name));
					interface_detaching(ifr_name);
					break;

				case KEV_DL_PROTO_ATTACHED :
				case KEV_DL_PROTO_DETACHED : {
					struct kev_dl_proto_data * protoEvent;

					protoEvent = (struct kev_dl_proto_data *)event_data;
					if (dataLen < sizeof(*protoEvent)) {
						handled = FALSE;
						break;
					}
					copy_if_name(&protoEvent->link_data,
						     ifr_name, sizeof(ifr_name));
					if (protoEvent->proto_remaining_count == 0) {
						mark_if_down(ifr_name);
					} else {
						mark_if_up(ifr_name);
					}
					break;
				}

#ifdef	KEV_DL_IF_IDLE_ROUTE_REFCNT
				case KEV_DL_IF_IDLE_ROUTE_REFCNT: {
					/*
					 * interface route refcnt idle
					 */
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(ev, ifr_name, sizeof(ifr_name));
					interface_update_idle_state(ifr_name);
					break;
				}
#endif	// KEV_DL_IF_IDLE_ROUTE_REFCNT

				case KEV_DL_LINK_OFF :
				case KEV_DL_LINK_ON :
					/*
					 * update the link status in the store
					 */
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(ev, ifr_name, sizeof(ifr_name));
					link_update_status(ifr_name, FALSE);
					break;

#ifdef  KEV_DL_LINK_QUALITY_METRIC_CHANGED
				case KEV_DL_LINK_QUALITY_METRIC_CHANGED: {
					struct kev_dl_link_quality_metric_data * lqm_data;
					lqm_data = (struct kev_dl_link_quality_metric_data *) event_data;

					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(ev, ifr_name, sizeof(ifr_name));
					interface_update_quality_metric(ifr_name,
								   lqm_data->link_quality_metric);
					break;
				}
#endif  // KEV_DL_LINK_QUALITY_METRIC_CHANGED

#ifdef	KEV_DL_ISSUES
				case KEV_DL_ISSUES: {
					struct kev_dl_issues *issues;

					issues = (struct kev_dl_issues *)event_data;
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(ev, ifr_name, sizeof(ifr_name));
					interface_update_link_issues(ifr_name,
								     issues->timestamp,
								     issues->modid,
								     DLIL_MODIDLEN,
								     issues->info,
								     (bcmp(issues->info, info_zero, DLIL_MODIDLEN) != 0)
									?DLIL_MODARGLEN
									:0);
					break;
				}
#endif	// KEV_DL_ISSUES

				case KEV_DL_SIFFLAGS :
				case KEV_DL_SIFMETRICS :
				case KEV_DL_SIFMTU :
				case KEV_DL_SIFPHYS :
				case KEV_DL_SIFMEDIA :
				case KEV_DL_SIFGENERIC :
				case KEV_DL_ADDMULTI :
				case KEV_DL_DELMULTI :
				case KEV_DL_LINK_ADDRESS_CHANGED :
				case KEV_DL_WAKEFLAGS_CHANGED :
#ifdef  KEV_DL_IFCAP_CHANGED
				case KEV_DL_IFCAP_CHANGED :
#endif	// KEV_DL_IFCAP_CHANGED
					break;

				default :
					handled = FALSE;
					break;
			}
			break;
		}
#ifdef	KEV_ND6_SUBCLASS
		case KEV_ND6_SUBCLASS : {
			eventName = nd6EventNameString(ev_msg->event_code);
			switch (ev_msg->event_code) {
				case KEV_KEV_ND6_RA :
					break;

				default :
					handled = FALSE;
					break;
			}
			break;
		}
#endif	// KEV_ND6_SUBCLASS
		case KEV_LOG_SUBCLASS : {
			break;
		}
		default :
			handled = FALSE;
			break;
	}

	if (handled == FALSE) {
		CFStringRef	evStr;

		evStr = CFStringCreateWithCString(NULL,
						  (eventName != NULL) ? eventName : "New Apple network subclass",
						  kCFStringEncodingASCII);
		logEvent(evStr, ev_msg);
		CFRelease(evStr);
	}
	return;
}
static void
processEvent_Apple_Network(struct kern_event_msg *ev_msg)
{
	const char *			eventName = NULL;
	int				dataLen = (ev_msg->total_size - KEV_MSG_HEADER_SIZE);
	void *				event_data = &ev_msg->event_data[0];
	Boolean				handled = TRUE;
	char				ifr_name[IFNAMSIZ+1];

	switch (ev_msg->kev_subclass) {
		case KEV_INET_SUBCLASS : {
			eventName = inetEventNameString(ev_msg->event_code);
			switch (ev_msg->event_code) {
				case KEV_INET_NEW_ADDR :
				case KEV_INET_CHANGED_ADDR :
				case KEV_INET_ADDR_DELETED :
				case KEV_INET_SIFDSTADDR :
				case KEV_INET_SIFBRDADDR :
				case KEV_INET_SIFNETMASK : {
					struct kev_in_data * ev;

					ev = (struct kev_in_data *)event_data;
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(&ev->link_data, ifr_name, sizeof(ifr_name));
					interface_update_ipv4(NULL, ifr_name);
					break;
				}
				case KEV_INET_ARPCOLLISION : {
					struct kev_in_collision * ev;

					ev = (struct kev_in_collision *)event_data;
					if ((dataLen < sizeof(*ev))
					    || (dataLen < (sizeof(*ev) + ev->hw_len))) {
						handled = FALSE;
						break;
					}
					copy_if_name(&ev->link_data, ifr_name, sizeof(ifr_name));
					interface_collision_ipv4(ifr_name,
								 ev->ia_ipaddr,
								 ev->hw_len,
								 ev->hw_addr);
					break;
				}
				default :
					handled = FALSE;
					break;
			}
			break;
		}
		case KEV_INET6_SUBCLASS : {
			struct kev_in6_data * ev;

			eventName = inet6EventNameString(ev_msg->event_code);
			ev = (struct kev_in6_data *)event_data;
			switch (ev_msg->event_code) {
				case KEV_INET6_NEW_USER_ADDR :
				case KEV_INET6_CHANGED_ADDR :
				case KEV_INET6_ADDR_DELETED :
				case KEV_INET6_NEW_LL_ADDR :
				case KEV_INET6_NEW_RTADV_ADDR :
				case KEV_INET6_DEFROUTER :
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(&ev->link_data, ifr_name, sizeof(ifr_name));
					interface_update_ipv6(NULL, ifr_name);
					break;

				default :
					handled = FALSE;
					break;
			}
			break;
		}
		case KEV_DL_SUBCLASS : {
			struct net_event_data * ev;

			eventName = dlEventNameString(ev_msg->event_code);
			ev = (struct net_event_data *)event_data;
			switch (ev_msg->event_code) {
				case KEV_DL_IF_ATTACHED :
					/*
					 * new interface added
					 */
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(ev, ifr_name, sizeof(ifr_name));
					link_add(ifr_name);
					break;

				case KEV_DL_IF_DETACHED :
					/*
					 * interface removed
					 */
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(ev, ifr_name, sizeof(ifr_name));
					link_remove(ifr_name);
					break;

				case KEV_DL_IF_DETACHING :
					/*
					 * interface detaching
					 */
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(ev, ifr_name, sizeof(ifr_name));
					interface_detaching(ifr_name);
					break;

				case KEV_DL_SIFFLAGS :
				case KEV_DL_SIFMETRICS :
				case KEV_DL_SIFMTU :
				case KEV_DL_SIFPHYS :
				case KEV_DL_SIFMEDIA :
				case KEV_DL_SIFGENERIC :
				case KEV_DL_ADDMULTI :
				case KEV_DL_DELMULTI :
					handled = FALSE;
					break;

				case KEV_DL_PROTO_ATTACHED :
				case KEV_DL_PROTO_DETACHED : {
					struct kev_dl_proto_data * protoEvent;

					protoEvent = (struct kev_dl_proto_data *)event_data;
					if (dataLen < sizeof(*protoEvent)) {
						handled = FALSE;
						break;
					}
					copy_if_name(&protoEvent->link_data,
						     ifr_name, sizeof(ifr_name));
					if (protoEvent->proto_remaining_count == 0) {
						mark_if_down(ifr_name);
					} else {
						mark_if_up(ifr_name);
					}
					break;
				}

				case KEV_DL_LINK_OFF :
				case KEV_DL_LINK_ON :
					/*
					 * update the link status in the store
					 */
					if (dataLen < sizeof(*ev)) {
						handled = FALSE;
						break;
					}
					copy_if_name(ev, ifr_name, sizeof(ifr_name));
					link_update_status(ifr_name, FALSE);
					break;

				default :
					handled = FALSE;
					break;
			}
			break;
		}
		case KEV_ATALK_SUBCLASS: {
			struct kev_atalk_data * ev;

			eventName = atalkEventNameString(ev_msg->event_code);
			ev = (struct kev_atalk_data *)event_data;
			if (dataLen < sizeof(*ev)) {
				handled = FALSE;
				break;
			}
			copy_if_name(&ev->link_data, ifr_name, sizeof(ifr_name));
			switch (ev_msg->event_code) {
				case KEV_ATALK_ENABLED:
					interface_update_atalk_address(ev, ifr_name);
					break;

				case KEV_ATALK_DISABLED:
					interface_update_shutdown_atalk();
					break;

				case KEV_ATALK_ZONEUPDATED:
					interface_update_atalk_zone(ev, ifr_name);
					break;

				case KEV_ATALK_ROUTERUP:
				case KEV_ATALK_ROUTERUP_INVALID:
				case KEV_ATALK_ROUTERDOWN:
					interface_update_appletalk(NULL, ifr_name);
					break;

				case KEV_ATALK_ZONELISTCHANGED:
					break;

				default :
					handled = FALSE;
					break;
			}
			break;
		}
		default :
			handled = FALSE;
			break;
	}

	if (handled == FALSE) {
		CFStringRef	evStr;

		evStr = CFStringCreateWithCString(NULL,
						  (eventName != NULL) ? eventName : "New Apple network subclass",
						  kCFStringEncodingASCII);
		logEvent(evStr, ev_msg);
		CFRelease(evStr);
	}
	return;
}
Exemplo n.º 5
0
/*
 * Burn CPU at a steady rate in this worker.
 */
void* cpuworker(void *opts)
{
  int rc;
  int cpu_index;
  int32_t target_epochs;
  int64_t next_deadline;
  int64_t target_cpuwork;
  int64_t link_waittime;
  double curr_epochs;
  double epochs_per_link;
  cpu_opts *cpu;
  cpu_burn_opts cbopts;
  gamut_opts *gopts;
  struct timeval start;
  struct timeval finish;
  struct timeval finish_time;

  if(!opts)
    return NULL;

  gopts = (gamut_opts *)opts;

  cpu_index = worker_register(gopts, CLS_CPU);
  if(cpu_index < 0) {
    return NULL;
  }
  else {
    cpu = &gopts->cpu[cpu_index];
  }

  /*
   * See if we need to wait for another linked worker
   *   to set us off.
   */
  rc = link_start_wait(gopts, CLS_CPU, cpu_index);
  if(rc < 0) {
    return NULL;
  }

  (void)gettimeofday(&cpu->shopts.start_time, NULL);
  cpu->total_work              = 0;
  cpu->shopts.missed_deadlines = 0;
  cpu->shopts.missed_usecs     = 0;
  cpu->shopts.total_deadlines  = 0;

  link_waittime                = 0;

restart:
  (void)gettimeofday(&cpu->shopts.mod_time, NULL);
  cpu->shopts.dirty = 0;

  target_cpuwork  = 0;
  epochs_per_link = 0.0;
  curr_epochs     = 0.0;
  target_epochs   = 0;

  rc = validate_worker_opts(opts, CLS_CPU, cpu_index);
  if(rc <= 0) {
    s_log(G_WARNING, "%s has invalid settings.\n", cpu->shopts.label);
    goto clean_out;
  }

  /*
   * We set the timer and deadline first so any delays in starting our
   *    work are absorbed; we'll catch up if necessary.
   */
  {
    struct timeval tv;

    (void)gettimeofday(&tv, NULL);
    
    if(cpu->shopts.exec_time) {
      finish_time.tv_sec  = tv.tv_sec + cpu->shopts.exec_time;
      finish_time.tv_usec = tv.tv_usec;
    }
    else {
      finish_time.tv_sec  = 0;
      finish_time.tv_usec = 0;
    }

    next_deadline  = tv.tv_usec;
    next_deadline += tv.tv_sec * US_SEC;
  }

  memset(&cbopts, 0, sizeof(cbopts));
  cbopts.count64 = (uint64_t)(second_count * cpu->percent_cpu)
                   / (100 * WORKER_EPOCHS_PER_SEC);
  s_log(G_INFO, "%s will do %lld CPU work per epoch.\n",
                 cpu->shopts.label, cbopts.count64);

  if(cpu->shopts.max_work)
  {
    target_cpuwork = cpu->shopts.max_work / cbopts.count64;
    /*
     * If the target work is 0, it's less than an epoch's worth.
     * Do one iteration anyway.
     */
    if(!target_cpuwork)
      target_cpuwork = 1;
  }
  else
  {
    target_cpuwork = -1;
  }

  /*
   * Make sure that if we've been asked to be part of a link,
   *   that we've got enough information to be sure that the
   *   link will actually work.
   */
  if(cpu->shopts.link_work && cpu->shopts.next_worker)
  {
    shared_opts *link_shopts;

    epochs_per_link = (double)cpu->shopts.link_work / cbopts.count64;
    curr_epochs     = epochs_per_link;
    target_epochs   = (int32_t)curr_epochs;

    link_shopts      = (shared_opts *)cpu->shopts.next_worker;

    s_log(G_DEBUG, "Will do %.2f epochs per link, handing off to %s.\n",
                   epochs_per_link, link_shopts->label);
  }
  else
  {
    target_epochs = -1;
  }

  /*
   * Each time through, perform these operations in this order:
   * 1. Calculate the next deadline
   * 2. Perform the CPU work
   * 3. If we're linked with another worker, see if it's handoff time
   * 4. See if it's time to exit
   * 5. Sleep, if there's enough time
   */
  (void)gettimeofday(&start, NULL);
  while(!cpu->shopts.exiting) {
    struct timeval now;

    /* Steps 1, 2 & 3 */
    if(target_epochs < 0) {
      /*
       * Just step 1 & 2 (burn CPU) since there are no links here.
       */
      next_deadline += US_PER_WORKER_EPOCH;

      cpu->cbfunc(cpu, &cbopts);
    }
    else /* (target_epochs >= 0) */ {
      if(target_epochs > 0) {
        next_deadline += US_PER_WORKER_EPOCH;

        cpu->cbfunc(cpu, &cbopts);
        target_epochs--;
      }

      /*
       * If we do have to wait, make sure we don't over-work
       *   when it's our turn.  Move the next_deadline backward
       *   by as much time as we spend waiting.
       */
      if(!target_epochs) {
        int64_t timediff;
        struct timeval b_link;

        (void)gettimeofday(&b_link, NULL);
        rc = link_next_wait(gopts, CLS_CPU, cpu_index, epochs_per_link,
                            &curr_epochs, &target_epochs);
        if(rc < 0) {
          s_log(G_WARNING, "Error in link_next_wait.\n");
        }
        else if(!rc) {
          s_log(G_DEBUG, "We need to exit (link_wait says so).\n");
          break;
        }
        else {
          struct timeval f_link;

          (void)gettimeofday(&f_link, NULL);
          s_log(G_DEBUG, "EL %.2f  CE %.2f  TE %d\n",
                         epochs_per_link, curr_epochs, target_epochs);

          timediff = calculate_timediff(&b_link, &f_link);
          next_deadline += timediff;
          link_waittime += timediff;
          s_log(G_DEBUG, "Moved next deadline backward by %lld usec.\n",
                         timediff);
        }
      }
    }

    /* Step 4 */
    if(target_cpuwork > 0) {
      target_cpuwork--;
      if(!target_cpuwork) {
        cpu->shopts.exiting = 1;
        break;
      }
    }

    /* Step 5 */
    (void)gettimeofday(&now, NULL);
    if(!finish_time.tv_sec
       || (finish_time.tv_sec > now.tv_sec)
       || ((finish_time.tv_sec == now.tv_sec)
           && (finish_time.tv_usec > now.tv_usec)
          )
      )
    {
      /*
       * Either there isn't a deadline
       *   or there is one, but we're a few seconds short
       *   or we're spot-on with the seconds
       *      and the usecs are short.
       */
      int64_t time_diff;
      int64_t current_time;

      current_time  = now.tv_usec;
      current_time += now.tv_sec * US_SEC;

      time_diff = next_deadline - current_time;
      s_log(G_DLOOP, "TD %lld\n", time_diff);
      if(current_time < next_deadline)
      {
        if(time_diff > MIN_SLEEP_US) {
          struct timeval sleeptv;

          sleeptv.tv_sec  = time_diff / US_SEC;
          sleeptv.tv_usec = time_diff - (sleeptv.tv_sec * US_SEC);
          s_log(G_DLOOP, "%s sleep.\n", cpu->shopts.label);
          (void)select(0, (fd_set *)NULL, (fd_set *)NULL,
                     (fd_set *)NULL, &sleeptv);
          s_log(G_DLOOP, "%s woke.\n", cpu->shopts.label);
        }
      }
      else
      {
        cpu->shopts.missed_deadlines++;
        cpu->shopts.missed_usecs += (current_time - next_deadline);
      }
      cpu->shopts.total_deadlines++;
    }
    else
    {
      cpu->shopts.exiting = 1;
      break;
    }

    if(cpu->shopts.dirty) {
      s_log(G_INFO, "%s reloading values.\n", cpu->shopts.label);
      goto restart;
    }
  }
  (void)gettimeofday(&finish, NULL);

  rc = lock_stats(gopts);
  if(rc < 0) {
    goto clean_out;
  }
  gopts->wstats.workers_exiting++;
  (void)unlock_stats(gopts);

clean_out:
  if(cpu->total_work)
  {
    int64_t cpu_usec;
    int64_t total_usec;
    uint64_t avg_miss_time;
    double cputime;
    double totaltime;

    total_usec    = calculate_timediff(&start, &finish);
    cpu_usec      = total_usec - link_waittime;
    totaltime     = (double)total_usec / US_SEC;
    cputime       = (double)cpu_usec / US_SEC;
    if(cpu->shopts.missed_deadlines) {
      avg_miss_time = cpu->shopts.missed_usecs
                      / cpu->shopts.missed_deadlines;
    }
    else {
      avg_miss_time = 0;
    }

    s_log(G_NOTICE, "%s did %llu CPU work in %.3f sec (total).\n",
                    cpu->shopts.label, cpu->total_work, totaltime);

    /*
     * Only print this out if we spent any time waiting on a link.
     */
    if(link_waittime) {
      s_log(G_NOTICE, "%s did %llu CPU work in %.3f sec (working).\n",
                      cpu->shopts.label, cpu->total_work, cputime);
    }

    s_log(G_INFO, "%s missed %llu of %llu deadlines by %llu usecs (avg).\n",
                  cpu->shopts.label, cpu->shopts.missed_deadlines,
                  cpu->shopts.total_deadlines, avg_miss_time);
  }

  /*
   * Remove ourselves from any link.
   */
  rc = link_remove(gopts, CLS_CPU, cpu_index);
  if(rc < 0) {
    s_log(G_WARNING, "Error removing %s from any link.\n",
                     cpu->shopts.label);
  }

  /*
   * Something has come along and decided we need to go.
   *   Clean up state behind us.
   */
  (void)worker_unregister(gopts, CLS_CPU);

  return NULL;
}