LCOV - code coverage report
Current view: top level - kernel - mutex.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 78 0.0 %
Date: 2022-08-18 11:36:24 Functions: 0 5 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 62 0.0 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (c) 2016 Wind River Systems, Inc.
       3                 :            :  *
       4                 :            :  * SPDX-License-Identifier: Apache-2.0
       5                 :            :  */
       6                 :            : 
       7                 :            : /**
       8                 :            :  * @file @brief mutex kernel services
       9                 :            :  *
      10                 :            :  * This module contains routines for handling mutex locking and unlocking.
      11                 :            :  *
      12                 :            :  * Mutexes implement a priority inheritance algorithm that boosts the priority
      13                 :            :  * level of the owning thread to match the priority level of the highest
      14                 :            :  * priority thread waiting on the mutex.
      15                 :            :  *
      16                 :            :  * Each mutex that contributes to priority inheritance must be released in the
      17                 :            :  * reverse order in which it was acquired.  Furthermore each subsequent mutex
      18                 :            :  * that contributes to raising the owning thread's priority level must be
      19                 :            :  * acquired at a point after the most recent "bumping" of the priority level.
      20                 :            :  *
      21                 :            :  * For example, if thread A has two mutexes contributing to the raising of its
      22                 :            :  * priority level, the second mutex M2 must be acquired by thread A after
      23                 :            :  * thread A's priority level was bumped due to owning the first mutex M1.
      24                 :            :  * When releasing the mutex, thread A must release M2 before it releases M1.
      25                 :            :  * Failure to follow this nested model may result in threads running at
      26                 :            :  * unexpected priority levels (too high, or too low).
      27                 :            :  */
      28                 :            : 
      29                 :            : #include <kernel.h>
      30                 :            : #include <kernel_structs.h>
      31                 :            : #include <toolchain.h>
      32                 :            : #include <ksched.h>
      33                 :            : #include <wait_q.h>
      34                 :            : #include <errno.h>
      35                 :            : #include <init.h>
      36                 :            : #include <syscall_handler.h>
      37                 :            : #include <tracing/tracing.h>
      38                 :            : #include <sys/check.h>
      39                 :            : #include <logging/log.h>
      40                 :            : LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL);
      41                 :            : 
      42                 :            : /* We use a global spinlock here because some of the synchronization
      43                 :            :  * is protecting things like owner thread priorities which aren't
      44                 :            :  * "part of" a single k_mutex.  Should move those bits of the API
      45                 :            :  * under the scheduler lock so we can break this up.
      46                 :            :  */
      47                 :            : static struct k_spinlock lock;
      48                 :            : 
      49                 :          0 : int z_impl_k_mutex_init(struct k_mutex *mutex)
      50                 :            : {
      51                 :          0 :         mutex->owner = NULL;
      52                 :          0 :         mutex->lock_count = 0U;
      53                 :            : 
      54                 :          0 :         z_waitq_init(&mutex->wait_q);
      55                 :            : 
      56                 :          0 :         z_object_init(mutex);
      57                 :            : 
      58                 :            :         SYS_PORT_TRACING_OBJ_INIT(k_mutex, mutex, 0);
      59                 :            : 
      60                 :          0 :         return 0;
      61                 :            : }
      62                 :            : 
      63                 :            : #ifdef CONFIG_USERSPACE
      64                 :            : static inline int z_vrfy_k_mutex_init(struct k_mutex *mutex)
      65                 :            : {
      66                 :            :         Z_OOPS(Z_SYSCALL_OBJ_INIT(mutex, K_OBJ_MUTEX));
      67                 :            :         return z_impl_k_mutex_init(mutex);
      68                 :            : }
      69                 :            : #include <syscalls/k_mutex_init_mrsh.c>
      70                 :            : #endif
      71                 :            : 
      72                 :          0 : static int32_t new_prio_for_inheritance(int32_t target, int32_t limit)
      73                 :            : {
      74         [ #  # ]:          0 :         int new_prio = z_is_prio_higher(target, limit) ? target : limit;
      75                 :            : 
      76                 :          0 :         new_prio = z_get_new_prio_with_ceiling(new_prio);
      77                 :            : 
      78                 :          0 :         return new_prio;
      79                 :            : }
      80                 :            : 
      81                 :          0 : static bool adjust_owner_prio(struct k_mutex *mutex, int32_t new_prio)
      82                 :            : {
      83         [ #  # ]:          0 :         if (mutex->owner->base.prio != new_prio) {
      84                 :            : 
      85   [ #  #  #  # ]:          0 :                 LOG_DBG("%p (ready (y/n): %c) prio changed to %d (was %d)",
      86                 :            :                         mutex->owner, z_is_thread_ready(mutex->owner) ?
      87                 :            :                         'y' : 'n',
      88                 :            :                         new_prio, mutex->owner->base.prio);
      89                 :            : 
      90                 :          0 :                 return z_set_prio(mutex->owner, new_prio);
      91                 :            :         }
      92                 :          0 :         return false;
      93                 :            : }
      94                 :            : 
      95                 :          0 : int z_impl_k_mutex_lock(struct k_mutex *mutex, k_timeout_t timeout)
      96                 :            : {
      97                 :            :         int new_prio;
      98                 :            :         k_spinlock_key_t key;
      99                 :          0 :         bool resched = false;
     100                 :            : 
     101         [ #  # ]:          0 :         __ASSERT(!arch_is_in_isr(), "mutexes cannot be used inside ISRs");
     102                 :            : 
     103                 :            :         SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_mutex, lock, mutex, timeout);
     104                 :            : 
     105                 :          0 :         key = k_spin_lock(&lock);
     106                 :            : 
     107   [ #  #  #  # ]:          0 :         if (likely((mutex->lock_count == 0U) || (mutex->owner == _current))) {
     108                 :            : 
     109                 :          0 :                 mutex->owner_orig_prio = (mutex->lock_count == 0U) ?
     110         [ #  # ]:          0 :                                         _current->base.prio :
     111                 :            :                                         mutex->owner_orig_prio;
     112                 :            : 
     113                 :          0 :                 mutex->lock_count++;
     114                 :          0 :                 mutex->owner = _current;
     115                 :            : 
     116         [ #  # ]:          0 :                 LOG_DBG("%p took mutex %p, count: %d, orig prio: %d",
     117                 :            :                         _current, mutex, mutex->lock_count,
     118                 :            :                         mutex->owner_orig_prio);
     119                 :            : 
     120                 :          0 :                 k_spin_unlock(&lock, key);
     121                 :            : 
     122                 :            :                 SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_mutex, lock, mutex, timeout, 0);
     123                 :            : 
     124                 :          0 :                 return 0;
     125                 :            :         }
     126                 :            : 
     127         [ #  # ]:          0 :         if (unlikely(K_TIMEOUT_EQ(timeout, K_NO_WAIT))) {
     128                 :          0 :                 k_spin_unlock(&lock, key);
     129                 :            : 
     130                 :            :                 SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_mutex, lock, mutex, timeout, -EBUSY);
     131                 :            : 
     132                 :          0 :                 return -EBUSY;
     133                 :            :         }
     134                 :            : 
     135                 :            :         SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(k_mutex, lock, mutex, timeout);
     136                 :            : 
     137                 :          0 :         new_prio = new_prio_for_inheritance(_current->base.prio,
     138                 :          0 :                                             mutex->owner->base.prio);
     139                 :            : 
     140         [ #  # ]:          0 :         LOG_DBG("adjusting prio up on mutex %p", mutex);
     141                 :            : 
     142         [ #  # ]:          0 :         if (z_is_prio_higher(new_prio, mutex->owner->base.prio)) {
     143                 :          0 :                 resched = adjust_owner_prio(mutex, new_prio);
     144                 :            :         }
     145                 :            : 
     146                 :          0 :         int got_mutex = z_pend_curr(&lock, key, &mutex->wait_q, timeout);
     147                 :            : 
     148         [ #  # ]:          0 :         LOG_DBG("on mutex %p got_mutex value: %d", mutex, got_mutex);
     149                 :            : 
     150   [ #  #  #  # ]:          0 :         LOG_DBG("%p got mutex %p (y/n): %c", _current, mutex,
     151                 :            :                 got_mutex ? 'y' : 'n');
     152                 :            : 
     153         [ #  # ]:          0 :         if (got_mutex == 0) {
     154                 :            :                 SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_mutex, lock, mutex, timeout, 0);
     155                 :          0 :                 return 0;
     156                 :            :         }
     157                 :            : 
     158                 :            :         /* timed out */
     159                 :            : 
     160         [ #  # ]:          0 :         LOG_DBG("%p timeout on mutex %p", _current, mutex);
     161                 :            : 
     162                 :          0 :         key = k_spin_lock(&lock);
     163                 :            : 
     164                 :          0 :         struct k_thread *waiter = z_waitq_head(&mutex->wait_q);
     165                 :            : 
     166                 :          0 :         new_prio = (waiter != NULL) ?
     167         [ #  # ]:          0 :                 new_prio_for_inheritance(waiter->base.prio, mutex->owner_orig_prio) :
     168                 :            :                 mutex->owner_orig_prio;
     169                 :            : 
     170         [ #  # ]:          0 :         LOG_DBG("adjusting prio down on mutex %p", mutex);
     171                 :            : 
     172   [ #  #  #  # ]:          0 :         resched = adjust_owner_prio(mutex, new_prio) || resched;
     173                 :            : 
     174         [ #  # ]:          0 :         if (resched) {
     175                 :          0 :                 z_reschedule(&lock, key);
     176                 :            :         } else {
     177                 :          0 :                 k_spin_unlock(&lock, key);
     178                 :            :         }
     179                 :            : 
     180                 :            :         SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_mutex, lock, mutex, timeout, -EAGAIN);
     181                 :            : 
     182                 :          0 :         return -EAGAIN;
     183                 :            : }
     184                 :            : 
     185                 :            : #ifdef CONFIG_USERSPACE
     186                 :            : static inline int z_vrfy_k_mutex_lock(struct k_mutex *mutex,
     187                 :            :                                       k_timeout_t timeout)
     188                 :            : {
     189                 :            :         Z_OOPS(Z_SYSCALL_OBJ(mutex, K_OBJ_MUTEX));
     190                 :            :         return z_impl_k_mutex_lock(mutex, timeout);
     191                 :            : }
     192                 :            : #include <syscalls/k_mutex_lock_mrsh.c>
     193                 :            : #endif
     194                 :            : 
     195                 :          0 : int z_impl_k_mutex_unlock(struct k_mutex *mutex)
     196                 :            : {
     197                 :            :         struct k_thread *new_owner;
     198                 :            : 
     199         [ #  # ]:          0 :         __ASSERT(!arch_is_in_isr(), "mutexes cannot be used inside ISRs");
     200                 :            : 
     201                 :            :         SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_mutex, unlock, mutex);
     202                 :            : 
     203         [ #  # ]:          0 :         CHECKIF(mutex->owner == NULL) {
     204                 :            :                 SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_mutex, unlock, mutex, -EINVAL);
     205                 :            : 
     206                 :          0 :                 return -EINVAL;
     207                 :            :         }
     208                 :            :         /*
     209                 :            :          * The current thread does not own the mutex.
     210                 :            :          */
     211         [ #  # ]:          0 :         CHECKIF(mutex->owner != _current) {
     212                 :            :                 SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_mutex, unlock, mutex, -EPERM);
     213                 :            : 
     214                 :          0 :                 return -EPERM;
     215                 :            :         }
     216                 :            : 
     217                 :            :         /*
     218                 :            :          * Attempt to unlock a mutex which is unlocked. mutex->lock_count
     219                 :            :          * cannot be zero if the current thread is equal to mutex->owner,
     220                 :            :          * therefore no underflow check is required. Use assert to catch
     221                 :            :          * undefined behavior.
     222                 :            :          */
     223         [ #  # ]:          0 :         __ASSERT_NO_MSG(mutex->lock_count > 0U);
     224                 :            : 
     225                 :          0 :         z_sched_lock();
     226                 :            : 
     227         [ #  # ]:          0 :         LOG_DBG("mutex %p lock_count: %d", mutex, mutex->lock_count);
     228                 :            : 
     229                 :            :         /*
     230                 :            :          * If we are the owner and count is greater than 1, then decrement
     231                 :            :          * the count and return and keep current thread as the owner.
     232                 :            :          */
     233         [ #  # ]:          0 :         if (mutex->lock_count > 1U) {
     234                 :          0 :                 mutex->lock_count--;
     235                 :          0 :                 goto k_mutex_unlock_return;
     236                 :            :         }
     237                 :            : 
     238                 :          0 :         k_spinlock_key_t key = k_spin_lock(&lock);
     239                 :            : 
     240                 :          0 :         adjust_owner_prio(mutex, mutex->owner_orig_prio);
     241                 :            : 
     242                 :            :         /* Get the new owner, if any */
     243                 :          0 :         new_owner = z_unpend_first_thread(&mutex->wait_q);
     244                 :            : 
     245                 :          0 :         mutex->owner = new_owner;
     246                 :            : 
     247   [ #  #  #  # ]:          0 :         LOG_DBG("new owner of mutex %p: %p (prio: %d)",
     248                 :            :                 mutex, new_owner, new_owner ? new_owner->base.prio : -1000);
     249                 :            : 
     250         [ #  # ]:          0 :         if (new_owner != NULL) {
     251                 :            :                 /*
     252                 :            :                  * new owner is already of higher or equal prio than first
     253                 :            :                  * waiter since the wait queue is priority-based: no need to
     254                 :            :                  * adjust its priority
     255                 :            :                  */
     256                 :          0 :                 mutex->owner_orig_prio = new_owner->base.prio;
     257                 :          0 :                 arch_thread_return_value_set(new_owner, 0);
     258                 :          0 :                 z_ready_thread(new_owner);
     259                 :          0 :                 z_reschedule(&lock, key);
     260                 :            :         } else {
     261                 :          0 :                 mutex->lock_count = 0U;
     262                 :          0 :                 k_spin_unlock(&lock, key);
     263                 :            :         }
     264                 :            : 
     265                 :            : 
     266                 :          0 : k_mutex_unlock_return:
     267                 :            :         SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_mutex, unlock, mutex, 0);
     268                 :            : 
     269                 :          0 :         k_sched_unlock();
     270                 :            : 
     271                 :          0 :         return 0;
     272                 :            : }
     273                 :            : 
     274                 :            : #ifdef CONFIG_USERSPACE
     275                 :            : static inline int z_vrfy_k_mutex_unlock(struct k_mutex *mutex)
     276                 :            : {
     277                 :            :         Z_OOPS(Z_SYSCALL_OBJ(mutex, K_OBJ_MUTEX));
     278                 :            :         return z_impl_k_mutex_unlock(mutex);
     279                 :            : }
     280                 :            : #include <syscalls/k_mutex_unlock_mrsh.c>
     281                 :            : #endif

Generated by: LCOV version 1.14