日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

rw_semaphore 原理与代码分析

發布時間:2024/3/13 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 rw_semaphore 原理与代码分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

代碼基于內核5.10版本

信號量的進階形式,對讀者寫者進行區分,寫者時互斥的,寫者持鎖時其余的寫者和讀者都只能等待。讀者是允許并發的,讀者持鎖時允許其他的讀者持鎖,但是寫者必須等待。

后續的分析不涉及死鎖檢測,也就是lockdep部分。

常用的API如下:

DECLARE_RWSEM(name)聲明名為name的讀寫信號量,并初始化它。
void init_rwsem(struct rw_semaphore *sem);對讀寫信號量sem進行初始化。
void down_read(struct rw_semaphore *sem);讀者用來獲取sem,若沒獲得時,則調用者睡眠等待。
void up_read(struct rw_semaphore *sem);讀者釋放sem。
int down_read_trylock(struct rw_semaphore *sem);讀者嘗試獲取sem,如果獲得返回1,如果沒有獲得返回0。可在中斷上下文使用。
void down_write(struct rw_semaphore *sem);寫者用來獲取sem,若沒獲得時,則調用者睡眠等待。
int down_write_trylock(struct rw_semaphore *sem);寫者嘗試獲取sem,如果獲得返回1,如果沒有獲得返回0。可在中斷上下文使用
void up_write(struct rw_semaphore *sem);寫者釋放sem。
void downgrade_write(struct rw_semaphore *sem);把寫者降級為讀者。

急用請看這

說實話,這部分機制還是比較難的,并且如果不是需要去深入研究其機制,完全沒必要了解這么多。只需要簡單debug,我這個給出一個快速的總結

struct rw_semaphore {atomic_long_t count; //用于記錄鎖的狀態atomic_long_t owner; //持鎖的寫者或者其中一個讀者struct optimistic_spin_queue osq; //用于樂觀自旋raw_spinlock_t wait_lock; //保護wait_list結構的spinlockstruct list_head wait_list; //等鎖者鏈表void *magic; //魔術數,記錄鎖的地址,當其被改變時可以認為鎖的數據結構被破壞struct lockdep_map dep_map; //死鎖檢測,暫不關注 } count bit* Bit 0 - writer locked bit* Bit 1 - waiters present bit* Bit 2 - lock handoff bit* Bits 3-7 - reserved* Bits 8-62 - 55-bit reader count* Bit 63 - read fail bit

遇到讀寫鎖相關問題,那大概率是發生在進程卡在rw_sem上了,這時候需要快速找到持鎖者,可以從棧中拿到sem的地址以及數據,然后尋找真正的持鎖者。注意owner最后幾個bit需要清零才是task結構。

  • magic不是鎖的首地址,那不用看了,結構體被破壞,屬于內存踩踏問題
  • writer locked bit被置位,那肯定是寫者持鎖,找owner就可以找到持鎖者。不要理會reader count是否存在,讀者會先斬后奏,這部分不能代表讀者持鎖。
  • writer locked bit為0,reader count等于1,這時候是讀者持鎖,可能沒有完全持鎖成功,即只寫入了count還沒來得及走完后面的流程。此時owner不一定準確,可能為NULL,也可能是讀者中的一個。如果讀者計數為1,并且owner是一個task地址,那么大概率就只有這個讀者持鎖。
  • writer locked bit為0,讀者計數大于1,此時owner為NULL是正常的,為讀者中的一個也是正常的。這種情況是沒有辦法通過鎖結構找完所有讀者的。可以嘗試開啟相關debug功能。如果你正在使用使用crash tool分析一個內核尸體的話, search -t 鎖地址 來尋找所有線程棧上可能殘留的蛛絲馬跡。沒有收獲,也沒有bug功能,那就只能自己使用kprobe之類的工具來debug了。
  • 一些奇奇怪怪的中間態:讀者計數為0,寫者沒有持鎖,存在等待者。這種情況要么鎖當前沒有問題,只是panic的時候恰巧碰到了這種狀態,要么等待隊列的隊首進程狀態異常,可能是自身非法調度引起,也可能是其所在cpu異常。
  • handoff機制

    handoff這里應該翻譯為“接力”,它的出現是為了彌補樂觀自旋帶來的漏洞,既并非先到先得。鎖大多被設計為先到先得,是為了實現公平,防止某些進程一直得不到資源被餓死。而樂觀自旋是為了減少進程切換的開銷,降低鎖帶來的延遲,但是樂觀自旋會打破先到先得的規則。

    semaphore的handoff是為了防止偷鎖的情況出現。等待隊列的進程都是在睡眠的,嘗試持鎖的進程可以進入樂觀自旋階段,這個階段無視等待隊列一旦有機會就會獲取鎖,造成本該最先拿到鎖的等待隊列隊首被跳過。隊首被喚醒持鎖,發現鎖已經被偷取,這時候就會設置handoff bit,后面所有的持鎖者看到這個bit就不能再偷鎖了。因此隊首進程可以順利獲取到鎖。

    設置時機:
    對于讀者,當讀者在隊首并且被喚醒嘗試持鎖失敗之后,如果超時設置handoff。
    對于寫者,同樣在隊首,當自身是RT進程或者超時時設置handoff。

    生效時機:
    所有試圖持鎖者都需要檢測是否有handoff標志位,一旦存在均會持鎖失敗。設置者會在持鎖成功之后才清除這個標志位。

    樂觀自旋

    樂觀自旋是性能和速度的均衡考慮,讓進程在滿足一定的條件下循環等待某些數據,即期待持有鎖的進程盡快釋放鎖所以占用cpu一段時間來死等不退出,避免自身陷入睡眠然后很快被喚醒。一段時間是指調度器沒有標記當前進程需要被調度走。 偽代碼如下:

    while(1) {檢測數據是否發生變變化是 退出,返回成功當前cpu時間片是否用盡?是 退出,返回失敗當前需要等待的進程是否還在cpu上運行否 退出,返回失敗 }

    數據結構

