sync mutex - ceragon/LinuxDoc GitHub Wiki

mutex

结构体

struct mutex {
    /* 1: unlocked, 0: locked, negative: locked, possible waiters */
    atomic_t		count;
    spinlock_t		wait_lock;
    struct list_head	wait_list;
    struct thread_info	*owner;
};
typedef struct {
    int counter;
} atomic_t;
typedef struct spinlock {
    union {
        struct raw_spinlock rlock;
    }
} spinlock_t;

typedef struct raw_spinlock {
    arch_spinlock_t raw_lock;
} raw_spinlock_t;

typedef struct arch_spinlock {
    unsigned int slock;
} arch_spinlock_t;
struct list_head {
    struct list_head *next, *prev;
};
struct thread_info {
    struct task_struct	*task;		/* main task structure */
    struct exec_domain	*exec_domain;	/* execution domain */
    __u32			flags;		/* low level flags */
    __u32			status;		/* thread synchronous flags */
    __u32			cpu;		/* current CPU */
    int			preempt_count;	/* 0 => preemptable,
                           <0 => BUG */
    mm_segment_t		addr_limit;
    struct restart_block    restart_block;
    void __user		*sysenter_return;
    int			uaccess_err;
};
struct rq {
    struct task_struct *curr, *idle, *stop;
};

初始化

void __mutex_init(struct mutex *lock, const char *name, struct lock_class_key *key) {
    // 将 count 设为 1
    atomic_set(&lock->count, 1);
//  spin_lock_init(&lock->wait_lock);
    do {
        spinlock_check(&lock->wait_lock);
        do { 
            *(&(&lock->wait_lock)->rlock) = (raw_spinlock_t) {
                .raw_lock={0},
            }; 
        } while (0);
    }
    while (0)
        
    INIT_LIST_HEAD(&lock->wait_list);
        
//  mutex_clear_owner(lock);
    lock->owner = NULL;

    debug_mutex_init(lock, name, key);
}
static inline void INIT_LIST_HEAD(struct list_head *list) {
    list->next = list;
    list->prev = list;
}

lock

