Example #1
0
/*
 * MPSAFE
 */
void
systimer_add(systimer_t info)
{
    struct globaldata *gd = mycpu;

    KKASSERT((info->flags & SYSTF_ONQUEUE) == 0);
    crit_enter();
    if (info->gd == gd) {
	systimer_t scan1;
	systimer_t scan2;
	scan1 = TAILQ_FIRST(&gd->gd_systimerq);
	if (scan1 == NULL || (int)(scan1->time - info->time) > 0) {
	    cputimer_intr_reload(info->time - sys_cputimer->count());
	    TAILQ_INSERT_HEAD(&gd->gd_systimerq, info, node);
	} else {
	    scan2 = TAILQ_LAST(&gd->gd_systimerq, systimerq);
	    for (;;) {
		if (scan1 == NULL) {
		    TAILQ_INSERT_TAIL(&gd->gd_systimerq, info, node);
		    break;
		}
		if ((int)(scan1->time - info->time) > 0) {
		    TAILQ_INSERT_BEFORE(scan1, info, node);
		    break;
		}
		if ((int)(scan2->time - info->time) <= 0) {
		    TAILQ_INSERT_AFTER(&gd->gd_systimerq, scan2, info, node);
		    break;
		}
		scan1 = TAILQ_NEXT(scan1, node);
		scan2 = TAILQ_PREV(scan2, systimerq, node);
	    }
	}
	info->flags = (info->flags | SYSTF_ONQUEUE) & ~SYSTF_IPIRUNNING;
	info->queue = &gd->gd_systimerq;
    } else {
#ifdef SMP
	KKASSERT((info->flags & SYSTF_IPIRUNNING) == 0);
	info->flags |= SYSTF_IPIRUNNING;
	lwkt_send_ipiq(info->gd, (ipifunc1_t)systimer_add, info);
#else
	panic("systimer_add: bad gd in info %p", info);
#endif
    }
    crit_exit();
}
Example #2
0
/*
 * Execute ready systimers.  Called directly from the platform-specific
 * one-shot timer clock interrupt (e.g. clkintr()) or via an IPI.  May
 * be called simultaniously on multiple cpus and always operations on 
 * the current cpu's queue.  Systimer functions are responsible for calling
 * hardclock, statclock, and other finely-timed routines.
 */
void
systimer_intr(sysclock_t *timep, int in_ipi, struct intrframe *frame)
{
    globaldata_t gd = mycpu;
    sysclock_t time = *timep;
    systimer_t info;

    if (gd->gd_syst_nest)
	return;

    crit_enter();
    ++gd->gd_syst_nest;
    while ((info = TAILQ_FIRST(&gd->gd_systimerq)) != NULL) {
	/*
	 * If we haven't reached the requested time, tell the cputimer
	 * how much is left and break out.
	 */
	if ((int)(info->time - time) > 0) {
	    cputimer_intr_reload(info->time - time);
	    break;
	}

	/*
	 * Dequeue and execute, detect a loss of the systimer.  Note
	 * that the in-progress systimer pointer can only be used to
	 * detect a loss of the systimer, it is only useful within
	 * this code sequence and becomes stale otherwise.
	 */
	info->flags &= ~SYSTF_ONQUEUE;
	TAILQ_REMOVE(info->queue, info, node);
	gd->gd_systimer_inprog = info;
	crit_exit();
	info->func(info, in_ipi, frame);
	crit_enter();

	/*
	 * The caller may deleted or even re-queue the systimer itself
	 * with a delete/add sequence.  If the caller does not mess with
	 * the systimer we will requeue the periodic interval automatically.
	 *
	 * If this is a non-queued periodic interrupt, do not allow multiple
	 * events to build up (used for things like the callout timer to
	 * prevent premature timeouts due to long interrupt disablements,
	 * BIOS 8254 glitching, and so forth).  However, we still want to
	 * keep things synchronized between cpus for efficient handling of
	 * the timer interrupt so jump in multiples of the periodic rate.
	 */
	if (gd->gd_systimer_inprog == info && info->periodic) {
	    if (info->which != sys_cputimer) {
		info->periodic = sys_cputimer->fromhz(info->freq);
		info->which = sys_cputimer;
	    }
	    info->time += info->periodic;
	    if ((info->flags & SYSTF_NONQUEUED) &&
		(int)(info->time - time) <= 0
	    ) {
		info->time += ((time - info->time + info->periodic - 1) / 
				info->periodic) * info->periodic;
	    }
	    systimer_add(info);
	}
	gd->gd_systimer_inprog = NULL;
    }
    --gd->gd_syst_nest;
    crit_exit();
}