    實際生效的定義如下: struct rw_semaphore {atomic_long_t count; //用于記錄鎖的狀態atomic_long_t owner; //持鎖的寫者或者其中一個讀者struct optimistic_spin_queue osq; //用于樂觀自旋raw_spinlock_t wait_lock; //保護wait_list結構的spinlockstruct list_head wait_list; //等鎖者鏈表void *magic; //魔術數,記錄鎖的地址,當其被改變時可以認為鎖的數據結構被破壞struct lockdep_map dep_map; //死鎖檢測,暫不關注 }

    count

    count用來表示當前鎖的狀態較為復雜,其定義如下:

    * Bit 0 - writer locked bit* Bit 1 - waiters present bit* Bit 2 - lock handoff bit* Bits 3-7 - reserved* Bits 8-62 - 55-bit reader count* Bit 63 - read fail bit

    bit 0用來表示是否有寫者持鎖,寫者持鎖成功的標志就是這個位被設置

    bit 1表示等待隊列中是否存在等待者,有等待者時讀者持鎖成功需要喚醒等待隊列的其他讀者,讀寫者釋放鎖時需要喚醒等待隊列的隊首進程

    bit 2表示hand off bit,這個是為了防止餓死設計的。handoff bit會阻止所有的偷鎖/持鎖行為保證鎖的交接。

    bit 8-62 表示讀者的數目,這個數目并不能真正的反應持鎖成功的讀者個數。某些情況下讀者會采取先斬后奏的方式,先加上這個計數,然后再判斷自己能否持鎖,不行的話就減去恢復原來的計數。一般而言,當讀者持鎖時這個計數是真實的,但是寫者持鎖時也可能有讀者計數,這部分就是先斬后奏的讀者。

    最高位63表示讀者個數溢出位,幾乎不會被設置,但是在某些路徑還是可能會被檢查

    下面是用于判斷count的一些掩碼。

    #define RWSEM_WRITER_LOCKED (1UL << 0) 1 #define RWSEM_FLAG_WAITERS (1UL << 1) 2 #define RWSEM_FLAG_HANDOFF (1UL << 2) 4 #define RWSEM_FLAG_READFAIL (1UL << (BITS_PER_LONG - 1)) 1<<63#define RWSEM_READER_SHIFT 8 #define RWSEM_READER_BIAS (1UL << RWSEM_READER_SHIFT) 1<<8 #define RWSEM_READER_MASK (~(RWSEM_READER_BIAS - 1)) 0xffff ffff ffff ff00 #define RWSEM_WRITER_MASK RWSEM_WRITER_LOCKED 1 #define RWSEM_LOCK_MASK (RWSEM_WRITER_MASK|RWSEM_READER_MASK) 0xffff ffff ffff ff01 #define RWSEM_READ_FAILED_MASK (RWSEM_WRITER_MASK|RWSEM_FLAG_WAITERS|\RWSEM_FLAG_HANDOFF|RWSEM_FLAG_READFAIL) 0x8000 0000 0000 0007

    osq

    struct optimistic_spin_queue osq 成員用于樂觀自旋,暫不做詳細分析,其本質為mcslock的一種變體,當持鎖者還在cpu上運行時,等鎖者可以期待持鎖者很快會釋放這個鎖,所以不會陷入睡眠而是自旋等待。反之持鎖者已經放棄了cpu,那么預計很長一段時間內持鎖者都不會釋放鎖,此時等鎖者陷入睡眠不再忙等。

    owner

    owner用來記錄當前持鎖的task,由于task必定是cacheline對齊的目前最小的cacheline也有16bit,所以owner的低位可以用來表示一些flag。同時可以有多個reader持鎖,因此繡著持鎖時owner就是持鎖者,但是讀者持鎖時owner有特殊表示。

    #define RWSEM_READER_OWNED (1UL << 0) bit 0 用來表示當前是reader持鎖 #define RWSEM_RD_NONSPINNABLE (1UL << 1) bit 1 表示是否允許讀者自旋等待 #define RWSEM_WR_NONSPINNABLE (1UL << 2) bit 2 表示是否允許寫者等待 #define RWSEM_NONSPINNABLE (RWSEM_RD_NONSPINNABLE | RWSEM_WR_NONSPINNABLE) #define RWSEM_OWNER_FLAGS_MASK (RWSEM_READER_OWNED | RWSEM_NONSPINNABLE)

    讀者的owner一般由rwsem_set_reader_owned設置,為 current | RWSEM_READER_OWNED | (之前owner & RWSEM_RD_NONSPINNABLE)

    寫者的owner由rwsem_set_owner設置,一般為 current

    wait_list

    等待隊列用來放置等待鎖的繼承,但是由于包含樂觀自旋邏輯,等待隊列不是全部的等待者,部分等待著可能在樂觀自旋等待owner。

    作為鏈表頭,連接所有的struct rwsem_waiter.list

    初始化

    存在兩種方式,一種是臨時定義一個棧上的變量并且初始化。

  • count初始化為0
  • owner初始化為0
  • wait_lock初始化為未加鎖狀態
  • wait_list初始化為空鏈表頭
  • magic初始化為鎖的起始地址
  • osq lock初始化為未加鎖
  • #define DECLARE_RWSEM(name) \ struct rw_semaphore name = __RWSEM_INITIALIZER(name)#define __RWSEM_INITIALIZER(name) \{ __RWSEM_COUNT_INIT(name), \.owner = ATOMIC_LONG_INIT(0), \__RWSEM_OPT_INIT(name) \.wait_lock = __RAW_SPIN_LOCK_UNLOCKED(name.wait_lock),\.wait_list = LIST_HEAD_INIT((name).wait_list), \__RWSEM_DEBUG_INIT(name) \__RWSEM_DEP_MAP_INIT(name) }count原子變量初始化為0 #define RWSEM_UNLOCKED_VALUE 0L #define __RWSEM_COUNT_INIT(name) .count = ATOMIC_LONG_INIT(RWSEM_UNLOCKED_VALUE)osq lock初始化為未加鎖 #define __RWSEM_OPT_INIT(lockname) .osq = OSQ_LOCK_UNLOCKED,magic初始化為鎖地址 # define __RWSEM_DEBUG_INIT(lockname) .magic = &lockname,

    還可以動態初始化,動態初始化的結果和靜態一致。

    #define init_rwsem(sem) \ do { \static struct lock_class_key __key; \\__init_rwsem((sem), #sem, &__key); \ } while (0)void __init_rwsem(struct rw_semaphore *sem, const char *name,struct lock_class_key *key) { #ifdef CONFIG_DEBUG_RWSEMSsem->magic = sem; #endifatomic_long_set(&sem->count, RWSEM_UNLOCKED_VALUE);raw_spin_lock_init(&sem->wait_lock);INIT_LIST_HEAD(&sem->wait_list);atomic_long_set(&sem->owner, 0L); #ifdef CONFIG_RWSEM_SPIN_ON_OWNERosq_lock_init(&sem->osq); #endiftrace_android_vh_rwsem_init(sem); }

    down_read 讀者持鎖

    讀者持鎖的判定條件較為復雜,即使有寫者持鎖時,也可能向count增加RWSEM_READER_BIAS,增加總是能成功的,但是不代表持鎖成功,增加之后一般會檢測是否有writer以及handoff,失敗需要回退,增加只是為了防止寫者搶占。讀者持鎖的條件如下:

  • 沒有讀者寫者以及等待者,即count == 0,此時向count增加RWSEM_READER_BIAS并且設置owner
  • 有其他讀者持鎖,向count增加RWSEM_READER_BIAS即算成功
  • 有等待者但是沒有寫者,偷鎖時向count增加RWSEM_READER_BIAS并且設置owner
  • 被其他人喚醒時waiter.task被清除,說明別人已經替當前進程持鎖了,當前進程只需要返回即可
  • note:

  • 讀者不管持鎖是否成功都可能會增加RWSEM_READER_BIAS一段時間,因此不能用此標志位判斷是否是讀者持鎖
  • 持鎖順序不是先來后到,樂觀自旋不管是讀者還是寫者都能通過rwsem_try_xxx_lock_unqueued無視等待隊列偷鎖
  • 隊首進程有特殊處理,如果隊首是寫者任何不會喚醒寫者(! RWSEM_WAKE_ANY)的動作都會失敗,如果隊首是讀者,喚醒讀者的時候鎖可能會被另一個寫者被偷取,此時隊首讀者如果等待超時會設置handoff不再進行競爭持鎖。
  • 讀者有兩種持鎖方式:在無人持鎖以及樂觀自旋的情況下是讀者自己獲取鎖,陷入睡眠被喚醒的情況下別人已經設置好了count和owner,自身不需要持鎖了,醒來檢測自己的waiter.task被清空了即可退出。
  • 讀者持鎖的owner:在無人持鎖以及樂觀自旋的情況下owner為第一個持鎖的讀者,如果是處于等待隊列中被其他人協助喚醒的讀者,是不會設置owner的。所以存在兩種情況:1.多個讀者,但是只有最先持鎖的讀者是owner。2.任意數目的讀者,最先持鎖的task已經釋放鎖并且清除owner,導致owner中的task為空。
  • void __sched down_read(struct rw_semaphore *sem) {//檢測是否有其他高優先級任務需要搶占,如果有讓出cpumight_sleep();//死鎖檢測rwsem_acquire_read(&sem->dep_map, 0, 0, _RET_IP_);//持鎖流程LOCK_CONTENDED(sem, __down_read_trylock, __down_read); } //如果穿入了trt_lock的接口,那么先嘗試不需要等待的try_lock,成功就直接持鎖退出 //否則調用會阻塞的lock接口 #define LOCK_CONTENDED(_lock, try, lock) \ do { \if (!try(_lock)) { \lock_contended(&(_lock)->dep_map, _RET_IP_); \lock(_lock); \} \lock_acquired(&(_lock)->dep_map, _RET_IP_); \ } while (0)簡化一下,流程為: down_read {//嘗試非阻塞持鎖if(!__down_read_trylock)//嘗試失敗,進入阻塞流程__down_read }

    __down_read_trylock

    讀者嘗試非阻塞的持鎖,僅當count == 0,也就是沒有reader也沒有writer也沒有waiter的情況下才能持鎖成功。成功之后設置自己的task | RWSEM_READER_OWNED 到owner。

    返回1表示成功,0表示失敗。

    __down_read_trylock {if(sem->count == 0)sem->count += RWSEM_READER_BIAS(1 << 8)sem->owner &= current | RWSEM_READER_OWNED | RWSEM_RD_NONSPINNABLEreturn 1return 0 } static inline int __down_read_trylock(struct rw_semaphore *sem) {long tmp;//檢測magic是否被改寫DEBUG_RWSEMS_WARN_ON(sem->magic != sem, sem);//設置tmp為鎖沒有持有者也沒有等待者的狀態的值,為0tmp = RWSEM_UNLOCKED_VALUE;do {//判斷sem->count == tmp//是,當前無人持鎖無人等待,設置sem->count = tmp + RWSEM_READER_BIAS,返回1表示更新成功//否,鎖不是空閑狀態,更新count失敗,返回0if (atomic_long_try_cmpxchg_acquire(&sem->count, &tmp,tmp + RWSEM_READER_BIAS)) {//sem->owner &= current | RWSEM_READER_OWNED | RWSEM_RD_NONSPINNABLErwsem_set_reader_owned(sem);//持鎖成功返回1return 1;}} while (!(tmp & RWSEM_READ_FAILED_MASK));//嘗試拿鎖失敗,返回0return 0; }

    __down_read

    阻塞性的等鎖直到持鎖成功。

    __down_read {rwsem_read_trylock{sem->count += RWSEM_READER_BIASreturn !(cnt & (RWSEM_WRITER_MASK|RWSEM_FLAG_WAITERS|RWSEM_FLAG_HANDOFF|RWSEM_FLAG_READFAIL))}if (!rwsem_read_trylock) {rwsem_down_read_slowpath} else {rwsem_set_reader_owned}} static inline void __down_read(struct rw_semaphore *sem) {//讀者嘗試持鎖,0表示失敗//不管失敗成功與否,都會在count中增加讀者的計數RWSEM_READER_BIAS。if (!rwsem_read_trylock(sem)) {//讀者持鎖失敗,進入慢速路徑rwsem_down_read_slowpath(sem, TASK_UNINTERRUPTIBLE);DEBUG_RWSEMS_WARN_ON(!is_rwsem_reader_owned(sem), sem);} else {//讀者持鎖成功,設置owner = current | RWSEM_READER_OWNED,并且繼承之前設置的RWSEM_RD_NONSPINNABLErwsem_set_reader_owned(sem);} }

    rwsem_read_trylock

    讀者嘗試持鎖,返回0表示失敗,1表示成功。不管失敗成功與否,都會在count中增加讀者的計數RWSEM_READER_BIAS。

    失敗的原因有:

  • 存在寫者持鎖
  • 等待隊列不為空
  • 鎖處于handoff交接狀態
  • 讀者數目溢出
  • static inline bool rwsem_read_trylock(struct rw_semaphore *sem) {//將count增加RWSEM_READER_BIAS,返回增加后的值//即讀者做一次預搶占,先設置搶占值再檢查是否能夠搶占成功long cnt = atomic_long_add_return_acquire(RWSEM_READER_BIAS, &sem->count);//數值小于0表示bit 63被設置,即讀者數目溢出了,禁止讀寫者在鎖上樂觀自旋等待if (WARN_ON_ONCE(cnt < 0))rwsem_set_nonspinnable(sem);//返回此時讀者是否能夠持有這個鎖,如果存在寫者或者存在等待者或者鎖處于特殊的handoff狀態,都會返回0,也就是失敗return !(cnt & RWSEM_READ_FAILED_MASK); }

    rwsem_down_read_slowpath

    讀者直接持鎖失敗,進入慢速路徑。

    注意,在此函數之前讀者已經將自己的標志位RWSEM_READER_BIAS加入了count。

    rwsem_down_read_slowpath {if(rwsem_can_spin_on_owner) {//判斷自身是否需要調度,持鎖者是否在運行,以及flag中有沒有明確禁止當前身份的自旋減去之前rwsem_read_trylock中加上的RWSEM_READER_BIAS (2)atomic_long_add(-RWSEM_READER_BIAS, &sem->count); //減去之前rwsem_read_trylock中加上的RWSEM_READER_BIASif(rwsem_optimistic_spin){ //樂觀自旋,自旋等待成功會設置當前task為owner,設置count對應bit等待隊列不為空?rwsem_mark_wake 喚醒等待隊列其他讀者return 持鎖成功,退出} else if (rwsem_reader_phase_trylock()) //樂觀自旋失敗,再嘗試一次持鎖return 持鎖成功,退出}樂觀自旋徹底失敗,加入等待隊列初始化waiter結構,設置超時時間如果進程沒有經歷過樂觀自旋(1)處設置的count一直在生效,會阻止寫者持鎖,當前不是寫者持鎖和handoff狀態,那讀者持鎖成功了持鎖成功,退出否則加上RWSEM_FLAG_WAITERS,將自身加入等待隊列加入等待隊列之后鎖被釋放,或者另一個讀者持鎖我們是隊首,那么rwsem_mark_wake喚醒所有讀者持鎖包括我們自身持鎖成功,退出while(1)等待waiter.task被清除} 讀者將自身加入等待隊列時,會創建喚醒隊列和等待隊列結構 struct rwsem_waiter {struct list_head list; //鏈表頭struct task_struct *task; //等待持鎖是task,被喚醒時會清除這個成員enum rwsem_waiter_type type; //表明自身是等待讀鎖還是寫鎖unsigned long timeout; //保存超時時間,只對隊首進程生效,超時之后并不會退出,而是設置handoff標志unsigned long last_rowner; //保存當前的持鎖者 }struct wake_q_head {struct wake_q_node *first; //初始化為WAKE_Q_TAILstruct wake_q_node **lastp; //初始化為自身first成員的地址 } static struct rw_semaphore __sched * rwsem_down_read_slowpath(struct rw_semaphore *sem, int state) {//adjustment用來存儲將要count增加或者減少的值//初始化為非0,在樂觀自旋邏輯中清零,這樣可以通過該值判斷進程有沒有經歷過樂觀自旋的邏輯long count, adjustment = -RWSEM_READER_BIAS;struct rwsem_waiter waiter;//初始化喚醒隊列DEFINE_WAKE_Q(wake_q);bool wake = false;//保存當前owner信息waiter.last_rowner = atomic_long_read(&sem->owner);//如果當前不是讀者持鎖if (!(waiter.last_rowner & RWSEM_READER_OWNED))//在保存的owner中加上禁止讀者自旋等待的flagwaiter.last_rowner &= RWSEM_RD_NONSPINNABLE;//當前是否允許自旋等待,不允許則加入等待隊列if (!rwsem_can_spin_on_owner(sem, RWSEM_RD_NONSPINNABLE))goto queue;//樂觀自旋等待的部分 ---------------------------------------------------------------------- (1)//減去之前rwsem_read_trylock中加上的RWSEM_READER_BIASatomic_long_add(-RWSEM_READER_BIAS, &sem->count);adjustment = 0;if (rwsem_optimistic_spin(sem, false)) {//自旋等待成功,成功設置owner為當前task,持有count//讀者持鎖成功,就可以將其他等待的讀者喚醒了if ((atomic_long_read(&sem->count) & RWSEM_FLAG_WAITERS)) {raw_spin_lock_irq(&sem->wait_lock);if (!list_empty(&sem->wait_list))//喚醒隊列中所有能喚醒的讀者rwsem_mark_wake(sem, RWSEM_WAKE_READ_OWNED, &wake_q);raw_spin_unlock_irq(&sem->wait_lock);wake_up_q(&wake_q);}return sem;//樂觀自旋失敗,進行最后一次嘗試} else if (rwsem_reader_phase_trylock(sem, waiter.last_rowner)) {return sem;}//樂觀自旋徹底失敗,加入等待隊列 ---------------------------------------------------------------------- (2) queue:waiter.task = current;waiter.type = RWSEM_WAITING_FOR_READ;waiter.timeout = jiffies + RWSEM_WAIT_TIMEOUT;raw_spin_lock_irq(&sem->wait_lock);//等待隊列為空,當前進程為隊首if (list_empty(&sem->wait_list)) {//adjustment!=0意味著沒有經歷過代碼(1)部分的樂觀自旋等待,即之前設置的count中的RWSEM_READER_BIAS沒有被減去//當前不是寫者持鎖和handoff狀態,那讀者持鎖成功了if (adjustment && !(atomic_long_read(&sem->count) &(RWSEM_WRITER_MASK | RWSEM_FLAG_HANDOFF))) {smp_acquire__after_ctrl_dep();raw_spin_unlock_irq(&sem->wait_lock);//count值前面已經增加過RWSEM_READER_BIAS了,現在不需要調整,設置owner之后就退出rwsem_set_reader_owned(sem);lockevent_inc(rwsem_rlock_fast);return sem;}//否則添加RWSEM_FLAG_WAITERS,表明等待隊列非空,當前進程不是隊首adjustment += RWSEM_FLAG_WAITERS;}//將自身加入等待隊列list_add_tail(&waiter.list, &sem->wait_list);if (adjustment)count = atomic_long_add_return(adjustment, &sem->count);else count = atomic_long_read(&sem->count);//加入等待隊列完畢 ----------------------------------------------------//加入等待隊列之后情況如果發生改變,鎖被釋放了if (!(count & RWSEM_LOCK_MASK)) {clear_wr_nonspinnable(sem);wake = true;}//或者沒有寫者持鎖并且我們是隊首//那么喚醒并且協助隊列中所有的讀者持鎖,包括我們自身//rwsem_mark_wake會清除所有被喚醒者的waiter.taskif (wake || (!(count & RWSEM_WRITER_MASK) &&(adjustment & RWSEM_FLAG_WAITERS))) //adjustment被設置了RWSEM_FLAG_WAITERS意味著我們是隊首rwsem_mark_wake(sem, RWSEM_WAKE_ANY, &wake_q);raw_spin_unlock_irq(&sem->wait_lock);wake_up_q(&wake_q);//在隊列中等待waiter.task被清除 ----------------------------------------------------------for (;;) {set_current_state(state);//當waiter.task被清除時,說明我們持鎖成功if (!smp_load_acquire(&waiter.task)) {/* Matches rwsem_mark_wake()'s smp_store_release(). */break;}//處理信號 ...schedule();lockevent_inc(rwsem_sleep_reader); }__set_current_state(TASK_RUNNING);lockevent_inc(rwsem_rlock);return sem;out_nolock:list_del(&waiter.list);if (list_empty(&sem->wait_list)) {atomic_long_andnot(RWSEM_FLAG_WAITERS|RWSEM_FLAG_HANDOFF,&sem->count);}raw_spin_unlock_irq(&sem->wait_lock);__set_current_state(TASK_RUNNING);lockevent_inc(rwsem_rlock_fail);return ERR_PTR(-EINTR); }
    rwsem_can_spin_on_owner

    檢測是否能夠樂觀自旋。

    static inline bool rwsem_can_spin_on_owner(struct rw_semaphore *sem,unsigned long nonspinnable) {struct task_struct *owner;unsigned long flags;bool ret = true;//當前任務被調度器標記為需要調度,那么不能自旋等待if (need_resched()) {return false;} ...//獲取當前owner以及flagowner = rwsem_owner_flags(sem, &flags);//如果flag中禁止了當前身份的自旋或者當前是寫者持鎖并且寫者放棄了cpu,自旋失敗if ((flags & nonspinnable) ||(owner && !(flags & RWSEM_READER_OWNED) && !owner_on_cpu(owner)))ret = false; ...return ret; }
    rwsem_optimistic_spin

    在owner上樂觀自旋,第一步通過樂觀自旋獲取sem->osq,只有持有這個鎖才能在sem->owner上自旋等待。返回自旋等待是否成功,如果是被迫退出自旋返回0.

    自旋等待成功會設置當前task為owner。

    rwsem_optimistic_spin {osq_lock 獲取樂觀自旋所osq失敗,退出while(1) {owner_state = rwsem_spin_on_owner 更新當前owner狀態OWNER_SPINNABLE? -> breakrwsem_try_xxx_lock_unqueued 調用不加入隊列嘗試獲取鎖的函數,此函數能夠搶占等待隊列的持鎖機會reader只檢測是否存在writer持鎖和handoffwriter只檢測鎖是否被人持有和handoff,兩者均不關心waiter成功 -> break寫者等待讀者持鎖的情況,讀者太多不能確定每個讀者是否都在線每16次循環檢測超時時間超時 -> breakRT任務不能被阻塞,讀者持鎖情況向讀者太多不能確定每個讀者是否都在線兩次機會獲取鎖或者帶寫者持鎖開始spin}} @sem 指向的指針鎖 @wlock 決定是否是寫者函數中使用owner_state表示當前owner的狀態 enum owner_state {OWNER_NULL = 1 owner為NULLOWNER_WRITER = 2 owner為寫者OWNER_READER = 4 owner為讀者OWNER_NONSPINNABLE = 8 當前不允許在owner上spin } #define OWNER_SPINNABLE (OWNER_NULL | OWNER_WRITER | OWNER_READER)static bool rwsem_optimistic_spin(struct rw_semaphore *sem, bool wlock) {bool taken = false;int prev_owner_state = OWNER_NULL;int loop = 0;u64 rspin_threshold = 0; unsigned long nonspinnable = wlock ? RWSEM_WR_NONSPINNABLE: RWSEM_RD_NONSPINNABLE;preempt_disable();/* 先獲取osqlock,osqlock也是一種樂觀自旋機制,在soqlock上自旋失敗,則不嘗試在owner上自旋等待,直接退出 */if (!osq_lock(&sem->osq))goto done;//由于到達此步之前需要持有osq,因此即使有多個讀寫者搶鎖也只能有一個自旋等待owner//樂觀自旋等待owner發生變化時持鎖for (;;) {enum owner_state owner_state;//自旋等待owner發生改變,返回改變之后的stateowner_state = rwsem_spin_on_owner(sem, nonspinnable);//除OWNER_NONSPINNABLE之外都是允許繼續spin的if (!(owner_state & OWNER_SPINNABLE))break;//根據身份調用不加入隊列嘗試獲取鎖的函數,不加入等待隊列意味著有可能搶占等待隊列進程的持鎖機會//對于寫者而言,等待count中既沒有讀者持鎖也沒有寫者持鎖,也不是handoff狀態//對于讀者而言,等待count中既沒有沒有寫者持鎖,也不是handoff狀態//成功返回1,失敗返回0taken = wlock ? rwsem_try_write_lock_unqueued(sem): rwsem_try_read_lock_unqueued(sem);//持鎖成功,退出循環if (taken)break;//其他的讀者可以直接持鎖,寫者需要等待所有讀者釋放鎖才能持鎖,寫者無法弄清楚是否所有讀者都在線//所以如果當前是寫者等鎖,并且讀者持有鎖,給自旋等待的時間加個限制if (wlock && (owner_state == OWNER_READER)) {//prev_owner_state初始化為OWNER_NULL,第一次此條件必定成立if (prev_owner_state != OWNER_READER) {//不允許寫者在owner上spin,那么退出if (rwsem_test_oflags(sem, nonspinnable))break;//計算寫者的最大等待時間rspin_threshold = rwsem_rspin_threshold(sem);//循環次數清零loop = 0;并且已經超過了該讀者的預定等}//每16次循環計算當前等待是否超時,如果超時設置RWSEM_NONSPINNABLE禁止所有的在owner上的spinelse if (!(++loop & 0xf) && (sched_clock() > rspin_threshold)) {rwsem_set_nonspinnable(sem);lockevent_inc(rwsem_opt_nospin);break;}}//讀者可能有很多,RT任務同樣不能弄清楚每個讀者的狀態//上次不是寫者持鎖,則RT任務不再等待,讀者一直持鎖RT任務將有兩次等待機會if (owner_state != OWNER_WRITER) {//調度器認為我們需要讓出cpu,那么退出等待if (need_resched())break;//讀者持鎖的情況向RT任務有兩次機會spinif (rt_task(current) &&(prev_owner_state != OWNER_WRITER))break;}//記錄這次循環的持鎖者,下次循環時會用到prev_owner_state = owner_state;cpu_relax();}osq_unlock(&sem->osq); done:preempt_enable();lockevent_cond_inc(rwsem_opt_fail, !taken);return taken; }

    rwsem_spin_on_owner

    ? 自旋等待owner或者flag發生改變,返回新的state

    static noinline enum owner_state rwsem_spin_on_owner(struct rw_semaphore *sem, unsigned long nonspinnable) {struct task_struct *new, *owner;unsigned long flags, new_flags;enum owner_state state; //獲取ownerowner = rwsem_owner_flags(sem, &flags);/* 獲取onwer的狀態,這里有4種情況* 1.owner中存在禁止當前身份spin的flag,比如當前是讀者owner中存在RWSEM_RD_NONSPINNABLE,返回 OWNER_NONSPINNABLE* 2.當前是讀者持鎖,返回 OWNER_READER* 3.在等待的過程中所已經被釋放了,返回 OWNER_NULL* 4.寫者持鎖,OWNER_WRITER */state = rwsem_owner_state(owner, flags, nonspinnable);//當前不是寫者持鎖,停止等待owner//因為讀者持鎖時間可能很長,OWNER_NONSPINNABLE意味著禁止自旋等待if (state != OWNER_WRITER)return state;rcu_read_lock();for (;;) {//讀取新的flag和ownernew = rwsem_owner_flags(sem, &new_flags);//如果owner或者flag發生了改變if ((new != owner) || (new_flags != flags)) {//重新獲取stat,終止循環state = rwsem_owner_state(new, new_flags, nonspinnable);break;} ...//如果自選條件不滿足if (need_resched() || !owner_on_cpu(owner)) {state = OWNER_NONSPINNABLE;break;}cpu_relax();}rcu_read_unlock();return state; }
    rwsem_try_xxx_lock_unqueued

    不加入等待隊列的嘗試持鎖,這此持鎖沒有判斷等待隊列是否有等待者,可能會搶占等待隊列中進程的持鎖機會,所以需要進行handoff的判斷,避免等待著被強占次數過多餓死。

    static inline bool rwsem_try_write_lock_unqueued(struct rw_semaphore *sem) {long count = atomic_long_read(&sem->count);//寫者等待無人持鎖以及非handoff狀態,之后嘗試持鎖//使用循環是為了給寫者更多的持鎖機會,讀者持鎖就只有一次判斷while (!(count & (RWSEM_LOCK_MASK|RWSEM_FLAG_HANDOFF))) {if (atomic_long_try_cmpxchg_acquire(&sem->count, &count,count | RWSEM_WRITER_LOCKED)) {rwsem_set_owner(sem);lockevent_inc(rwsem_opt_wlock);return true;}}return false; }

    前面讀者實際上是有非阻塞持鎖的函數的,rwsem_read_trylock,與此函數區別在于是否判斷waiter。

    static inline bool rwsem_try_read_lock_unqueued(struct rw_semaphore *sem) {long count = atomic_long_read(&sem->count);//判斷是否有寫者持鎖以及是否在handoff狀態,讀者持鎖不影響另一個讀者if (count & (RWSEM_WRITER_MASK | RWSEM_FLAG_HANDOFF))return false;//不在的話嘗試持鎖,將count + RWSEM_READER_BIAScount = atomic_long_fetch_add_acquire(RWSEM_READER_BIAS, &sem->count);//成功則開始設置ownerif (!(count & (RWSEM_WRITER_MASK | RWSEM_FLAG_HANDOFF))) {rwsem_set_reader_owned(sem);lockevent_inc(rwsem_opt_rlock);return true;}//失敗則回退自己設置的RWSEM_READER_BIASatomic_long_add(-RWSEM_READER_BIAS, &sem->count);return false; }
    rwsem_mark_wake

    喚醒等待隊列的進程。

    如果隊首是寫者但是喚醒類型不是RWSEM_WAKE_ANY,那么喚醒不了任何進程。

    隊首是讀者,當前不是讀者持鎖喚醒其他讀者,那么幫助這個讀者提前占據鎖。搶鎖失敗并且超時,就設置handoff位。

    @wake_type 需要喚醒對象的類型 enum rwsem_wake_type {RWSEM_WAKE_ANY, /* 喚醒隊列的第一個等待者 */RWSEM_WAKE_READERS, /* 只喚醒讀者 */RWSEM_WAKE_READ_OWNED /* 讀者持鎖喚醒其他讀者 */ }; @wake_q 喚醒隊列static void rwsem_mark_wake(struct rw_semaphore *sem, enum rwsem_wake_type wake_type,struct wake_q_head *wake_q) {struct rwsem_waiter *waiter, *tmp;long oldcount, woken = 0, adjustment = 0;struct list_head wlist;lockdep_assert_held(&sem->wait_lock);//如果第一個等待者是寫者,并且喚醒對象是任意隊首等待者,那么不喚醒所有進程進行競爭,只喚醒這個寫者//如果第一個等待者是寫者,但是喚醒對象是其他的,比如讀者持鎖喚醒其他讀者或者僅限于喚醒讀者的情況//直接返回不進行喚醒動作,因為第一個等待的寫者應該最先被喚醒waiter = rwsem_first_waiter(sem);if (waiter->type == RWSEM_WAITING_FOR_WRITE) {if (wake_type == RWSEM_WAKE_ANY) {wake_q_add(wake_q, waiter->task);lockevent_inc(rwsem_wake_writer);}return;}//此處之后處理隊首是讀者的情況 -------------------------------------------------------------------------//讀者計數溢出處理if (unlikely(atomic_long_read(&sem->count) < 0))return;//不是讀者持鎖喚醒其他讀者的情況,說明當前鎖是未被人持有的狀態,需要搶鎖if (wake_type != RWSEM_WAKE_READ_OWNED) {struct task_struct *owner;//先構造一個虛假的讀者持鎖,幫助這個隊首的讀者提前獲取鎖adjustment = RWSEM_READER_BIAS;oldcount = atomic_long_fetch_add(adjustment, &sem->count);//搶鎖失敗了,寫者偷到了鎖,隊首的讀者錯過了一次持鎖機會,并且已經超過了該讀者的預定等待時間//現在設置handoff,屏蔽所有競爭鎖以及竊取鎖的動作,讓下個持鎖者直接把鎖交給隊首的讀者,退出if (unlikely(oldcount & RWSEM_WRITER_MASK)) {if (!(oldcount & RWSEM_FLAG_HANDOFF) &&time_after(jiffies, waiter->timeout)) {adjustment -= RWSEM_FLAG_HANDOFF;lockevent_inc(rwsem_rlock_handoff);}atomic_long_add(-adjustment, &sem->count); return;}//幫助隊首的讀者搶鎖成功了,設置隊首進程為新的ownerowner = waiter->task;if (waiter->last_rowner & RWSEM_RD_NONSPINNABLE) {owner = (void *)((unsigned long)owner | RWSEM_RD_NONSPINNABLE);lockevent_inc(rwsem_opt_norspin);}__rwsem_set_reader_owned(sem, owner);}/* 這里之后,隊首是讀者,持鎖者也是讀者,需要做的是喚醒隊列中的其他讀者 1.讀者持鎖喚醒其他讀者, 2.幫助隊首的讀者獲取了鎖,現在需要繼續喚醒剩余的讀者*/ -------------------------------------------------------------------//現在,持鎖者是讀者了,我們可以喚醒隊列中至多MAX_READERS_WAKEUP個其他的讀者,一般是1600個INIT_LIST_HEAD(&wlist);list_for_each_entry_safe(waiter, tmp, &sem->wait_list, list) {if (waiter->type == RWSEM_WAITING_FOR_WRITE)continue;woken++;list_move_tail(&waiter->list, &wlist);if (woken >= MAX_READERS_WAKEUP)break;}//增加需要喚醒的讀者的計數,如果等待隊列為空需要移除掉waiter標志adjustment = woken * RWSEM_READER_BIAS - adjustment;lockevent_cond_inc(rwsem_wake_reader, woken);if (list_empty(&sem->wait_list)) {adjustment -= RWSEM_FLAG_WAITERS;}//handoff只作用于隊首進程,現在我們已經協助隊首拿到鎖了,清除它if (woken && (atomic_long_read(&sem->count) & RWSEM_FLAG_HANDOFF))adjustment -= RWSEM_FLAG_HANDOFF;//將增加的讀者數目寫入countif (adjustment)atomic_long_add(adjustment, &sem->count);/* 將需要喚醒的進程放入wake_q */list_for_each_entry_safe(waiter, tmp, &wlist, list) {struct task_struct *tsk;tsk = waiter->task;get_task_struct(tsk);//喚醒之前清除waiter->tasksmp_store_release(&waiter->task, NULL);wake_q_add_safe(wake_q, tsk);} }

    up_read 讀者釋放鎖

    如果owner是當前task,清除掉owner中task_struct部分,保留flag部分。

    將count減去RWSEM_READER_BIAS,即減少一名讀者計數。

    若無人持鎖并且等待隊列不為空,調用rwsem_wake喚醒等待隊列進程,最終盜用的是rwsem_mark_wake,喚醒類型為RWSEM_WAKE_ANY。

    void up_read(struct rw_semaphore *sem) {rwsem_release(&sem->dep_map, _RET_IP_);__up_read(sem); }static inline void __up_read(struct rw_semaphore *sem) {long tmp;DEBUG_RWSEMS_WARN_ON(sem->magic != sem, sem);DEBUG_RWSEMS_WARN_ON(!is_rwsem_reader_owned(sem), sem);//onwer是當前進程,將flag中的task_struct清除,保留flagrwsem_clear_reader_owned(sem);//減少讀者數目tmp = atomic_long_add_return_release(-RWSEM_READER_BIAS, &sem->count);DEBUG_RWSEMS_WARN_ON(tmp < 0, sem);//當前無人持鎖if (unlikely((tmp & (RWSEM_LOCK_MASK|RWSEM_FLAG_WAITERS)) ==RWSEM_FLAG_WAITERS)) {clear_wr_nonspinnable(sem);//喚醒等待隊列rwsem_wake(sem, tmp);} }

    down_write 寫者持鎖

    寫者和其他寫著以及讀者互斥,因此需要沒有任何人持鎖的情況下才能獲取鎖。

    寫者持鎖的條件比讀者簡單,只需要設置count的RWSEM_WRITER_LOCKED bit就算持鎖成功。

    讀者會有其他人協助幫忙持鎖,自身被喚醒只用檢查waiter.task是否被清空,寫者無人協助,被喚醒之后會自己去檢查鎖的狀態,然后拿鎖。

    挾制持鎖,owner必定是自身。

    down_write 和讀者持鎖流程一樣,都是先嘗試__down_write_trylock失敗則調用__down_write。

    偽代碼為: if !__down_write_trylock__down_writevoid __sched down_write(struct rw_semaphore *sem) {might_sleep();rwsem_acquire(&sem->dep_map, 0, 0, _RET_IP_);LOCK_CONTENDED(sem, __down_write_trylock, __down_write); }

    __down_write_trylock

    寫者嘗試非阻塞持鎖。僅當count == 0時,將其設置為RWSEM_WRITER_LOCKED,并且設置owner為當前task。

    static inline int __down_write_trylock(struct rw_semaphore *sem) {long tmp;DEBUG_RWSEMS_WARN_ON(sem->magic != sem, sem);tmp = RWSEM_UNLOCKED_VALUE;//如果count == 0,那么設置count = RWSEM_WRITER_LOCKED,即表明寫者持鎖if (atomic_long_try_cmpxchg_acquire(&sem->count, &tmp,RWSEM_WRITER_LOCKED)) { //設置owner = current rwsem_set_owner(sem);return true;}return false; }

    __down_write

    static inline void __down_write(struct rw_semaphore *sem) {long tmp = RWSEM_UNLOCKED_VALUE;//再嘗試一次__down_write_trylock的流程if (unlikely(!atomic_long_try_cmpxchg_acquire(&sem->count, &tmp, RWSEM_WRITER_LOCKED)))//失敗則進入慢速路徑rwsem_down_write_slowpath(sem, TASK_UNINTERRUPTIBLE);elserwsem_set_owner(sem); }

    rwsem_down_write_slowpath

    樂觀自旋持鎖以及加入等待隊列死等的慢速路徑。

    rwsem_down_write_slowpath {和讀者類似的樂觀自旋嘗試加入等待隊列不是隊首,寫者持鎖什么都不干,讀者持鎖喚醒隊列其他讀者,無人持鎖喚醒隊列隊首while(1) {rwsem_try_write_lock 嘗試持鎖,當前隊首進程等待超時設置handoff當前進程設置了handoff,那么嘗試在owner上樂觀自旋} } wstate 用來表明當前寫著的狀態 WRITER_FIRST 當前是等待隊列的隊首 WRITER_NOT_FIRST 當前不是隊首 WRITER_HANDOFF 當前進程已經等待超時或者當前時RT進程,允許在count中設置handoffstatic struct rw_semaphore * rwsem_down_write_slowpath(struct rw_semaphore *sem, int state) {long count;bool disable_rspin;enum writer_wait_state wstate;struct rwsem_waiter waiter;struct rw_semaphore *ret = sem;DEFINE_WAKE_Q(wake_q);//嘗試樂觀自旋偷鎖//和前面讀者流程一致,不過是以寫者身份spin,等待count中沒有寫者持鎖,讀者持鎖,handoff標記if (rwsem_can_spin_on_owner(sem, RWSEM_WR_NONSPINNABLE) &&rwsem_optimistic_spin(sem, true)) {/* rwsem_optimistic_spin() implies ACQUIRE on success */return sem;}//獲取禁止spin的flagdisable_rspin = atomic_long_read(&sem->owner) & RWSEM_NONSPINNABLE;//初始化waiter,準備加入等待隊列waiter.task = current;waiter.type = RWSEM_WAITING_FOR_WRITE;waiter.timeout = jiffies + RWSEM_WAIT_TIMEOUT;raw_spin_lock_irq(&sem->wait_lock); //當前進程是等待隊列的隊首嗎?wstate = list_empty(&sem->wait_list) ? WRITER_FIRST : WRITER_NOT_FIRST;list_add_tail(&waiter.list, &sem->wait_list);//如果我們不是隊首,需要處理下等待隊列if (wstate == WRITER_NOT_FIRST) {count = atomic_long_read(&sem->count);//當前是寫者持鎖,沒什么好做的慢慢等待即可if (count & RWSEM_WRITER_MASK)goto wait;//當前是讀者持鎖,嘗試使用RWSEM_WAKE_READERS喚醒隊列內的其他讀者持鎖//當前無人持鎖,RWSEM_WAKE_READERS喚醒隊列的隊首rwsem_mark_wake(sem, (count & RWSEM_READER_MASK)? RWSEM_WAKE_READERS: RWSEM_WAKE_ANY, &wake_q);//喚醒進程if (!wake_q_empty(&wake_q)) { raw_spin_unlock_irq(&sem->wait_lock);wake_up_q(&wake_q);wake_q_init(&wake_q); /* Used again, reinit */raw_spin_lock_irq(&sem->wait_lock);}} else {//我們是隊首,增加RWSEM_FLAG_WAITERS標志atomic_long_or(RWSEM_FLAG_WAITERS, &sem->count);}wait:/* 死循環等鎖 */set_current_state(state);for (;;) {//以寫者的身份嘗試獲取鎖,成功返回1if (rwsem_try_write_lock(sem, wstate)) {break;}raw_spin_unlock_irq(&sem->wait_lock);//如果當前進程允許設置handoff還是持鎖失敗,那么嘗試在owner上自旋等待owner為NULL,避免睡眠//因為設置了handoff之后應該不會再有競爭者if (wstate == WRITER_HANDOFF &&rwsem_spin_on_owner(sem, RWSEM_NONSPINNABLE) == OWNER_NULL)goto trylock_again;/* Block until there are no active lockers. */for (;;) {...//讓出cpu,等待喚醒schedule();//喚醒之后的節點,更新自身以及鎖的狀態,發生改變就進入外層循環再次嘗試持鎖,否則回來繼續睡眠 ------------------------------------------------------//當前進程能夠設置handoff狀態,馬上退出內存循環到外層循環rwsem_try_write_lock給count設置handoffif (wstate == WRITER_HANDOFF)break;//當前不是隊首進程,更新下自身狀態看下是否已經成為新的隊首if ((wstate == WRITER_NOT_FIRST) &&(rwsem_first_waiter(sem) == &waiter))wstate = WRITER_FIRST;//更新鎖的狀態,如果無人持鎖馬上去外層循環嘗試持鎖count = atomic_long_read(&sem->count);if (!(count & RWSEM_LOCK_MASK))break;//當前是隊首并且是RT進程或者等待超時,那么允許設置handoff狀態if ((wstate == WRITER_FIRST) && (rt_task(current) ||time_after(jiffies, waiter.timeout))) {wstate = WRITER_HANDOFF;lockevent_inc(rwsem_wlock_handoff);break;}} trylock_again:raw_spin_lock_irq(&sem->wait_lock);}//持鎖成功,恢復進程狀態,將自身從等待隊列刪除__set_current_state(TASK_RUNNING);list_del(&waiter.list);rwsem_disable_reader_optspin(sem, disable_rspin);raw_spin_unlock_irq(&sem->wait_lock);lockevent_inc(rwsem_wlock);return ret;out_nolock:__set_current_state(TASK_RUNNING);raw_spin_lock_irq(&sem->wait_lock); list_del(&waiter.list);if (unlikely(wstate == WRITER_HANDOFF))atomic_long_add(-RWSEM_FLAG_HANDOFF, &sem->count);if (list_empty(&sem->wait_list))atomic_long_andnot(RWSEM_FLAG_WAITERS, &sem->count);elserwsem_mark_wake(sem, RWSEM_WAKE_ANY, &wake_q);raw_spin_unlock_irq(&sem->wait_lock);wake_up_q(&wake_q);lockevent_inc(rwsem_wlock_fail);return ERR_PTR(-EINTR); }

    rwsem_try_write_lock

    寫者嘗試獲取鎖,主要用來處理寫者的handoff的標記。

  • 存在count handoff標記當前進程不是隊首的情況下會直接失敗。
  • 有人持鎖,根據wstate選擇是否設置handoff,然后返回
  • 無人持鎖,清除handoff,然后持鎖
  • @wstate 用來表明當前寫著的狀態 WRITER_FIRST 當前是等待隊列的隊首 WRITER_NOT_FIRST 當前不是隊首 WRITER_HANDOFF 當前進程已經等待超時或者當前時RT進程,允許在count中設置handoff static inline bool rwsem_try_write_lock(struct rw_semaphore *sem,enum writer_wait_state wstate) {long count, new;lockdep_assert_held(&sem->wait_lock);//讀取新的countcount = atomic_long_read(&sem->count);do {//count中是否被設置了handoff bit?bool has_handoff = !!(count & RWSEM_FLAG_HANDOFF);//如果存在handoff那意味著鎖會被交接給隊首,當前如果不是隊首,不可能獲取到鎖,因此直接返回失敗if (has_handoff && wstate == WRITER_NOT_FIRST)return false;new = count;//當前仍然有人持鎖if (count & RWSEM_LOCK_MASK) {//存在handoff或者當前進程暫時不能設置handoff,返回失敗if (has_handoff || (wstate != WRITER_HANDOFF))return false;//我們可以設置handoff,加上handoff標記new |= RWSEM_FLAG_HANDOFF;} else {//無人持鎖了,嘗試持鎖new |= RWSEM_WRITER_LOCKED;new &= ~RWSEM_FLAG_HANDOFF;//持鎖成功如果等待隊列為空,需要刪除RWSEM_FLAG_WAITERSif (list_is_singular(&sem->wait_list))new &= ~RWSEM_FLAG_WAITERS;}//如果這期間count沒有發生變化,那么將sem->count設置為new,返回更新是否成功,count被更新為sem->count} while (!atomic_long_try_cmpxchg_acquire(&sem->count, &count, new));if (new & RWSEM_FLAG_HANDOFF)return false;//設置owner == currentrwsem_set_owner(sem);return true; }

    總結

    以上是生活随笔為你收集整理的rw_semaphore 原理与代码分析的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    日韩久久激情 | 国产精品久久三 | 黄色小网站在线观看 | 超碰在线中文字幕 | 麻豆传媒一区二区 | 国产爽妇网 | 国产+日韩欧美 | 福利视频导航网址 | 久久精品欧美一区二区三区麻豆 | 色网免费观看 | 视频在线观看91 | 成人午夜电影在线观看 | 在线免费观看羞羞视频 | 欧美色噜噜噜 | 免费 在线 中文 日本 | 中文字幕黄色网址 | 久久久国产毛片 | 国产精品综合av一区二区国产馆 | 激情小说网站亚洲综合网 | 99精品欧美一区二区三区黑人哦 | 色在线免费 | 久久999久久 | 青青草视频精品 | 亚洲精品黄色片 | 日韩欧美一级二级 | 国产不卡精品 | 中文字幕乱码日本亚洲一区二区 | 日韩久久在线 | 免费高清影视 | 中文字幕在线日亚洲9 | 日韩专区在线 | 狠狠躁夜夜a产精品视频 | 欧美日韩视频精品 | 在线网址你懂得 | 高清不卡毛片 | av网站手机在线观看 | 久热精品国产 | 波多野结衣在线播放一区 | 欧美一级视频一区 | 在线观看中文 | 国产美女视频网站 | 日韩在线第一 | 国产五码一区 | 在线观看免费91 | 成人黄色片免费 | 亚洲色图27p | 天天干天天怕 | 91亚洲网站 | 精品国产一区二区三区免费 | 国产区免费 | 久久久久 免费视频 | 夜夜躁日日躁狠狠久久88av | 福利电影久久 | 久久精品香蕉视频 | 日本一区二区高清不卡 | 亚洲日韩欧美一区二区在线 | 国产男女爽爽爽免费视频 | 久久视频在线观看中文字幕 | 人人狠狠综合久久亚洲婷 | 三级黄色欧美 | 亚洲欧洲成人 | 九草视频在线 | 天天爽天天爽天天爽 | 永久免费av在线播放 | 人人爽人人射 | 国产破处在线播放 | 一区二区不卡在线观看 | avwww在线 | 最近能播放的中文字幕 | 在线观看黄色的网站 | 国产精品美女久久久久久免费 | 欧美久久精品 | 日韩久久精品一区二区 | 最近高清中文在线字幕在线观看 | 久草电影免费在线观看 | 狠狠狠狠狠狠狠干 | 久久国产精品影视 | 在线观看国产成人av片 | 91在线91| 国产在线综合视频 | 欧美日韩视频网站 | 99视频在线 | 亚洲国产日韩一区 | 国产一线二线三线性视频 | 日韩欧美大片免费观看 | 国产精品久久精品国产 | 国内精品久久久久久久影视麻豆 | 日韩欧美在线观看 | 免费网站色 | 久久伦理网| 欧美在线一二 | 久久久人人人 | 日韩有码欧美 | 99久久www| 国产精品久久久久av免费 | 中文在线免费看视频 | 黄色av网站在线免费观看 | 99视频免费播放 | 在线看一区| 黄色av一区二区 | av免费在线看网站 | 精品久操| 免费在线一区二区 | avhd高清在线谜片 | 国产韩国日本高清视频 | 黄av在线 | 婷婷六月综合网 | 最新日韩视频在线观看 | 97精品国产97久久久久久 | 免费能看的av | 亚洲 综合 国产 精品 | 偷拍区另类综合在线 | 亚洲精品欧洲精品 | 久久视频在线观看中文字幕 | 97精品视频在线播放 | 99视频精品| 天天操夜夜干 | 国产婷婷久久 | 欧美一级视频免费 | 97色婷婷人人爽人人 | 久久综合干 | 国产成人一区在线 | 国产美女精品人人做人人爽 | 久久看片网站 | 亚洲 欧美 另类人妖 | 夜夜操天天干 | 婷婷丁香久久五月婷婷 | 奇米影视8888 | 国产男女免费完整视频 | 国产91在线看 | 99这里都是精品 | 欧美一区影院 | 黄色成人av在线 | 久久精品五月 | 99这里只有精品视频 | 午夜精品久久久久久99热明星 | 国产精品中文字幕在线 | 黄色免费网站 | 国产精品99精品久久免费 | 国产精品成人在线 | 午夜精品一区二区三区在线播放 | 色综合咪咪久久网 | 在线观看av麻豆 | 国产精品video爽爽爽爽 | 欧美作爱视频 | 国产特黄色片 | 少妇精品久久久一区二区免费 | 麻豆久久精品 | 伊色综合久久之综合久久 | 亚洲精品美女在线 | 日韩 在线a | 国产涩涩在线观看 | 亚洲综合成人婷婷小说 | 91视频免费国产 | 婷婷.com| 国产伦精品一区二区三区在线 | 久久国产高清 | 97视频网址 | 99热官网 | 亚洲精品影视在线观看 | 欧美精品乱码99久久影院 | 91成人短视频在线观看 | 日日夜夜国产 | 91亚瑟视频 | 激情综合网色播五月 | 91精品久久久久久综合乱菊 | 综合国产在线 | 日韩激情综合 | 欧美性色综合网 | 日韩一二区在线观看 | 日操干| 少妇bbw撒尿 | 久久a v电影| 9久久精品 | 久久艹艹 | 久草在线久草在线2 | 亚洲精品97| 久章操| 久久综合影音 | 亚洲免费婷婷 | 亚洲春色综合另类校园电影 | 日韩欧美精选 | 久久五月天色综合 | 色资源网免费观看视频 | 91精品国产三级a在线观看 | 色97在线| 国产福利专区 | 激情小说网站亚洲综合网 | 日韩av片免费在线观看 | 亚洲精品综合一二三区在线观看 | 9i看片成人免费看片 | 999国产在线 | 国产精品视频免费 | 在线v片免费观看视频 | 五月激情婷婷丁香 | 日本夜夜草视频网站 | 国产一区在线视频播放 | 精品国产精品国产偷麻豆 | 在线 你懂 | 日韩一区精品 | 一区二区中文字幕在线观看 | 亚洲午夜精品久久久久久久久 | 日韩成人免费在线 | 久草在线在线视频 | 国产美女搞久久 | 欧美日韩aaaa| 免费在线一区二区三区 | 色干综合 | 黄色小说网站在线 | 亚洲伊人成综合网 | 欧美日韩另类在线观看 | 日韩色av色资源 | 五月天天天操 | 免费观看黄 | 中文字幕一区二区在线观看 | 人人超碰97 | 91aaa在线观看 | 97在线播放 | 国产视频欧美视频 | 成人综合婷婷国产精品久久免费 | 日韩精品视频免费在线观看 | 在线观看视频免费大全 | 欧美日产一区 | 免费黄色av. | 天天爽天天碰狠狠添 | 日韩久久视频 | 丁香五香天综合情 | 黄污网站在线 | 婷婷99| 99 色 | 国产在线精品一区 | 国产亚洲va综合人人澡精品 | 亚洲成人av影片 | 久草在线电影网 | 成年人免费在线观看网站 | 日韩www在线 | 99re8这里有精品热视频免费 | 91中文字幕永久在线 | 99热最新地址 | 日韩免费成人av | 综合国产在线 | 久热免费在线观看 | 一本一本久久aa综合精品 | 欧美精品v国产精品 | 91av蜜桃| 久久免费av | 夜夜夜草| 日韩在线电影一区二区 | 在线视频 成人 | 国产精品成人a免费观看 | 亚洲免费视频在线观看 | 国产精品久久久久久久久大全 | 欧美日韩在线第一页 | 日韩在线视频精品 | 最新久久免费视频 | 天天操夜夜操国产精品 | 六月丁香婷 | 在线视频 影院 | 91经典在线 | wwwwww黄 | 毛片网站免费在线观看 | 久久精品国产成人精品 | 国产资源站| 色婷婷亚洲精品 | 五月天狠狠操 | 91桃色免费视频 | 亚洲 成人 欧美 | 又黄又网站 | 波多野结衣理论片 | 国产成人精品区 | 欧美a级一区二区 | 日韩视频免费观看高清完整版在线 | 国产精品久久久久久久久岛 | 亚洲一区二区三区四区在线视频 | 久久精品www人人爽人人 | 成人一级片在线观看 | 天天综合成人 | 久久精品99久久久久久 | 精品av网站 | 久久午夜精品影院一区 | 亚洲一级在线观看 | 精品黄色片| 中文视频在线 | 久久精品人 | av资源中文字幕 | 成人国产网站 | 久草视频在线资源站 | 91在线入口 | 久久艹艹| 国产在线精品一区二区不卡了 | 在线免费观看一区二区三区 | 久久精品播放 | 欧美巨乳网| 亚洲国产三级 | 日日干夜夜草 | 激情视频一区二区三区 | 成年人免费观看国产 | 国产黄a三级 | 97色噜噜 | 五月天色网站 | 丁香 婷婷 激情 | 日韩三级免费 | 一区二区三区在线观看 | 制服丝袜天堂 | 午夜精品视频一区二区三区在线看 | 亚洲精品电影在线 | 久久激情小说 | 色com网| 亚洲国产精品va在线看黑人动漫 | 丁香午夜| 国产视频久| 日韩精品欧美视频 | av免费高清观看 | 成人av免费播放 | 久保带人| 91看毛片 | 国产一区二区在线观看视频 | 亚洲国产精品久久 | 91精品国产综合久久福利不卡 | 日韩激情片在线观看 | 69精品久久 | 五月婷婷丁香 | 国产一区二区三区免费在线 | 日本中文字幕电影在线免费观看 | 91麻豆文化传媒在线观看 | 久久香蕉国产 | 天天操夜夜叫 | sm免费xx网站 | 国产成人免费高清 | 国产精品免费看久久久8精臀av | 国产一区二区不卡视频 | 在线免费观看涩涩 | 波多野结衣电影一区 | 午夜性盈盈 | 欧美午夜精品久久久久 | 成人a v视频 | 欧美精品亚洲精品日韩精品 | 久久伊人综合 | 日韩大片在线免费观看 | 久久免费试看 | 成人免费网视频 | 九色琪琪久久综合网天天 | 久久久999免费视频 日韩网站在线 | 免费福利视频导航 | 91传媒激情理伦片 | 91在线蜜桃臀 | 日本激情视频中文字幕 | 日韩精品免费在线播放 | 欧美色噜噜噜 | 亚洲欧洲久久久 | 亚洲va综合va国产va中文 | 国产 成人 久久 | 日韩欧美精品在线 | 操老逼免费视频 | 天天插伊人 | 欧美日韩69 | 亚洲欧洲日韩在线观看 | 日韩在线观看一区二区三区 | 久久久国产毛片 | 日日麻批40分钟视频免费观看 | 国产黄大片 | 伊人激情综合 | 日本黄色片一区二区 | 国产成人精品在线 | 天天草视频 | 久久99视频免费 | 国产日产精品一区二区三区四区的观看方式 | 波多野结衣视频一区二区三区 | 2020天天干天天操 | 国产成人一区三区 | 午夜精品一区二区三区可下载 | 亚洲国产日本 | 中文字幕高清有码 | 99久久婷婷国产一区二区三区 | 久久www免费人成看片高清 | 综合色综合 | 中文字幕乱码日本亚洲一区二区 | 成人免费在线视频 | 99精品视频一区二区 | 成人久久18免费网站 | www.色午夜 | 91丨九色丨蝌蚪丰满 | 国产精品国产三级国产专区53 | a级片在线播放 | 久久国产精品免费视频 | 日韩久久在线 | 五月天激情综合网 | 在线观看视频99 | 日本中文字幕在线播放 | 亚洲电影影音先锋 | 不卡的av在线播放 | 久久久久久国产精品999 | 国产系列精品av | 十八岁以下禁止观看的1000个网站 | 亚洲国产成人精品在线 | 亚洲精品三级 | 久久国产精品99久久久久久老狼 | 日本在线观看中文字幕无线观看 | 日日夜夜精品免费观看 | 97国产精品亚洲精品 | 日韩av资源站 | 黄色网大全 | 亚洲精品免费播放 | 狠狠久久综合 | 日本在线观看中文字幕 | 青青河边草免费观看 | 五月天激情在线 | 久久国产精品99国产精 | 黄色国产精品 | 伊人资源站 | 中文字幕在线免费观看 | 精品9999 | 天天干国产 | 99re8这里有精品热视频免费 | 蜜臀精品久久久久久蜜臀 | 一级黄色片在线免费看 | 日本爱爱免费视频 | 欧美另类成人 | 99热精品国产一区二区在线观看 | 在线免费看黄网站 | 免费高清影视 | 色鬼综合网 | 色偷偷中文字幕 | 国产人成在线视频 | 日韩91精品| 日韩av一区在线观看 | 色欧美日韩| 成人一区二区三区在线 | 午夜精品成人一区二区三区 | 日韩欧美在线第一页 | 日韩高清免费观看 | 99草视频 | 欧美在线视频a | 久草在线免费新视频 | 国产999精品| 性色av免费看 | 亚洲va在线va天堂va偷拍 | 国产精品不卡一区 | 国产精品自在欧美一区 | 久久久精品日本 | 一区二区精品在线观看 | 色瓜 | 8x成人在线 | 亚洲亚洲精品在线观看 | 蜜臀av性久久久久av蜜臀妖精 | 狠狠色伊人亚洲综合网站野外 | 亚洲国产成人精品在线观看 | 免费精品在线 | 久久久国产一区二区三区 | 免费视频一区二区 | 久久久激情视频 | av一区二区三区在线观看 | 97国产大学生情侣白嫩酒店 | 超碰免费在线公开 | 丁香六月国产 | 91视频国产免费 | 九九免费在线观看视频 | 在线视频欧美亚洲 | 91桃色免费观看 | 欧美久久久久久 | 久草久热 | 精品免费视频 | 国产美女免费视频 | 日韩av美女| av免费在线免费观看 | av电影免费在线播放 | 日本久久91 | 天天亚洲 | 久久久免费在线观看 | 五月婷婷天堂 | 中文字幕丝袜制服 | 在线观看www. | 久久av中文字幕片 | 欧美成人黄色 | 日韩视频图片 | 91成版人在线观看入口 | 婷婷干五月| 夜色资源站国产www在线视频 | www视频在线免费观看 | 国产成人免费在线 | 99精品视频免费在线观看 | 久久国产乱 | 国产精品女同一区二区三区久久夜 | 九九亚洲视频 | 成人国产精品av | 欧美一级视频免费看 | 99视频免费 | 久99精品| 免费中文字幕 | 亚洲精品国产精品国自产观看浪潮 | 99热官网| 天天干天天射天天爽 | 91视频啊啊啊 | 久久免费视频一区 | www.亚洲黄 | 国产精品视频永久免费播放 | 日韩专区在线播放 | 欧美一区二区三区在线观看 | 最近中文字幕免费大全 | 91精品一区二区在线观看 | 欧美激情精品久久久久久变态 | 国产精品久久久久久久久久久久午 | 亚洲成人av影片 | 日韩午夜在线观看 | 国产色在线观看 | 波多野结衣理论片 | 九九久久影视 | av中文字幕亚洲 | 国产精品毛片久久久久久久 | 人人涩| 99久久精品一区二区成人 | 五月丁婷婷 | 波多野结衣视频一区 | 狠狠色狠狠色综合系列 | 免费在线观看的av网站 | 免费观看版 | 91精品国产欧美一区二区成人 | 中文电影网| 国产视频一区在线播放 | 中文字幕 国产专区 | 久久免费在线视频 | 久久婷婷一区二区三区 | 黄色av电影在线观看 | 91精品视频免费看 | 日韩欧美一区二区在线播放 | 中文字幕第一页在线播放 | 色婷婷九月 | 色欧美视频 | 丝袜制服综合网 | 国产在线a不卡 | 91视频-88av | 精品不卡av| 高清国产午夜精品久久久久久 | 亚洲欧美激情插 | 欧美国产不卡 | 亚洲精品国产精品99久久 | 国产精品视频地址 | 黄色录像av | 天天色官网 | 国产精品亚洲综合久久 | 日日夜夜干 | 国产日韩av在线 | 青青草华人在线视频 | 亚洲成av| 久久综合五月 | 在线视频观看你懂的 | 97精品国产97久久久久久久久久久久 | 久久黄色网页 | 人人擦 | 91成人免费视频 | 亚洲精品国精品久久99热 | 九九精品视频在线看 | 欧美日韩一区二区三区在线免费观看 | 狠狠狠色狠狠色综合 | 久久久网| 精品一二三区视频 | 久久香蕉国产精品麻豆粉嫩av | 毛片的网址 | 97视频久久久 | 中文字幕超清在线免费 | 天堂在线视频免费观看 | 免费看的黄色 | 天天色天天上天天操 | 日韩av在线小说 | 在线免费看黄色 | 免费电影一区二区三区 | 午夜精品久久久久久99热明星 | av一级片在线观看 | 亚洲一级电影在线观看 | 国产精品v a免费视频 | 国产亚洲婷婷 | 成人影音av | 视频精品一区二区三区 | 97av在线视频免费播放 | 欧美日韩国产综合一区二区 | 国产成人精品一区二区三区福利 | 国产精品无 | 中文字幕在线视频一区二区三区 | 精品视频在线播放 | 久久美女免费视频 | 99在线热播精品免费99热 | 狠狠干网址 | 国产乱对白刺激视频在线观看女王 | 在线成人免费电影 | 国产不卡精品 | 伊人激情网 | 99麻豆视频 | 看av免费| 99久久精品视频免费 | 国产又粗又猛又爽又黄的视频先 | 日韩电影一区二区三区在线观看 | 天天干夜夜擦 | 91香蕉国产 | 久久精品—区二区三区 | 亚洲成av人片在线观看 | 91黄在线看 | 69精品视频在线观看 | 久免费 | 最新日韩视频 | 亚洲人成免费 | 中文字幕国产 | 免费视频 三区 | 伊色综合久久之综合久久 | 可以免费观看的av片 | 成人va在线观看 | 亚洲一区免费在线 | 久久 亚洲视频 | 国产探花视频在线播放 | 亚洲欧美日本国产 | 视频福利在线观看 | 玖玖在线播放 | 一本一道波多野毛片中文在线 | 91九色蝌蚪视频网站 | 天天草夜夜 | 久久国产欧美日韩 | 四虎在线观看精品视频 | 国产香蕉视频在线播放 | 99中文视频在线 | 精品在线观 | 天天色天天艹 | 日本bbbb摸bbbb | 日韩欧美视频二区 | 久久看片网站 | 麻豆视频在线看 | 日韩国产在线观看 | 一区二区三区日韩在线 | 91pony九色丨交换 | 国产精品久久久久久久免费大片 | 中文字幕在线看视频国产中文版 | 久久99精品一区二区三区三区 | 91视频麻豆视频 | 欧美伦理一区二区三区 | av黄色免费看 | 天天干天天弄 | 久久精品牌麻豆国产大山 | 久久综合久久八八 | 亚洲免费婷婷 | 精品一区 精品二区 | 国产小视频免费观看 | 国产中文字幕视频在线观看 | 黄色av网站在线观看免费 | 欧美日韩aa | 国产小视频免费观看 | 综合在线色 | 伊人婷婷综合 | 亚洲精品日韩av | 99人久久精品视频最新地址 | 99热在线国产精品 | 亚州国产精品视频 | 色噜噜在线观看 | 免费麻豆视频 | 在线婷婷 | 亚洲综合成人专区片 | 人人干在线 | 国产系列 在线观看 | 久久久久久久久久久福利 | 天天草天天色 | 欧美精品一区在线 | 国产精品电影在线 | 国产美女在线精品免费观看 | 伊人久久在线观看 | 欧美一级高清片 | 97成人免费视频 | 叶爱av在线 | 亚洲综合在线一区二区三区 | 热久久免费国产视频 | 亚洲午夜在线视频 | 久久国产精品免费观看 | 一区二区欧美在线观看 | av在线一二三区 | 国产精品一区二区白浆 | 成人小视频在线播放 | 麻豆激情电影 | 欧美精品一区二区在线播放 | 亚洲国产伊人 | 偷拍精偷拍精品欧洲亚洲网站 | 天天综合人人 | 亚洲一区美女视频在线观看免费 | 在线观看精品黄av片免费 | 欧美91精品久久久久国产性生爱 | 伊人中文字幕在线 | 一区二区三区久久 | 久久午夜羞羞影院 | 91xav | 欧美另类xxx | 欧美日韩一区二区三区免费视频 | 亚洲成a人片77777kkkk1在线观看 | 亚洲精品777 | 在线高清| 日韩精品视频在线观看网址 | 亚洲人久久久 | 亚洲精品中文在线 | 国产在线播放一区 | 在线观看一区 | 丁香五婷| 中字幕视频在线永久在线观看免费 | 免费在线观看成人小视频 | 成人国产精品av | 玖玖爱在线观看 | 亚洲人xxx | 亚洲精品在线一区二区三区 | 欧美成人黄色 | 97av精品| 高清日韩一区二区 | 久久99久久99精品免观看粉嫩 | 一二三久久久 | 片网站| 亚洲国产大片 | 久草视频2 | 中文字幕在线国产 | 国产精品福利在线播放 | 欧美性猛片, | www久久 | 久久综合免费视频 | 97精品国产97久久久久久免费 | 一区二区三区四区免费视频 | 成人免费网站在线观看 | 国产精品成人一区二区三区 | 色婷婷狠狠操 | 中文字幕日本在线观看 | 久久久久亚洲天堂 | 色伊人网 | 最近中文字幕免费大全 | 国产精品网红直播 | 黄色一级动作片 | 日韩高清在线看 | 久久久久久久久久免费 | 毛片在线播放网址 | 91亚洲精品久久久 | 色综合久久久久综合体 | 久久久精品国产一区二区三区 | 国产视 | 新版资源中文在线观看 | 中文字幕乱在线伦视频中文字幕乱码在线 | 国产自产高清不卡 | 超碰人人在 | 婷五月天激情 | 久久情爱| 国产在线欧美 | 天天色天天色天天色 | 国产精品久久99 | 日本中文字幕网站 | 亚洲狠狠干 | 69人人| 在线 你懂 | 色婷婷播放 | 国产99在线免费 | 狠狠干婷婷| 97精品久久人人爽人人爽 | 日韩专区一区二区 | 一区二区不卡高清 | 婷婷狠狠操| 99视频久久 | 久久只精品99品免费久23小说 | 免费一级特黄录像 | 亚洲成av | 欧美成年人在线视频 | 经典三级一区 | 亚洲精品在线二区 | 欧美一区二区三区在线观看 | 成人一区二区三区在线 | 日本在线观看中文字幕无线观看 | 久草在线综合网 | 精品国产一二三 | av一级免费 | a级国产毛片 | 久久经典国产 | 中文字幕在线专区 | 欧美国产精品一区二区 | 久久久91精品国产 | av一级片网站 | 69av视频在线 | 国产精品久久久久久久久久久久午夜 | 91完整版观看 | 很污的网站 | 国产成人在线免费观看 | 国产精品久久久久久久久免费看 | 免费三级影片 | 丁香导航 | 亚洲爱爱视频 | 97人人澡人人爽人人模亚洲 | 在线观av | 婷婷六月久久 | 亚洲.www| 中文字幕在线播放日韩 | 中文字幕视频一区 | 免费视频一区 | av理论电影 | 欧美精品资源 | 欧美婷婷色 | 欧美日韩在线视频免费 | 国产xx在线| av3级在线 | 国产一级特黄毛片在线毛片 | 午夜精品一区二区三区在线视频 | 久久久精品国产一区二区 | 精品视频不卡 | 九色91在线 | av资源中文字幕 | 五月亚洲婷婷 | 色吊丝在线永久观看最新版本 | 国产精品久久久久一区二区国产 | 中文字幕视频观看 | 免费看av在线 | 欧美日韩高清在线一区 | 香蕉在线视频播放网站 | 国产电影一区二区三区四区 | 日韩激情久久 | 狠狠躁天天躁综合网 | 97超碰人人爱| 正在播放 国产精品 | 中文资源在线播放 | 国产精品久久电影网 | 国产精品久久久视频 | 香蕉视频在线视频 | 久久成人国产精品 | 国产码电影 | 欧美激情va永久在线播放 | 国产麻豆传媒 | 深爱婷婷久久综合 | 国产一区国产二区在线观看 | 国产不卡在线观看 | 久久国语露脸国产精品电影 | 国产午夜精品久久久久久久久久 | 国产精品久久久久亚洲影视 | 国产伦理久久精品久久久久_ | 91中文字幕 | 免费精品人在线二线三线 | 亚洲三区在线 | 国产尤物一区二区三区 | 五月婷婷在线播放 | 日韩欧美视频在线免费观看 | 欧美日在线 | 91桃色视频| 精品国产精品一区二区夜夜嗨 | 中文字幕国产视频 | 午夜三级理论 | 婷婷色资源 | 黄色亚洲免费 | 涩涩网站在线观看 | 日韩美女黄色片 | 日韩v在线91成人自拍 | 国产精品完整版 | 韩国精品一区二区三区六区色诱 | 少妇高潮冒白浆 | 色综合久久久久久中文网 | 99午夜| 91精品国产高清自在线观看 | 国产不卡视频 | 天天躁日日躁狠狠躁av麻豆 | 国产99久久久精品 | 精品久久久国产 | 国产午夜av | 97电影在线观看 | 中文字幕黄色av | 91av在线免费看 | 97视频资源 | 久久avav | 久久成年视频 | 九九国产精品视频 | 在线观看免费福利 | 亚洲精品国产综合99久久夜夜嗨 | 人人揉人人揉人人揉人人揉97 | 九九热1 | 免费视频一区 | 欧美极度另类性三渗透 | 国产福利在线免费 | 夜夜干天天操 | 国产精品av免费 | 久久精品国产成人 | 久久久国产99久久国产一 | 成人免费在线观看入口 | 亚洲精品视频久久 | 亚洲国产精品成人va在线观看 | 色丁香婷婷 | 狠狠操天天干 | 啪啪免费试看 | 欧美日韩1区 | 五月婷婷色播 | 国产超碰在线 | 右手影院亚洲欧美 | 国产中的精品av小宝探花 | 99热在线精品观看 | 久久久穴 | 婷婷 中文字幕 | 在线观看你懂的网址 | 午夜精品久久久久久久99婷婷 | 久久精品香蕉视频 | 麻豆系列在线观看 | 日韩特级毛片 | 国产免费观看久久黄 | 亚洲精品午夜aaa久久久 | www.狠狠操.com | 久久久久久精 | 精品成人国产 | 午夜精品影院 | 日韩精品三区四区 | 久久丁香网 | 国产黑丝一区二区三区 | 天天色天天干天天色 | 伊人干综合 | 亚洲欧美日韩精品久久奇米一区 | 色播五月激情五月 | 超碰大片 | 国产精品mv | 亚洲国产精品久久久 | 五月天,com| 亚洲资源在线网 | 国产亚州av | 91精品国产一区 | 天天爱天天射天天干天天 | 91视频a| 91禁看片 | 国产在线观看免 | 国产精品视频久久 | 天堂视频一区 | 日韩成人欧美 | 国产在线观看av | 青青久草在线视频 | 国产精品国产亚洲精品看不卡15 | 在线中文字幕网站 | 日韩乱色精品一区二区 | 国产一级片在线播放 | 在线免费看黄网站 | 久久综合婷婷 | 亚洲午夜不卡 | 激情欧美一区二区三区免费看 | 久久久精品日本 | 久久综合电影 | 日韩欧美综合在线视频 | 日韩动漫免费观看高清完整版在线观看 | 久久久久久久久久久免费av | 欧洲精品码一区二区三区免费看 | 在线看片成人 | 天天夜夜狠狠操 | 国产免费xvideos视频入口 | 高清不卡一区二区在线 | 欧美在线视频一区二区三区 | 激情五月播播久久久精品 | 国产午夜精品福利视频 | 天天色天天射天天综合网 | 91九色精品国产 | 超碰个人在线 | 丁香婷婷社区 | 国产美腿白丝袜足在线av | 麻豆视频在线播放 | 麻花豆传媒mv在线观看 | 少妇bbw搡bbbb搡bbb | 天天做日日爱夜夜爽 | 久久久久中文字幕 | 日韩精品你懂的 | 波多野结衣一区二区 | 成人黄色小说在线观看 | 欧美大片大全 | 黄p在线播放 | www.国产在线视频 | 亚洲精品国产电影 | 亚洲成免费| 九九热在线观看视频 | 色的网站在线观看 | 粉嫩av一区二区三区四区 | 免费视频黄 | 欧美大片www | 国产黄免费 | 久久精品国产一区二区三 | 在线视频第一页 | 成人小视频在线观看免费 | 超碰97国产在线 | 激情视频91 | www免费黄色 | 国产高清视频免费观看 | 中文字幕在线观看资源 | 久久男人免费视频 | 天天干,天天操,天天射 | 综合色中文 | 99亚洲精品在线 | 久久视频| 久久综合狠狠综合久久激情 | 青草视频免费观看 | 国产精品一区二区av日韩在线 | 亚洲激情综合 | 亚洲精品66| 97视频在线观看网址 | 西西444www大胆无视频 | 国产亚洲精品精品精品 | 中文在线8新资源库 | 日韩电影在线观看一区二区 | 久久精品99视频 | 国产黄色精品在线 | 西西www444 | 久久久精品免费看 | 欧美一级久久 | 一区二区三区中文字幕在线观看 | 中文字幕在线日本 | 狠狠干美女| 麻豆激情电影 | 国产一区二区三区免费观看视频 | 国产一级久久 |