void __sched mutex_lock(struct mutex *lock) {
//  might_sleep();
    do { _cond_resched(); } while (0);
    // 锁定快速路径是从“解锁”到“锁定”状态的 1->0 转换。
//  __mutex_fastpath_lock(&lock->count, __mutex_lock_slowpath);
    do {
        unsigned long dummy;
        ({
            atomic_t *__dummy;
            typeof(&lock->count) __dummy2;
            (void) (&__dummy == &__dummy2);
            1;
        });
        ({
            typeof(
            void(*)
            (atomic_t * ))__tmp = __mutex_lock_slowpath;
            (void) __tmp;
        });
        asm volatile(".section .smp_locks,\"a\"\n" ".balign 4\n" ".long 671f - .\n" ".previous\n" "671:" "\n\t"
                        "lock; "
                        "decl (%%rdi)\n"
                        "jns 1f     \n"
                        "call " "__mutex_lock_slowpath" "\n"
                        "1:"
                        :"=D"(dummy)
                        :"D"(&lock->count)
                        :"rax", "rsi", "rdx", "rcx", "r8", "r9", "r10", "r11", "memory");
    }while (0);
    
//  mutex_set_owner(lock);
    lock->owner = current_thread_info();
}
static __used noinline void __sched __mutex_lock_slowpath(atomic_t *lock_count){
//  struct mutex *lock = container_of(lock_count, struct mutex, count);
    struct mutex *lock = ({
        const typeof(((struct mutex *) 0)->count) *__mptr = (lock_count);
        (struct mutex *) ((char *) __mptr - ((size_t) &((struct mutex *) 0)->count));
    });
    __mutex_lock_common(lock, TASK_UNINTERRUPTIBLE, 0, _RET_IP_);
}
static inline int __sched __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass,
	       	unsigned long ip){
    struct task_struct *task = get_current();
    struct mutex_waiter waiter;
    unsigned long flags;

//  preempt_disable();
//  mutex_acquire(&lock->dep_map, subclass, 0, ip);
    
    // 乐观自旋锁
    // 当我们发现没有挂起的等待者并且锁所有者当前在(不同的)CPU 上运行时,我们会尝试自旋获取。
    // 理由是如果锁的所有者正在运行,它很可能很快就会释放锁。
    // 由于这需要锁的拥有者,而且这个互斥锁实现不会在锁字段中原子地跟踪拥有者,我们需要非原子地跟踪它。
    // 我们不能对 DEBUG_MUTEXES 执行此操作,因为它依赖于 wait_lock 来序列化所有内容。
    for (;;) {
        struct thread_info *owner;
        // 判断是否获得了大内核锁
        if (unlikely(current->lock_depth >= 0))
            break;
        owner = ACCESS_ONCE(lock->owner);
        if (owner && !mutex_spin_on_owner(lock, owner))
            break;
        // cmpxchg(&lock->count->counter, old, new)
        // 作用是期望 counter 是 1,然后设置为。和信号量有点类似,只是信号量只有1.
        if (atomic_cmpxchg(&lock->count, 1, 0) == 1) { // 能把信号量从 1 变为 0,说明 down 操作执行成功
//          lock_acquired(&lock->dep_map, ip);
//          mutex_set_owner(lock);
            // lock 成功
            lock->owner = current_thread_info();
//          preempt_enable();
            return 0;
        }
        // 当没有所有者时,我们可能会在所有者获取锁和设置所有者字段之间进行抢占。 
        // 如果我们是一个 RT 任务将 live-lock,因为我们不会让所有者完成。 
        if (!owner && (need_resched() || rt_task(task)))
            break;
        
//      arch_mutex_cpu_relax();
        cpu_relax();
    }
    // 到这一步,就要被迫进入等待队列了
    // 自旋获取等待锁,因为只能有一个core 修改 lock->wait_list
//  spin_lock_mutex(&lock->wait_lock, flags);
    do {
        spin_lock(&lock->wait_lock);
        (void) (flags);
    }
    while (0)
    // waiter就是自己,将自己添加到等待队列的队尾
    list_add_tail(&waiter.list, &lock->wait_list);
    // task 就是当前进程
    waiter.task = task;
    // 将 -1 赋值给 &lock->count,返回 &lock->count 的原值
    if (atomic_xchg(&lock->count, -1) == 1) // 应该是再尝试一次获取锁
        goto done;
    
//  lock_contended(&lock->dep_map, ip);
    
    for (;;) {
        // 让我们再次尝试获取锁 - 即使我们第一次到达这里(在未能获取锁后不久),
        // 这也是必需的,以确保一旦解锁我们就会得到唤醒。 
        // 稍后,如果我们休眠,这就是给我们锁的操作。 
        // 我们将其 xchg 为 -1,这样当我们释放锁时,我们可以正确唤醒其他等候者: 
        if (atomic_xchg(&lock->count, -1) == 1)
            break;
        if (unlikely(signal_pending_state(state, task))) {
            // 感觉像是收到了一个信号,当前进程被迫关闭了,所以需要从等待队列移除
            // 从等待队列移除
            mutex_remove_waiter(lock, &waiter, task_thread_info(task));
//          mutex_release(&lock->dep_map, 1, ip);
//            spin_unlock_mutex(&lock->wait_lock, flags);
            do {
                // 释放持有的 wait_lock
                spin_unlock(&lock->wait_lock);
                (void) (flags);
            }
            while (0);

//          debug_mutex_free_waiter(&waiter);
//          preempt_enable();
            // 中断系统调用    
            return -EINTR;
        }
        // 修改任务的状态为 TASK_UNINTERRUPTIBLE
//		__set_task_state(task, state);
        do { (task)->state = (state); } while (0);
        
        // 不要持有这个锁,去 sleep
//      spin_unlock_mutex(&lock->wait_lock, flags);
        do {
            // 释放持有的 wait_lock
            spin_unlock(&lock->wait_lock);
            (void) (flags);
        }
        while (0);
        
//      preempt_enable_no_resched();
        // 把 cpu 的使用权交出去
        schedule();
//      preempt_disable();
        // 既然被唤醒了,那么再尝试一次。尝试前先强占 wait_lock 
        spin_lock_mutex(&lock->wait_lock, flags);
    }
done:
//  lock_acquired(&lock->dep_map, ip);
    // 取到了锁,此时还占有着 wait_lock。所以可以直接把自己从队列中删除
    mutex_remove_waiter(lock, &waiter, current_thread_info());
    // 设置自己为锁的持有者
    mutex_set_owner(lock);

    // 如果当前没有等待者了,就把信号量置为0
    if (likely(list_empty(&lock->wait_list)))
        atomic_set(&lock->count, 0);
    
    // 释放 wait_lock
    spin_unlock_mutex(&lock->wait_lock, flags);

//    debug_mutex_free_waiter(&waiter);
//    preempt_enable();

    return 0;
}
int mutex_spin_on_owner(struct mutex *lock, struct thread_info *owner) {
    unsigned int cpu;
	struct rq *rq;

	if (!sched_feat(OWNER_SPIN))
		return 0;
    cpu = owner->cpu;
    // cpu索引不能超过 cpu 上限
    if (cpu >= nr_cpumask_bits)
		return 0;
    if (!cpu_online(cpu))
		return 0;
    // 获取 cpu 的 runqueues
    rq = cpu_rq(cpu);
    for (;;) {
        if (lock->owner != owner) { // 如果我不持有锁
            // 其他人正在持有锁
            if (lock->owner)
				return 0;
            // 没有人在持有锁
			break;
        }
        // 现在锁的持有者是我,但是当前 cpu 的 rq 中 curr 不是我,而且需要重新安排,则自旋失败。
        // 如果当前 cpu 的 rq 中 curr 是我,则再次自旋。
        
        // 条件1:我是否在 runqueues 中,且当前是我在执行
        // 条件2:是否需要重新安排
        // 总结:Is that owner really running on that cpu? 我是否真的运行在这个 cpu 上。
        if (task_thread_info(rq->curr) != owner || need_resched())
			return 0;

//      arch_mutex_cpu_relax();
        // cpu_relax()调用会降低CPU的能量消耗或者让位于超线程双处理器;
        // #define cpu_relax()	asm volatile("rep; nop" ::: "memory");
        cpu_relax(); 
        
    }
    // 没有人持有锁,自旋成功
    return 1;
}

