Bladeren bron

Merge branch 'patch-devfs-unregister-device'

fslongjin 2 jaren geleden
bovenliggende
commit
f3bd316578
4 gewijzigde bestanden met toevoegingen van 426 en 2 verwijderingen
  1. 81 0
      kernel/arch/x86_64/asm/cmpxchg.h
  2. 104 0
      kernel/common/lockref.h
  3. 17 2
      kernel/common/spinlock.h
  4. 224 0
      kernel/lib/lockref.c

+ 81 - 0
kernel/arch/x86_64/asm/cmpxchg.h

@@ -0,0 +1,81 @@
+#pragma once
+#include <common/compiler.h>
+#include <arch/x86_64/asm/asm.h>
+
+/**
+ * @brief 通过extern不存在的函数,来让编译器报错。以防止不符合要求的代码的产生。
+ */
+extern void __cmpxchg_wrong_size(void) __compiletime_error("Bad argument size for cmpxchg");
+
+// 定义常量:操作符涉及到的字节数
+#define __X86_CASE_B 1
+#define __X86_CASE_W 2
+#define __X86_CASE_L 4
+#define __X86_CASE_Q 8
+
+/**
+ * @brief lock cmpxchg指令的包装。
+ * 将_ptr指向的值与old_ptr指向的值做比较,如果相等,则将_new指向的值,加载到_ptr指向的值中。
+ */
+#define __raw_try_cmpxchg(_ptr, _old_ptr, _new, size)               \
+    ({                                                              \
+        bool is_success = false;                                    \
+        typeof(_ptr) _old = (typeof(_ptr))(_old_ptr);               \
+        typeof(*(_ptr)) __old = *_old;                              \
+        typeof(*(_ptr)) __new = (_new);                             \
+        switch (size)                                               \
+        {                                                           \
+        case __X86_CASE_B:                                          \
+        {                                                           \
+            volatile uint8_t *__ptr = (volatile uint8_t *)(_ptr);   \
+            asm volatile("lock cmpxchgb %[new], %[ptr]\n\t"         \
+                         : CC_OUT(z)(is_success),                   \
+                           [ptr] "+m"(*__ptr),                      \
+                           [old] "+a"(__old)                        \
+                         : [new] "q"(__new)                         \
+                         : "memory");                               \
+            break;                                                  \
+        }                                                           \
+        case __X86_CASE_W:                                          \
+        {                                                           \
+            volatile uint16_t *__ptr = (volatile uint16_t *)(_ptr); \
+            asm volatile("lock cmpxchgw %[new], %[ptr]\n\t"         \
+                         : CC_OUT(z)(is_success),                   \
+                           [ptr] "+m"(*__ptr),                      \
+                           [old] "+a"(__old)                        \
+                         : [new] "q"(__new)                         \
+                         : "memory");                               \
+            break;                                                  \
+        }                                                           \
+        case __X86_CASE_L:                                          \
+        {                                                           \
+            volatile uint32_t *__ptr = (volatile uint32_t *)(_ptr); \
+            asm volatile("lock cmpxchgl %[new], %[ptr]\n\t"         \
+                         : CC_OUT(z)(is_success),                   \
+                           [ptr] "+m"(*__ptr),                      \
+                           [old] "+a"(__old)                        \
+                         : [new] "q"(__new)                         \
+                         : "memory");                               \
+            break;                                                  \
+        }                                                           \
+        case __X86_CASE_Q:                                          \
+        {                                                           \
+            volatile uint64_t *__ptr = (volatile uint64_t *)(_ptr); \
+            asm volatile("lock cmpxchgq %[new], %[ptr]\n\t"         \
+                         : CC_OUT(z)(is_success),                   \
+                           [ptr] "+m"(*__ptr),                      \
+                           [old] "+a"(__old)                        \
+                         : [new] "q"(__new)                         \
+                         : "memory");                               \
+            break;                                                  \
+        }                                                           \
+        default:                                                    \
+            __cmpxchg_wrong_size();                                 \
+        }                                                           \
+        if (unlikely(is_success == false))                          \
+            *_old = __old;                                          \
+        likely(is_success);                                         \
+    })
+
+#define arch_try_cmpxchg(ptr, old_ptr, new_ptr) \
+    __raw_try_cmpxchg((ptr), (old_ptr), (new_ptr), sizeof(*ptr))

+ 104 - 0
kernel/common/lockref.h

