Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • kernel/generic/src/time/timeout.c

    r583c2a3 r4760793  
    11/*
    22 * Copyright (c) 2001-2004 Jakub Jermar
     3 * Copyright (c) 2022 Jiří Zárevúcky
    34 * All rights reserved.
    45 *
     
    5758}
    5859
    59 /** Reinitialize timeout
    60  *
    61  * Initialize all members except the lock.
    62  *
    63  * @param timeout Timeout to be initialized.
    64  *
    65  */
    66 void timeout_reinitialize(timeout_t *timeout)
    67 {
    68         timeout->cpu = NULL;
    69         timeout->ticks = 0;
    70         timeout->handler = NULL;
    71         timeout->arg = NULL;
    72         link_initialize(&timeout->link);
    73 }
    74 
    7560/** Initialize timeout
    7661 *
     
    8267void timeout_initialize(timeout_t *timeout)
    8368{
    84         irq_spinlock_initialize(&timeout->lock, "timeout_t_lock");
    85         timeout_reinitialize(timeout);
     69        link_initialize(&timeout->link);
     70        timeout->cpu = NULL;
     71}
     72
     73/* Only call when interrupts are disabled. */
     74deadline_t timeout_deadline_in_usec(uint32_t usec)
     75{
     76        if (usec == 0)
     77                return 0;
     78
     79        return CPU_LOCAL->current_clock_tick + us2ticks(usec);
     80}
     81
     82static void timeout_register_deadline_locked(timeout_t *timeout, deadline_t deadline,
     83    timeout_handler_t handler, void *arg)
     84{
     85        assert(!link_in_use(&timeout->link));
     86
     87        *timeout = (timeout_t) {
     88                .cpu = CPU,
     89                .deadline = deadline,
     90                .handler = handler,
     91                .arg = arg,
     92                .finished = ATOMIC_VAR_INIT(false),
     93        };
     94
     95        /* Insert timeout into the active timeouts list according to timeout->deadline. */
     96
     97        link_t *last = list_last(&CPU->timeout_active_list);
     98        if (last == NULL || timeout->deadline >= list_get_instance(last, timeout_t, link)->deadline) {
     99                list_append(&timeout->link, &CPU->timeout_active_list);
     100        } else {
     101                for (link_t *cur = list_first(&CPU->timeout_active_list); cur != NULL;
     102                    cur = list_next(cur, &CPU->timeout_active_list)) {
     103
     104                        if (timeout->deadline < list_get_instance(cur, timeout_t, link)->deadline) {
     105                                list_insert_before(&timeout->link, cur);
     106                                break;
     107                        }
     108                }
     109        }
    86110}
    87111
     
    102126{
    103127        irq_spinlock_lock(&CPU->timeoutlock, true);
    104         irq_spinlock_lock(&timeout->lock, false);
     128        timeout_register_deadline_locked(timeout, timeout_deadline_in_usec(time), handler, arg);
     129        irq_spinlock_unlock(&CPU->timeoutlock, true);
     130}
    105131
    106         if (timeout->cpu)
    107                 panic("Unexpected: timeout->cpu != 0.");
    108 
    109         timeout->cpu = CPU;
    110         timeout->ticks = us2ticks(time);
    111 
    112         timeout->handler = handler;
    113         timeout->arg = arg;
    114 
    115         /*
    116          * Insert timeout into the active timeouts list according to timeout->ticks.
    117          */
    118         uint64_t sum = 0;
    119         timeout_t *target = NULL;
    120         link_t *cur, *prev;
    121         prev = NULL;
    122         for (cur = list_first(&CPU->timeout_active_list);
    123             cur != NULL; cur = list_next(cur, &CPU->timeout_active_list)) {
    124                 target = list_get_instance(cur, timeout_t, link);
    125                 irq_spinlock_lock(&target->lock, false);
    126 
    127                 if (timeout->ticks < sum + target->ticks) {
    128                         irq_spinlock_unlock(&target->lock, false);
    129                         break;
    130                 }
    131 
    132                 sum += target->ticks;
    133                 irq_spinlock_unlock(&target->lock, false);
    134                 prev = cur;
    135         }
    136 
    137         if (prev == NULL)
    138                 list_prepend(&timeout->link, &CPU->timeout_active_list);
    139         else
    140                 list_insert_after(&timeout->link, prev);
    141 
    142         /*
    143          * Adjust timeout->ticks according to ticks
    144          * accumulated in target's predecessors.
    145          */
    146         timeout->ticks -= sum;
    147 
    148         /*
    149          * Decrease ticks of timeout's immediate succesor by timeout->ticks.
    150          */
    151         if (cur != NULL) {
    152                 irq_spinlock_lock(&target->lock, false);
    153                 target->ticks -= timeout->ticks;
    154                 irq_spinlock_unlock(&target->lock, false);
    155         }
    156 
    157         irq_spinlock_unlock(&timeout->lock, false);
     132void timeout_register_deadline(timeout_t *timeout, deadline_t deadline,
     133    timeout_handler_t handler, void *arg)
     134{
     135        irq_spinlock_lock(&CPU->timeoutlock, true);
     136        timeout_register_deadline_locked(timeout, deadline, handler, arg);
    158137        irq_spinlock_unlock(&CPU->timeoutlock, true);
    159138}
     
    170149bool timeout_unregister(timeout_t *timeout)
    171150{
    172         DEADLOCK_PROBE_INIT(p_tolock);
     151        if (atomic_load_explicit(&timeout->finished, memory_order_acquire))
     152                /* The timeout fired and finished already, no need to check the list. */
     153                return false;
    173154
    174 grab_locks:
    175         irq_spinlock_lock(&timeout->lock, true);
    176         if (!timeout->cpu) {
    177                 irq_spinlock_unlock(&timeout->lock, true);
    178                 return false;
     155        assert(timeout->cpu);
     156
     157        irq_spinlock_lock(&timeout->cpu->timeoutlock, true);
     158
     159        bool success = link_in_use(&timeout->link);
     160        if (success) {
     161                list_remove(&timeout->link);
    179162        }
    180163
    181         if (!irq_spinlock_trylock(&timeout->cpu->timeoutlock)) {
    182                 irq_spinlock_unlock(&timeout->lock, true);
    183                 DEADLOCK_PROBE(p_tolock, DEADLOCK_THRESHOLD);
    184                 goto grab_locks;
     164        irq_spinlock_unlock(&timeout->cpu->timeoutlock, true);
     165
     166        if (!success) {
     167                /* Timeout was fired, we need to wait for the callback to finish. */
     168                while (!atomic_load_explicit(&timeout->finished, memory_order_acquire))
     169                        cpu_spin_hint();
    185170        }
    186171
    187         /*
    188          * Now we know for sure that timeout hasn't been activated yet
    189          * and is lurking in timeout->cpu->timeout_active_list.
    190          */
    191 
    192         link_t *cur = list_next(&timeout->link,
    193             &timeout->cpu->timeout_active_list);
    194         if (cur != NULL) {
    195                 timeout_t *tmp = list_get_instance(cur, timeout_t, link);
    196                 irq_spinlock_lock(&tmp->lock, false);
    197                 tmp->ticks += timeout->ticks;
    198                 irq_spinlock_unlock(&tmp->lock, false);
    199         }
    200 
    201         list_remove(&timeout->link);
    202         irq_spinlock_unlock(&timeout->cpu->timeoutlock, false);
    203 
    204         timeout_reinitialize(timeout);
    205         irq_spinlock_unlock(&timeout->lock, true);
    206 
    207         return true;
     172        return success;
    208173}
    209174
Note: See TracChangeset for help on using the changeset viewer.