Ignore:
File:
1 edited

Legend:

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

    r4760793 r583c2a3  
    11/*
    22 * Copyright (c) 2001-2004 Jakub Jermar
    3  * Copyright (c) 2022 Jiří Zárevúcky
    43 * All rights reserved.
    54 *
     
    5857}
    5958
     59/** Reinitialize timeout
     60 *
     61 * Initialize all members except the lock.
     62 *
     63 * @param timeout Timeout to be initialized.
     64 *
     65 */
     66void 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
    6075/** Initialize timeout
    6176 *
     
    6782void timeout_initialize(timeout_t *timeout)
    6883{
    69         link_initialize(&timeout->link);
    70         timeout->cpu = NULL;
    71 }
    72 
    73 /* Only call when interrupts are disabled. */
    74 deadline_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 
    82 static 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         }
     84        irq_spinlock_initialize(&timeout->lock, "timeout_t_lock");
     85        timeout_reinitialize(timeout);
    11086}
    11187
     
    126102{
    127103        irq_spinlock_lock(&CPU->timeoutlock, true);
    128         timeout_register_deadline_locked(timeout, timeout_deadline_in_usec(time), handler, arg);
     104        irq_spinlock_lock(&timeout->lock, false);
     105
     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);
    129158        irq_spinlock_unlock(&CPU->timeoutlock, true);
    130159}
    131160
    132 void 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);
    137         irq_spinlock_unlock(&CPU->timeoutlock, true);
    138 }
    139 
    140161/** Unregister timeout
    141162 *
     
    149170bool timeout_unregister(timeout_t *timeout)
    150171{
    151         if (atomic_load_explicit(&timeout->finished, memory_order_acquire))
    152                 /* The timeout fired and finished already, no need to check the list. */
     172        DEADLOCK_PROBE_INIT(p_tolock);
     173
     174grab_locks:
     175        irq_spinlock_lock(&timeout->lock, true);
     176        if (!timeout->cpu) {
     177                irq_spinlock_unlock(&timeout->lock, true);
    153178                return false;
    154 
    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);
    162         }
    163 
    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();
    170         }
    171 
    172         return success;
     179        }
     180
     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;
     185        }
     186
     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;
    173208}
    174209
Note: See TracChangeset for help on using the changeset viewer.