@@ -0,0 +1,104 @@
+#pragma once
+
+#include <common/sys/types.h>
+#include <common/spinlock.h>
+
+#if ARCH(X86_64)
+// 仅在x64架构下启用cmpxchg
+#define __LOCKREF_ENABLE_CMPXCHG__
+#endif
+struct lockref
+{
+    union
+    {
+#ifdef __LOCKREF_ENABLE_CMPXCHG__
+        aligned_u64 lock_count; // 通过该变量的声明,使得整个lockref按照8字节对齐
+#endif
+        struct
+        {
+            spinlock_t lock;
+            int count;
+        };
+    };
+};
+
+/**
+ * @brief 原子的将引用计数加1
+ * 
+ * @param lock_ref 要被操作的lockref变量
+ */
+void lockref_inc(struct lockref *lock_ref);
+
+/**
+ * @brief 原子地将引用计数加1.如果原来的count≤0,则操作失败。
+ *
+ * @param lock_ref 指向要被操作的lockref变量的指针
+ * @return int  操作成功=>true
+ *              操作失败=>false
+ */
+bool lockref_inc_not_zero(struct lockref *lock_ref);
+
+/**
+ * @brief 原子地减少引用计数。如果已处于count≤0的状态,则返回-1
+ *
+ * 本函数与lockref_dec_return()的区别在于,当在cmpxchg()中检测到count<=0或已加锁,本函数会再次尝试通过加锁来执行操作
+ * 而后者会直接返回错误
+ * 
+ * @param lock_ref 指向要被操作的lockref变量的指针
+ * @return int 操作成功 => 返回新的引用变量值
+ *             lockref处于count≤0的状态 => 返回-1
+ */
+int lockref_dec(struct lockref *lock_ref);
+
+/**
+ * @brief 原子地减少引用计数。如果处于已加锁或count≤0的状态,则返回-1
+ *
+ * 本函数与lockref_dec()的区别在于,当在cmpxchg()中检测到count<=0或已加锁,本函数会直接返回错误
+ * 而后者会再次尝试通过加锁来执行操作
+ * 
+ * @param lock_ref 指向要被操作的lockref变量的指针
+ * @return int  操作成功 => 返回新的引用变量值
+ *              lockref处于已加锁或count≤0的状态 => 返回-1
+ */
+int lockref_dec_return(struct lockref *lock_ref);
+
+
+/**
+ * @brief 原子地减少引用计数。若当前的引用计数≤1,则操作失败
+ * 
+ * 该函数与lockref_dec_or_lock_not_zero()的区别在于,当cmpxchg()时发现old.count≤1时,该函数会直接返回false.
+ * 而后者在这种情况下,会尝试加锁来进行操作。
+ * 
+ * @param lock_ref 指向要被操作的lockref变量的指针
+ * @return true 成功将引用计数减1
+ * @return false 如果当前的引用计数≤1,操作失败
+ */
+bool lockref_dec_not_zero(struct lockref *lock_ref);
+
+/**
+ * @brief 原子地减少引用计数。若当前的引用计数≤1,则操作失败
+ *
+ * 该函数与lockref_dec_not_zero()的区别在于,当cmpxchg()时发现old.count≤1时,该函数会尝试加锁来进行操作。
+ * 而后者在这种情况下,会直接返回false.
+ *
+ * @param lock_ref 指向要被操作的lockref变量的指针
+ * @return true 成功将引用计数减1
+ * @return false 如果当前的引用计数≤1,操作失败
+ */
+bool lockref_dec_or_lock_not_zero(struct lockref *lock_ref);
+
+/**
+ * @brief 将lockref变量标记为已经死亡(将count设置为负值)
+ * 
+ * @param lock_ref 指向要被操作的lockref变量的指针
+ */
+void lockref_mark_dead(struct lockref * lock_ref);
+
+/**
+ * @brief 自增引用计数。(除非该lockref已经死亡)
+ * 
+ * @param lock_ref 指向要被操作的lockref变量的指针
+ * @return true 操作成功
+ * @return false 操作失败,lockref已死亡
+ */
+bool lockref_inc_not_dead(struct lockref *lock_ref);

+ 17 - 2
kernel/common/spinlock.h

@@ -11,6 +11,7 @@
 #pragma once
 #include <common/glib.h>
 #include <process/preempt.h>
