뮤텍스를 써서 Critical Section을 보호하는 루틴 1
https://elixir.bootlin.com/linux/v5.15.30/source/drivers/cpufreq/cpufreq_governor.c
static void dbs_work_handler(struct work_struct *work)
{
struct policy_dbs_info *policy_dbs;
struct cpufreq_policy *policy;
struct dbs_governor *gov;
policy_dbs = container_of(work, struct policy_dbs_info, work);
policy = policy_dbs->policy;
gov = dbs_governor_of(policy);
mutex_lock(&policy_dbs->update_mutex);
gov_update_sample_delay(policy_dbs, gov->gov_dbs_update(policy)); // 이 함수는 하나의 프로세스만 실행이 된다.
mutex_unlock(&policy_dbs->update_mutex);
...
}
뮤텍스를 써서 Critical Section을 보호하는 루틴 2
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 5419c4b9f27ad..80c5a1c572168 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c // arm에서 제공하는 ip
@@ -273,7 +273,9 @@ int iommu_probe_device(struct device *dev)
* support default domains, so the return value is not yet
* checked.
*/
+ mutex_lock(&group->mutex);
iommu_alloc_default_domain(group, dev);
+ mutex_unlock(&group->mutex);
if (group->default_domain) {
ret = __iommu_attach_device(group->default_domain, dev);
- CONFIG_MUTEX_SPIN_ON_OWNER, CONFIG_DEBUG_MUTEXES,
CONFIG_DEBUG_LOCK_ALLOC은 추가 디버깅 피쳐
뮤텍스 자료 구조
https://elixir.bootlin.com/linux/v5.15.30/source/include/linux/mutex.h
struct mutex {
atomic_long_t owner; // 뮤텍스를 획득한 프로세스의 태스크 디스크립터 주소 오너가 0면 뮤텍스를 획득한 프로세스가 없다. 오너에 주소가 보이면 이 뮤텍스를 어떤 프로세스가 획득했다.
raw_spinlock_t wait_lock;
#ifdef CONFIG_MUTEX_SPIN_ON_OWNER
struct optimistic_spin_queue osq; /* Spinner MCS lock */
#endif
struct list_head wait_list; // 뮤텍스를 기다리는 프로세스의 정보, 자신을 등록하고 슬립에 돌입
#ifdef CONFIG_DEBUG_MUTEXES
void *magic;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
};
뮤텍스 자료 구조
crash64> struct mutex
struct mutex {
atomic_long_t owner;
raw_spinlock_t wait_lock;
struct optimistic_spin_queue osq;
struct list_head wait_list;
}
SIZE: 32
뮤텍스 자료 구조
https://elixir.bootlin.com/linux/v5.15.30/source/kernel/locking/mutex.h
struct mutex_waiter {
struct list_head list;
struct task_struct *task;
struct ww_acquire_ctx *ww_ctx;
#ifdef CONFIG_DEBUG_MUTEXES
void *magic;
#endif
};
미 뮤텍스를 획득한 경우 실행되는 동작
뮤텍스의 구현 방식: fastpath
https://elixir.bootlin.com/linux/v5.15.30/source/kernel/locking/mutex.c
void __sched mutex_lock(struct mutex *lock)
{
might_sleep();
if (!__mutex_trylock_fast(lock)) // 뮤텍스의 구조체 체크 , 오너필드 0이면 뮤텍스 오너에 자신의 프로세스의 속성정보가 담긴 태스크디스크립터 주소를 저장하고 크리티컬 섹션
__mutex_lock_slowpath(lock);
}
EXPORT_SYMBOL(mutex_lock);
뮤텍스의 구현 방식: fastpath
https://elixir.bootlin.com/linux/v5.15.30/source/kernel/locking/mutex.c
static __always_inline bool __mutex_trylock_fast(struct mutex *lock)
{
unsigned long curr = (unsigned long)current; // current : 현재 실행중인 프로세스의 태스크 디스크립터의 시작주소 curr이라는 로컬변수에 저장
unsigned long zero = 0UL;
if (atomic_long_try_cmpxchg_acquire(&lock->owner, &zero, curr)) // 0이면 curr 오너에 저장
return true;
return false;
}
뮤텍스의 구현 방식: fastpath
https://elixir.bootlin.com/linux/v5.15.30/source/kernel/locking/mutex.c
void __sched mutex_unlock(struct mutex *lock)
{
#ifndef CONFIG_DEBUG_LOCK_ALLOC
if (__mutex_unlock_fast(lock)) // 현재 이코드를 실행하고 있는
return;
#endif
__mutex_unlock_slowpath(lock, _RET_IP_);
}
EXPORT_SYMBOL(mutex_unlock);
뮤텍스의 구현 방식: fastpath
https://elixir.bootlin.com/linux/v5.15.30/source/kernel/locking/mutex.c
static __always_inline bool __mutex_unlock_fast(struct mutex *lock)
{
unsigned long curr = (unsigned long)current; // 프로세스의 태스크 디스크립터의 현재주소를 curr에 저장, 락 오너의 필드를 0으로 바꿔줌
return atomic_long_try_cmpxchg_release(&lock->owner, &curr, 0UL);
}
뮤텍스의 구현 방식: slowpath
https://elixir.bootlin.com/linux/v5.15.30/source/kernel/locking/mutex.c
static __always_inline int __sched
__mutex_lock_common(struct mutex *lock, unsigned int state, unsigned int subclass,
struct lockdep_map *nest_lock, unsigned long ip,
struct ww_acquire_ctx *ww_ctx, const bool use_ww_ctx)
{
...
waiter.task = current; // 뮤텍스획득을 시도하다가 슬립에 진입하는 프로세스의 태스크 주소를 이 필드에 저장
...
set_current_state(state) // task 언인터럽터블 상태, 프로세스가 깨어날 조건을 설정하고 슬립에 진입할 때, 아래의 함수를 호출하고 슬립에 진입.
...
raw_spin_unlock(&lock->wait_lock);
schedule_preempt_disabled(); // 슬립에 진입, 뮤텍스를 해제할 때 아래의 루틴 실행
...
acquired:
__set_current_state(TASK_RUNNING);
...
}
뮤텍스의 구현 방식: slowpath
static noinline void __sched __mutex_unlock_slowpath(struct mutex *lock, unsigned long ip)
{
struct task_struct *next = NULL;
...
if (!list_empty(&lock->wait_list)) {
/* get the first entry from the wait-list: */
struct mutex_waiter *waiter =
list_first_entry(&lock->wait_list,
struct mutex_waiter, list);
next = waiter->task;
debug_mutex_wake_waiter(lock, waiter);
wake_q_add(&wake_q, next); // 깨움
}
...
wake_up_q(&wake_q);
}
Ftrace: 뮤텍스 디버깅
kworker/u8:0-1063 [003] .... 3223.346749: mutex_lock+0x4/0x58 <- // 워커스레드,
flush_to_ldisc+0x48/0x140
kworker/u8:0-1063 [003] .... 3223.346751: <stack trace>
=> mutex_lock+0x8/0x58 // 뮤텍스락
=> flush_to_ldisc+0x48/0x140 // 워크핸들러
=> process_one_work+0x1f4/0x4d8
=> worker_thread+0x50/0x480
=> kthread+0x148/0x158
=> ret_from_fork+0x10/0x30
kworker/u8:0-1063 [003] .... 3223.346760: mutex_unlock+0x4/0x60 <- // 해제할때 스택 트레이스
flush_to_ldisc+0x78/0x140
kworker/u8:0-1063 [003] .... 3223.346761: <stack trace>
=> mutex_unlock+0x8/0x60
=> flush_to_ldisc+0x78/0x140
=> process_one_work+0x1f4/0x4d8
=> worker_thread+0x50/0x480
=> kthread+0x148/0x158
=> ret_from_fork+0x10/0x30