unlock

void __sched mutex_unlock(struct mutex *lock) {
    // 解锁快速路径是从“锁定”状态到“解锁”状态的 0->1 转换:
    // 启用调试后,我们不能在时间之前清除所有者,将始终采用慢速路径,
    // 并且在验证所有者字段确实是当前字段后清除它。
//  mutex_clear_owner(lock);
    lock->owner = NULL;
//	__mutex_fastpath_unlock(&lock->count, __mutex_unlock_slowpath);
    do {
        unsigned long dummy;
        ({
            atomic_t *__dummy;
            typeof(&lock->count) __dummy2;
            (void) (&__dummy == &__dummy2);
            1;
        });
        ({
        typeof(
            void(*)
            (atomic_t * ))__tmp = __mutex_unlock_slowpath;
            (void) __tmp;
        });
        asm volatile(".section .smp_locks,\"a\"\n" ".balign 4\n" ".long 671f - .\n" ".previous\n" "671:" "\n\t"
                     // 锁缓存行                                                                                     
                     "lock; "
                     // 将 &lock->count 自增
                     "incl (%%rdi)\n"
                     "jg 1f\n"
                     "call " "__mutex_unlock_slowpath" "\n"
                     "1:"
                     :"=D"(dummy)
                     :"D"(&lock->count)
                     :"rax", "rsi", "rdx", "rcx", "r8", "r9", "r10", "r11", "memory");
    }while (0);
}
static __used noinline void __mutex_unlock_slowpath(atomic_t *lock_count) {
//	__mutex_unlock_common_slowpath(lock_count, 1);
//    struct mutex *lock = container_of(lock_count, struct mutex, count);
    struct mutex *lock = ({
        const typeof(((struct mutex *)0)->count) *__mptr = (lock_count);
        (struct mutex *)((char *)__mptr - ((size_t) & ((struct mutex *)0)->count));
    })
    unsigned long flags;
    // 自旋的方式获取 wait_lock
    spin_lock_mutex(&lock->wait_lock, flags);
//    mutex_release(&lock->dep_map, nested, _RET_IP_);
//    debug_mutex_unlock(lock);
    // 一些架构在快速路径失败的情况下保持解锁状态,而另一些架构则需要保持锁定状态。
    // 在后一种情况下,我们必须在这里解锁
    if (__mutex_slowpath_needs_to_unlock())
        // 释放信号量
        atomic_set(&lock->count, 1);
    
    if (!list_empty(&lock->wait_list)) { // 当前有等待者
        // 获取排第一的等待者
        struct mutex_waiter *waiter =
            list_entry(lock->wait_list.next, struct mutex_waiter, list);

//        debug_mutex_wake_waiter(lock, waiter);
        // 唤醒等待的进程
        wake_up_process(waiter->task);
    }
    // 释放等待锁
    spin_unlock_mutex(&lock->wait_lock, flags);
}
⚠️ **GitHub.com Fallback** ⚠️