+#include <debug/bug.h>
 
 /**
  * @brief 定义自旋锁结构体
@@ -19,8 +20,7 @@
 typedef struct
 {
     int8_t lock; // 1:unlocked 0:locked
-}spinlock_t;
-
+} spinlock_t;
 
 /**
  * @brief 自旋锁加锁
@@ -167,3 +167,18 @@ long spin_trylock(spinlock_t *lock)
         spin_unlock(lock);    \
         local_irq_enable();   \
     } while (0)
+
+/**
+ * @brief 判断自旋锁是否已经加锁
+ * 
+ * @param lock 待判断的自旋锁
+ * @return true 已经加锁
+ * @return false 尚未加锁
+ */
+static inline bool spin_is_locked(const spinlock_t *lock)
+{
+    int x = READ_ONCE(lock->lock);
+    return (x == 0) ? true : false;
+}
+
+#define assert_spin_locked(lock) BUG_ON(!spin_is_locked(lock))

+ 224 - 0
kernel/lib/lockref.c

@@ -0,0 +1,224 @@
+#include <common/lockref.h>
+#include <common/compiler.h>
+
+#ifdef __LOCKREF_ENABLE_CMPXCHG__
+#include <arch/x86_64/asm/cmpxchg.h>
+
+#define CMPXCHG_LOOP(__lock_ref, CODE, SUCCESS)                                                     \
+    {                                                                                               \
+        int retry = 100;                                                                            \
+        struct lockref old;                                                                         \
+        BUILD_BUG_ON(sizeof(old) != sizeof(uint64_t));                                              \
+        old.lock_count = READ_ONCE(__lock_ref->lock_count);                                         \
+        while (likely(!spin_is_locked(&old.lock)))                                                  \
+        {                                                                                           \
+            struct lockref new = old;                                                               \
+            CODE;                                                                                   \
+            if (likely(arch_try_cmpxchg(&__lock_ref->lock_count, &old.lock_count, new.lock_count))) \
+            {                                                                                       \
+                SUCCESS;                                                                            \
+            }                                                                                       \
+            if (!--retry)                                                                           \
+                break;                                                                              \
+            pause();                                                                                \
+        }                                                                                           \
+    }
+#else
+
+#define CMPXCHG_LOOP(__lock_ref, CODE, SUCCESS) \
+    do                                          \
+    {                                           \
+    } while (0)
+
+#endif
+
+/**
+ * @brief 原子的将引用计数加1
+ *
+ * @param lock_ref 指向要被操作的lockref变量的指针
+ */
+void lockref_inc(struct lockref *lock_ref)
+{
+    // 先尝试使用cmpxchg进行无锁操作,若成功则返回
+    CMPXCHG_LOOP(lock_ref, ++new.count;, return;);
+
+    // 无锁操作超时,或当前是上锁的状态,则退化为有锁操作
+    spin_lock(&lock_ref->lock);
+    ++lock_ref->count;
+    spin_unlock(&lock_ref->lock);
+}
+
+/**
+ * @brief 原子地将引用计数加1.如果原来的count≤0,则操作失败。
+ *
+ * @param lock_ref 指向要被操作的lockref变量的指针
+ * @return int  操作成功=>true
+ *              操作失败=>false
+ */
+bool lockref_inc_not_zero(struct lockref *lock_ref)
+{
+    CMPXCHG_LOOP(lock_ref,
+                 if (old.count <= 0) return false;
+                 ++new.count;
+                 ,
+                 return true;)
+    bool retval;
+    spin_lock(&lock_ref->lock);
+    retval = false;
+    if (lock_ref->count > 0)
+    {
+        ++lock_ref->count;
+        retval = true;
+    }
+    spin_unlock(&lock_ref->lock);
+    return retval;
+}
+
+/**
+ * @brief 原子地减少引用计数。如果已处于count≤0的状态,则返回-1
+ *
+ * 本函数与lockref_dec_return()的区别在于,当在cmpxchg()中检测到count<=0或已加锁,本函数会再次尝试通过加锁来执行操作
+ * 而后者会直接返回错误
+ *
+ * @param lock_ref 指向要被操作的lockref变量的指针
+ * @return int 操作成功 => 返回新的引用变量值
+ *             lockref处于count≤0的状态 => 返回-1
+ */
+int lockref_dec(struct lockref *lock_ref)
+{
+    CMPXCHG_LOOP(lock_ref,
+                 if (old.count <= 0) break;
+                 --new.count;
+                 ,
+                 return new.count;);
+
+    // 如果xchg时,处于已加锁的状态或者检测到old.count <= 0,则采取加锁处理
+    int retval = -1;
+    spin_lock(&lock_ref->lock);
+    if (lock_ref->count > 0)
+    {
+        --lock_ref->count;
+        retval = lock_ref->count;
+    }
+    spin_unlock(&lock_ref->lock);
+
+    return retval;
+}
+
+/**
+ * @brief 原子地减少引用计数。如果处于已加锁或count≤0的状态,则返回-1
+ *
+ * 本函数与lockref_dec()的区别在于,当在cmpxchg()中检测到count<=0或已加锁,本函数会直接返回错误
+ * 而后者会再次尝试通过加锁来执行操作
+ *
+ * @param lock_ref 指向要被操作的lockref变量的指针
+ * @return int  操作成功 => 返回新的引用变量值
+ *              lockref处于已加锁或count≤0的状态 => 返回-1
+ */
+int lockref_dec_return(struct lockref *lock_ref)
+{
+    CMPXCHG_LOOP(lock_ref,
+                 if (old.count <= 0) return -1;
+                 --new.count;
+                 ,
+                 return new.count;);
+
+    return -1;
+}
+
+/**
+ * @brief 原子地减少引用计数。若当前的引用计数≤1,则操作失败
+ *
+ * 该函数与lockref_dec_or_lock_not_zero()的区别在于,当cmpxchg()时发现old.count≤1时,该函数会直接返回false.
+ * 而后者在这种情况下,会尝试加锁来进行操作。
+ *
+ * @param lock_ref 指向要被操作的lockref变量的指针
+ * @return true 成功将引用计数减1
+ * @return false 如果当前的引用计数≤1,操作失败
+ */
+bool lockref_dec_not_zero(struct lockref *lock_ref)
+{
+    CMPXCHG_LOOP(lock_ref,
+                 if (old.count <= 1) return false;
+                 --new.count;
+                 ,
+                 return true;)
+
+    bool retval = false;
+    spin_lock(&lock_ref->lock);
+    if (lock_ref->count > 1)
+    {
+        --lock_ref->count;
+        retval = true;
+    }
+    spin_unlock(&lock_ref->lock);
+    return retval;
+}
+
+/**
+ * @brief 原子地减少引用计数。若当前的引用计数≤1,则操作失败
+ *
+ * 该函数与lockref_dec_not_zero()的区别在于,当cmpxchg()时发现old.count≤1时,该函数会尝试加锁来进行操作。
+ * 而后者在这种情况下,会直接返回false.
+ *
+ * @param lock_ref 指向要被操作的lockref变量的指针
+ * @return true 成功将引用计数减1
+ * @return false 如果当前的引用计数≤1,操作失败
+ */
+bool lockref_dec_or_lock_not_zero(struct lockref *lock_ref)
+{
+    CMPXCHG_LOOP(lock_ref,
+                 if (old.count <= 1) break;
+                 --new.count;
+                 ,
+                 return true;);
+
+    bool retval = false;
+    spin_lock(&lock_ref->lock);
+    if (lock_ref->count > 1)
+    {
+        --lock_ref->count;
+        retval = true;
+    }
+    spin_unlock(&lock_ref->lock);
+    return retval;
+}
+
+/**
+ * @brief 将lockref变量标记为已经死亡(将count设置为负值)
+ *
+ * @param lock_ref 指向要被操作的lockref变量的指针
+ */
+void lockref_mark_dead(struct lockref *lock_ref)
+{
+    // 需要自旋锁先被加锁,若没有被加锁,则会抛出错误信息
+    assert_spin_locked(&lock_ref->lock);
+    lock_ref->count = -128;
+}
+
+/**
+ * @brief 自增引用计数。(除非该lockref已经死亡)
+ *
+ * @param lock_ref 指向要被操作的lockref变量的指针
+ * @return true 操作成功
+ * @return false 操作失败,lockref已死亡
+ */
+bool lockref_inc_not_dead(struct lockref *lock_ref)
+{
+    CMPXCHG_LOOP(lock_ref,
+                 if (old.count < 0) return false;
+                 ++new.count;
+                 ,
+                 return true;)
+
+    bool retval = false;
+    // 快捷路径操作失败,尝试加锁
+    spin_lock(&lock_ref->lock);
+    if (lock_ref->count >= 0)
+    {
+        ++lock_ref->count;
+        retval = true;
+    }
+    spin_unlock(&lock_ref->lock);
+    return retval;
+}