国产秋霞理论久久久电影-婷婷色九月综合激情丁香-欧美在线观看乱妇视频-精品国avA久久久久久久-国产乱码精品一区二区三区亚洲人-欧美熟妇一区二区三区蜜桃视频

由 OOM 引發(fā)的 ext4 文件系統(tǒng)卡死

共 23210字,需瀏覽 47分鐘

 ·

2021-01-27 12:48


注:本問(wèn)題影響 3.10.0-862.el7.centos 及之后的 CentOS 7 版本內(nèi)核,目前問(wèn)題還未被修復(fù)。

背景

近日,我司的測(cè)試同學(xué)發(fā)現(xiàn)內(nèi)部集群中一個(gè)存儲(chǔ)節(jié)點(diǎn)無(wú)法通過(guò) ssh 訪問(wèn)了。iDRAC console 界面出現(xiàn)如下圖的錯(cuò)誤:有若干個(gè)服務(wù)進(jìn)程由于 cgroup 超過(guò)內(nèi)存限制被 Kill,隨后許多進(jìn)程陸續(xù)卡住,出現(xiàn) “task xxx blocked for more than 120 seconds”。此時(shí),系統(tǒng)無(wú)法進(jìn)行賬號(hào)登錄,似乎整個(gè)根文件系統(tǒng)(ext4)都卡住了。測(cè)試同學(xué)通過(guò) ipmitool 工具發(fā)送 NMI 信號(hào)讓系統(tǒng)產(chǎn)生 vmcore(Linux 內(nèi)核崩潰時(shí)產(chǎn)生的內(nèi)核轉(zhuǎn)儲(chǔ)文件)以便后續(xù)分析,然后重啟機(jī)器讓業(yè)務(wù)恢復(fù)。

將多個(gè)服務(wù)放置在 cgroup 中,限制它們的內(nèi)存使用量;當(dāng)內(nèi)存使用量超過(guò)預(yù)設(shè)上限值,OOM(Out-of-Memory) Killer 按照一定策略 Kill 掉部分進(jìn)程,實(shí)現(xiàn)內(nèi)存釋放。這是很常規(guī)的操作,如 Red Hat Enterprise Linux Resource Management Guide - memory(https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/resource_management_guide/sec-memory)。

從內(nèi)存使用量的監(jiān)控圖表中,我們確定了系統(tǒng)中某個(gè)服務(wù)存在內(nèi)存占用隨著實(shí)例數(shù)增加而線性增長(zhǎng)的 bug。但是,為什么 OOM 會(huì)導(dǎo)致 ext4 文件系統(tǒng)卡死呢?

相關(guān)問(wèn)題檢索

從日志和采集到的 vmcore 中,發(fā)現(xiàn)有許多進(jìn)程都卡在 ext4/jbd2 的 I/O 操作上。其中 jbd2 (Journaling block device)是 kernel 中通用的 journal 模塊,被 ext4 使用。從日志中可以看出,OOM 后不久就觸發(fā)許多進(jìn)程卡住,可以推斷 OOM 與 ext4 卡住存在很強(qiáng)的關(guān)聯(lián)性。

為此,找到如下一些 RedHat 官方關(guān)于 ext4/jbd2 的 KB,但經(jīng)過(guò)版本和調(diào)用棧比對(duì),我們排除了它們,看上去這是一個(gè)新的問(wèn)題。

Hung task "jbd2/dm-16-8" in jbd2_journal_commit_transaction() when issuing I/O through a software RAID or mirror device(https://access.redhat.com/solutions/3226391)

RHEL7: jbd2 deadlock occurs while checkpoint thread waits commit thread to finish(https://access.redhat.com/solutions/4054181).

嘗試復(fù)現(xiàn)

嘗試根據(jù)現(xiàn)有線索構(gòu)建復(fù)現(xiàn)腳本,方便后續(xù) debug 分析和假想驗(yàn)證。一旦復(fù)現(xiàn)問(wèn)題,就可以生成 vmcore 做分析。然而,vmcore 只能記錄產(chǎn)生 vmcore 時(shí)刻的快照信息,犯罪進(jìn)程可能已經(jīng)逃離現(xiàn)場(chǎng),此時(shí)更詳細(xì)的日志可以幫助恢復(fù)現(xiàn)場(chǎng)。因此,我們還開(kāi)啟了 jbd2 模塊日志功能,重新編譯內(nèi)核。

初次嘗試

根據(jù)網(wǎng)上一些相似問(wèn)題的討論,我們最初的猜想是 cgroup 內(nèi)的進(jìn)程在做 ext4 I/O 操作時(shí),可能由于內(nèi)存限制無(wú)法申請(qǐng)到內(nèi)存后忘記釋放文件系統(tǒng)的某些關(guān)鍵資源,導(dǎo)致其他進(jìn)程(可能是 cgroup 內(nèi),也可能不處于 cgroup 中)ext4 I/O 操作無(wú)法進(jìn)行。于是,有了以下復(fù)現(xiàn)方法:

  • 在 cgroup 中有兩類進(jìn)程,一類不斷分配內(nèi)存,在 15s 左右達(dá)到 cgroup 的內(nèi)存限制;另一類跑 fio,對(duì) ext4 分區(qū)的文件做大塊隨機(jī)寫(xiě)操作。每類進(jìn)程各 3 個(gè);如果進(jìn)程被 OOM Kill,會(huì)被 systemd 重新拉起;
  • 在 cgroup 之外,有另外 3 個(gè) fio 進(jìn)程,對(duì) ext4 分區(qū)的其他文件做大塊隨機(jī)寫(xiě)操作;

經(jīng)歷了 7 個(gè)小時(shí)左右,我們復(fù)現(xiàn)了一次,卡住現(xiàn)象與最初現(xiàn)場(chǎng)一致。但隨后一天中,4 個(gè)節(jié)點(diǎn)都沒(méi)法再次復(fù)現(xiàn),需要找到加速?gòu)?fù)現(xiàn)的方式。

再次嘗試

由于前兩次復(fù)現(xiàn)的場(chǎng)景最后被 OOM Kill 的進(jìn)程都是 IO 進(jìn)程,而上述復(fù)現(xiàn)步驟中經(jīng)常被 Kill 的是分配內(nèi)存的進(jìn)程,因此考慮做以下調(diào)整:

  • 準(zhǔn)備一個(gè)測(cè)試程序 ext4-repro-with-write,打開(kāi) ext4 文件系統(tǒng)上的文件,并不斷地對(duì)該文件做寫(xiě)操作,過(guò)程中會(huì)涉及到 ext4 inode 內(nèi)部的一些內(nèi)存分配操作;測(cè)試的 cgroup 中只有這一類進(jìn)程,OOM Kill 的只可能是這類進(jìn)程;
  • 創(chuàng)建 10 個(gè) cgroup,每個(gè)的內(nèi)存限制為 100 MB;
  • 對(duì)于每個(gè) cgroup,創(chuàng)建 30 個(gè) ext4-repro-with-write 進(jìn)程;

使用這一腳本,我們能夠在 10 分鐘以內(nèi)復(fù)現(xiàn)這個(gè)問(wèn)題。相關(guān)復(fù)現(xiàn)腳本已經(jīng)上傳到 https://gist.github.com/jiewei-ke/4962ac0061703fdcd0837bab2dbec261。

確定影響版本

分別測(cè)試 CentOS 7 的幾個(gè)官方內(nèi)核版本以及 CentOS 8 的 4.18 內(nèi)核,我們確定:

  • CentOS 7 3.10.0-693.el7 版本沒(méi)有問(wèn)題;
  • CentOS 7 3.10.0-862.el7 及后續(xù)版本(包含最新的 3.10.0-1160.el7)都有問(wèn)題;
  • CentOS 8 4.18.0 版本沒(méi)有問(wèn)題;

即該問(wèn)題是 CentOS 7 3.10.0-862.el7 引入的。

vmcore 分析

獲取到帶有 jbd2 模塊日志的 vmcore 后,下面使用 crash 工具對(duì)其進(jìn)行分析。

注:分析過(guò)程比較冗長(zhǎng),對(duì)于 vmcore 分析不感興趣的同學(xué)可以直接跳到 Recap 部分看結(jié)論。

ext4/jbd2 簡(jiǎn)介

參考 Linux: The Journaling Block Device(https://web.archive.org/web/20070926223043/http://kerneltrap.org/node/6741),ext4 文件系統(tǒng)基于 jbd2 以 WAL(write-ahead logging)形式更新元數(shù)據(jù),防止系統(tǒng) crash 時(shí)發(fā)生元數(shù)據(jù)損壞。每一個(gè) ext4 文件系統(tǒng)實(shí)例對(duì)應(yīng)一個(gè) jbd2 Journal 實(shí)例。每個(gè)用戶線程的一次數(shù)據(jù)更新稱為一個(gè) Handle,在時(shí)間上連續(xù)的多個(gè) Handle(可能來(lái)自不同線程)組成一個(gè) Transaction,以便實(shí)現(xiàn)批量提交。Transaction 提交滿足原子性。

Journal 包括兩個(gè)階段,Commit 和 Checkpoint。Commit 負(fù)責(zé)提交修改,由用戶線程和內(nèi)核 Journal 線程配合完成。

  • 當(dāng)用戶程序?qū)?ext4 文件系統(tǒng)做文件操作(例如 create())時(shí),用戶線程先獲取 Handle,然后獲取 Journal 當(dāng)前的 running Transaction,如果存在則使用它,不存在則新創(chuàng)建一個(gè)。同時(shí)原子增加 running Transaction 的 t_updates 計(jì)數(shù),表示該 Transaction 被多少個(gè)線程使用。隨后,用戶線程將修改的元數(shù)據(jù)內(nèi)容加入到 Transaction 的 buffer 鏈表中,遞減 t_updates。
  • 內(nèi)核的 Journal 線程會(huì)定期喚醒,將 Journal 的 running Transaction 變?yōu)?committing 狀態(tài),然后等待 t_updates 歸零,即等待用戶線程完成插入 buffer 鏈表。隨后將 buffer 鏈表合并寫(xiě)入到對(duì)應(yīng)的 Journal 區(qū)域。隨后該 Transaction 加入等待 checkpoint 的 Transaction 列表中。
  • 以上過(guò)程中,每個(gè) Journal 實(shí)例同時(shí)只有一個(gè) running Transaction 和一個(gè) committing Transaction。

Checkpoint 負(fù)責(zé)持久化修改,由用戶線程發(fā)起。用戶線程從待 checkpoint 的 Transaction 列表中獲取 buffer,然后合并寫(xiě)入到物理磁盤(pán),Transaction 至此結(jié)束。

以上描述只關(guān)注本文涉及到的步驟。更詳細(xì)的過(guò)程請(qǐng)參考 High-Performance Transaction Processing in Journaling File Systems(https://www.usenix.org/system/files/conference/fast18/fast18-son.pdf) 的 Background 部分。

進(jìn)程堆棧分析

針對(duì) vmcore,通過(guò) crash 命令 ”foreach bt > bt.log“ 獲得所有進(jìn)程的堆棧信息。根據(jù) jbd2 關(guān)鍵字過(guò)濾進(jìn)程,主要有以下幾類:

類別 1:內(nèi)核的 Journal 線程卡在等待 t_updates 歸零

上文提到,內(nèi)核 Journal 線程進(jìn)行 Commit 之前需要先等待 t_updates 歸零。下圖的 Journal 線程正是卡在等待 t_updates 歸零的循環(huán)中,說(shuō)明此時(shí)有一個(gè)用戶進(jìn)程正在發(fā)起 Commit 操作,但是還沒(méi)有完成 buffer 鏈表的更新。

PID: 587    TASK: ffff9f53f8c31070  CPU: 5   COMMAND: "jbd2/md127-8"
#0 [ffff9f53ff35bbf0] __schedule at ffffffffb139e420
#1 [ffff9f53ff35bc88] schedule at ffffffffb139eb09
#2 [ffff9f53ff35bc98] jbd2_journal_commit_transaction at ffffffffc01fe8b0 [jbd2]
#3 [ffff9f53ff35be40] kjournald2 at ffffffffc0205c5e [jbd2]
#4 [ffff9f53ff35bec8] kthread at ffffffffb0cc6bd1
/*
* jbd2_journal_commit_transaction
*
* The primary function for committing a transaction to the log. This
* function is called by the journal thread to begin a complete commit.
*/
void jbd2_journal_commit_transaction(journal_t *journal)
{
// ...
while (atomic_read(&commit_transaction->t_updates)) { <<< 在這循環(huán)
DEFINE_WAIT(wait);

prepare_to_wait(&journal->j_wait_updates, &wait,
TASK_UNINTERRUPTIBLE);
if (atomic_read(&commit_transaction->t_updates)) {
spin_unlock(&commit_transaction->t_handle_lock);
write_unlock(&journal->j_state_lock);
schedule();
write_lock(&journal->j_state_lock);
spin_lock(&commit_transaction->t_handle_lock);
}
finish_wait(&journal->j_wait_updates, &wait);
}

類別 2:等待 Transaction Commit 完成的進(jìn)程

有 3 個(gè)進(jìn)程,它們都在等待 Transaction Commit 的完成。jbd2_log_wait_commit() 正在等待 j_wait_done_commit,該鎖正被類別 1 的 Journal 線程持有,只有當(dāng) Journal 線程完成 jbd2_journal_commit_transaction() 時(shí),才會(huì)釋放 j_wait_done_commit。因此,這 3 個(gè)進(jìn)程也是被動(dòng)卡住的線程,正在等待 Journal 線程。

PID: 27153  TASK: ffff9f53fc35a0e0  CPU: 5   COMMAND: "ext4-repro-with-write"
#0 [ffff9f526e04fd70] __schedule at ffffffffb139e420
#1 [ffff9f526e04fe08] schedule at ffffffffb139eb09
#2 [ffff9f526e04fe18] jbd2_log_wait_commit at ffffffffc02054a5 [jbd2]
#3 [ffff9f526e04fe98] jbd2_complete_transaction at ffffffffc0206e21 [jbd2]
#4 [ffff9f526e04feb8] ext4_sync_file at ffffffffc03cff60 [ext4]
#5 [ffff9f526e04ff00] do_fsync at ffffffffb0e8ccf7
#6 [ffff9f526e04ff40] sys_fsync at ffffffffb0e8cff0
#7 [ffff9f526e04ff50] system_call_fastpath at ffffffffb13ac81e

類別 3:等待 Commit Transaction 的用戶進(jìn)程

這類有 32 個(gè)進(jìn)程,數(shù)量比較多,都卡在 jbd2__journal_start() -> wait_transaction_locked() 上,在 Commit Transaction 的早期階段。這些進(jìn)程都是被動(dòng)卡住的,不是事發(fā)原因。

PID: 906    TASK: ffff9f53fa13b150  CPU: 5   COMMAND: "loop0"
#0 [ffff9f53fb7cf830] __schedule at ffffffffb139e420
#1 [ffff9f53fb7cf8c8] schedule at ffffffffb139eb09
#2 [ffff9f53fb7cf8d8] wait_transaction_locked at ffffffffc01fb085 [jbd2]
#3 [ffff9f53fb7cf930] add_transaction_credits at ffffffffc01fb360 [jbd2]
#4 [ffff9f53fb7cf990] start_this_handle at ffffffffc01fb63f [jbd2]
#5 [ffff9f53fb7cfa48] jbd2__journal_start at ffffffffc01fbbb6 [jbd2]
#6 [ffff9f53fb7cfa90] __ext4_journal_start_sb at ffffffffc0409c89 [ext4]
#7 [ffff9f53fb7cfad0] ext4_dirty_inode at ffffffffc03dc9da [ext4]
#8 [ffff9f53fb7cfaf0] __mark_inode_dirty at ffffffffb0e87280
#9 [ffff9f53fb7cfb28] update_time at ffffffffb0e7484a
#10 [ffff9f53fb7cfb58] file_update_time at ffffffffb0e74961
#11 [ffff9f53fb7cfba0] __generic_file_aio_write at ffffffffb0dc4eac
#12 [ffff9f53fb7cfc20] generic_file_aio_write at ffffffffb0dc5109
#13 [ffff9f53fb7cfc60] ext4_file_write at ffffffffc03cfb8e [ext4]
#14 [ffff9f53fb7cfcd8] do_sync_write at ffffffffb0e54936
#15 [ffff9f53fb7cfdb0] __do_lo_send_write at ffffffffc0597045 [loop]
#16 [ffff9f53fb7cfdf8] do_lo_send_direct_write at ffffffffc0597156 [loop]
#17 [ffff9f53fb7cfe30] loop_thread at ffffffffc0598382 [loop]
#18 [ffff9f53fb7cfec8] kthread at ffffffffb0cc6bd1

在以上 3 種類別中,類別 1 內(nèi)核的 Journal 線程卡住是導(dǎo)致其他進(jìn)程卡住的原因,此時(shí)整個(gè) ext4 文件系統(tǒng)的文件操作都無(wú)法繼續(xù)。下面著重分析該進(jìn)程。

內(nèi)核的 Journal 線程分析

首先檢查當(dāng)前 Journal 的狀況。crash 切換到內(nèi)核 Journal 線程,顯示每個(gè) stack frame 的參數(shù)寄存器內(nèi)容,主要關(guān)注 jbd2_journal_commit_transaction()。嘗試解析各個(gè)地址,發(fā)現(xiàn) ffff9f4539ac9000 能夠正確解析為 journal_t 類型(也可以根據(jù)反匯編確定參數(shù)是如何傳遞的),拿到 Journal 的指針。

crash> mod -s jbd2
MODULE NAME SIZE OBJECT FILE
ffffffffc02100e0 jbd2 117134 /lib/modules/3.10.0-1.el7.x86_64/kernel/fs/jbd2/jbd2.ko

crash> set 587
PID: 587
COMMAND: "jbd2/md127-8"
TASK: ffff9f53f8c31070 [THREAD_INFO: ffff9f53ff358000]
CPU: 5
STATE: TASK_UNINTERRUPTIBLE

crash> bt
PID: 587 TASK: ffff9f53f8c31070 CPU: 5 COMMAND: "jbd2/md127-8"
#0 [ffff9f53ff35bbf0] __schedule at ffffffffb139e420
#1 [ffff9f53ff35bc88] schedule at ffffffffb139eb09
#2 [ffff9f53ff35bc98] jbd2_journal_commit_transaction at ffffffffc01fe8b0 [jbd2]
#3 [ffff9f53ff35be40] kjournald2 at ffffffffc0205c5e [jbd2]
#4 [ffff9f53ff35bec8] kthread at ffffffffb0cc6bd1

crash> bt -fl
PID: 587 TASK: ffff9f53f8c31070 CPU: 5 COMMAND: "jbd2/md127-8"
#0 [ffff9f53ff35bbf0] __schedule at ffffffffb139e420
...
#1 [ffff9f53ff35bc88] schedule at ffffffffb139eb09
/usr/src/debug/kernel-ml-3.10.0/linux-3.10.0-1.el7.x86_64/kernel/sched/core.c: 3654
ffff9f53ff35bc90: ffff9f53ff35be38 ffffffffc01fe8b0
#2 [ffff9f53ff35bc98] jbd2_journal_commit_transaction at ffffffffc01fe8b0 [jbd2]

ffff9f53ff35be10: ffff9f4539ac9028 ffff9f4539ac93e8
ffff9f53ff35be20: ffff9f4539ac90a8 ffff9f53ff35be80
ffff9f53ff35be30: ffff9f4539ac9000 ffff9f53ff35bec0 // 能夠正確解析為 Journal 的地址
ffff9f53ff35be40: ffffffffc0205c5e
#3 [ffff9f53ff35be40] kjournald2 at ffffffffc0205c5e [jbd2]
ffff9f53ff35be48: ffff9f5316aa0f62 ffff9f53f8c31070
ffff9f53ff35be58: ffff9f53f8c31070 ffff9f4539ac9090
...
#4 [ffff9f53ff35bec8] kthread at ffffffffb0cc6bd1
/usr/src/debug/kernel-ml-3.10.0/linux-3.10.0-1.el7.x86_64/kernel/kthread.c: 202

crash> journal_t.j_devname ffff9f4539ac9000
j_devname = "md127-8\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"

獲取 Journal 的 running Transaction 0xffff9f53d4ec1900,可以看到它的 t_updates 為 1,因此處于循環(huán)等待中。

crash> journal_t.j_running_transaction ffff9f4539ac9000
j_running_transaction = 0xffff9f53d4ec1900
crash> transaction_t 0xffff9f53d4ec1900
struct transaction_t {
t_journal = 0xffff9f4539ac9000,
t_tid = 380243810,
t_state = T_LOCKED,
...
t_updates = {
counter = 1 // t_updates 為 1,處于循環(huán)等待
},
t_outstanding_credits = {
counter = 56
},

}

提交 Transaction 的用戶進(jìn)程分析

t_updates 的更新是由提交 Transaction 的用戶進(jìn)程完成的。下面看一下其更新流程。

t_updates 遞增的過(guò)程

在 Transaction 被提交前,用戶進(jìn)程會(huì)先通過(guò) jbd2__journal_start() 獲取當(dāng)前進(jìn)程的 Handle,隨后調(diào)用 start_this_handle() 為 Transaction 預(yù)留需要的 Journal 空間。

static inline handle_t *journal_current_handle(void)
{
return current->journal_info;
}

handle_t *jbd2__journal_start(journal_t *journal, int nblocks, int rsv_blocks,
gfp_t gfp_mask, unsigned int type,
unsigned int line_no)
{
handle_t *handle = journal_current_handle(); // 獲取進(jìn)程的 Handle
...
err = start_this_handle(journal, handle, gfp_mask);

在 start_this_handle() 中,我們看到 Handle 中保存了 running Transaction 的指針,同時(shí) Transaction 的 t_updates 被原子加一。

static int start_this_handle(journal_t *journal, handle_t *handle,
gfp_t gfp_mask)
{
...
transaction = journal->j_running_transaction;

...
update_t_max_wait(transaction, ts);
handle->h_transaction = transaction;
handle->h_requested_credits = blocks;
handle->h_start_jiffies = jiffies;
atomic_inc(&transaction->t_updates);
atomic_inc(&transaction->t_handle_count);

t_updates 遞減的過(guò)程

相對(duì)應(yīng)的,Transaction 的 t_updates 是由 jbd2_journal_stop 中遞減的。

int jbd2_journal_stop(handle_t *handle)
{
...
/*
* Once we drop t_updates, if it goes to zero the transaction
* could start committing on us and eventually disappear. So
* once we do this, we must not dereference transaction
* pointer again.
*/
tid = transaction->t_tid;
if (atomic_dec_and_test(&transaction->t_updates)) {
wake_up(&journal->j_wait_updates);
if (journal->j_barrier_count)
wake_up(&journal->j_wait_transaction_locked);
}

t_updates 為何沒(méi)有歸零?

我們找一下是否有 Handle 持有著這個(gè) Transaction。事先添加的 jbd2 模塊日志會(huì)打印出 Handle 相關(guān)日志,檢索日志中曾經(jīng)出現(xiàn)過(guò)的 Handle 指針如下,

$ grep jbd2_journal_stop vmcore-dmesg.txt | grep Handle | awk '{print $7}' | sort | uniq
...
ffff9f53f9676cf0
ffff9f53fa89bb70
ffff9f53fe3423c0

逐個(gè)嘗試解析 Handle,發(fā)現(xiàn) Handle ffff9f53fa89bb70 正在處理上述的 Transaction ?0xffff9f53d4ec1900。其中,h_line_no 為 2264,即該 Handle 是在行號(hào)為 2264 的代碼啟動(dòng)的(但沒(méi)有包含文件名信息)。

crash> handle_t ffff9f53fa89bb70
struct handle_t {
{
h_transaction = 0xffff9f53d4ec1900,
h_journal = 0xffff9f53d4ec1900 // 這里好像填錯(cuò)了,跟 transaction 是一樣的。。
},
h_rsv_handle = 0x0,
h_buffer_credits = 33,
h_ref = 1,
h_err = 0,
h_sync = 0,
h_jdata = 0,
h_reserved = 0,
h_aborted = 0,
h_type = 4,
h_line_no = 2264,
h_start_jiffies = 4298558935,
h_requested_credits = 35
}

在 ext4 代碼中,發(fā)現(xiàn) ext4_create() 中調(diào)用 ext4_new_inode_start_handle() 的行號(hào)正好與之匹配。根據(jù)代碼,ext4_new_inode_start_handle() 會(huì)調(diào)用 jbd2__journal_start() 將 t_updates 加一,然后將 inode 元數(shù)據(jù)信息更新加入 Transaction。之后,ext4_create() 調(diào)用 ext4_journal_stop() 將 t_updates 減一。

static int ext4_create(struct inode *dir, struct dentry *dentry, umode_t mode,
bool excl)
{
handle_t *handle;
struct inode *inode;
int err, credits, retries = 0;

dquot_initialize(dir);

credits = (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3);
retry:
inode = ext4_new_inode_start_handle(dir, mode, &dentry->d_name, 0,
NULL, EXT4_HT_DIR, credits); // 在這里卡住了
handle = ext4_journal_current_handle();
err = PTR_ERR(inode);
if (!IS_ERR(inode)) {
inode->i_op = &ext4_file_inode_operations;
inode->i_fop = &ext4_file_operations.kabi_fops;
ext4_set_aops(inode);
err = ext4_add_nondir(handle, dentry, inode);
if (!err && IS_DIRSYNC(dir))
ext4_handle_sync(handle);
}
if (handle)
ext4_journal_stop(handle); // 還沒(méi)有調(diào)用
if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries))
goto retry;
return err;
}

t_updates 沒(méi)有歸零,說(shuō)明 ext4_journal_stop() 沒(méi)有調(diào)用。如果 Handle 還沒(méi)有執(zhí)行 jbd2_journal_stop(),那么它會(huì)被某個(gè)進(jìn)程所持有,保存在進(jìn)程控制塊 task_struct 的 journal_info 中。通過(guò)搜索所有進(jìn)程的 task_struct,可以看到 task 27225(我們的測(cè)試程序 ext4-repro-with-write)進(jìn)程正在持有這個(gè) Handle。

crash> foreach task > task.log

crash> task 27225 | grep journal_info
journal_info = 0xffff9f53fa89bb70,

crash> ps | grep 27225
27225 1 4 ffff9f533a6d3150 RU 0.0 239712 38800 ext4-repro-with-write

crash> bt 27225
PID: 27225 TASK: ffff9f533a6d3150 CPU: 4 COMMAND: "ext4-repro-with-write"
#0 [ffff9f53c234f900] __schedule at ffffffffb139e420
#1 [ffff9f53c234f990] sys_sched_yield at ffffffffb0cdafdc
#2 [ffff9f53c234f9b0] yield at ffffffffb139fca2
#3 [ffff9f53c234f9c8] free_more_memory at ffffffffb0e8ee2c
#4 [ffff9f53c234fa08] __getblk at ffffffffb0e90013
#5 [ffff9f53c234fa70] __ext4_get_inode_loc at ffffffffc03d358f [ext4]
#6 [ffff9f53c234fad8] ext4_reserve_inode_write at ffffffffc03d8e62 [ext4]
#7 [ffff9f53c234fb08] ext4_mark_inode_dirty at ffffffffc03d8f23 [ext4]
#8 [ffff9f53c234fb60] ext4_ext_tree_init at ffffffffc04038ba [ext4]
#9 [ffff9f53c234fb70] __ext4_new_inode at ffffffffc03d271b [ext4]
#10 [ffff9f53c234fc18] ext4_create at ffffffffc03e3fb3 [ext4]
#11 [ffff9f53c234fc90] vfs_create at ffffffffb0e62aa8
#12 [ffff9f53c234fcc8] do_last at ffffffffb0e67e5f
#13 [ffff9f53c234fd70] path_openat at ffffffffb0e681e0
#14 [ffff9f53c234fe08] do_filp_open at ffffffffb0e69d6d
#15 [ffff9f53c234fee0] do_sys_open at ffffffffb0e5442e
#16 [ffff9f53c234ff40] sys_openat at ffffffffb0e54504
#17 [ffff9f53c234ff50] system_call_fastpath at ffffffffb13ac81e

ext4-repro-with-write 進(jìn)程處于 RUNNING 狀態(tài),正在執(zhí)行 ext4_create -> ext4_new_inode_start_handle -> __ext4_new_inode,此時(shí) t_updates 已經(jīng)加一,但是由于 ext4_new_inode_start_handle() 還沒(méi)有結(jié)束,后續(xù)的 ext4_journal_stop() 也沒(méi)有執(zhí)行,故 t_updates 沒(méi)有歸零。

問(wèn)題出在 ext4_new_inode_start_handle(),如果 __get_blk() 獲取不到內(nèi)存時(shí),會(huì)不斷地嘗試調(diào)用 free_more_memory() 釋放掉部分內(nèi)存(事實(shí)證明釋放并沒(méi)有成功),然后再嘗試獲取。如果一直獲取不到,那么將會(huì)卡在這里。

get_blk 為何分配不到內(nèi)存?

與前文的復(fù)現(xiàn)腳本略微有些差異,這個(gè)測(cè)試中 ext4-repro-with-write 內(nèi)部包含兩個(gè)線程,一個(gè)負(fù)責(zé)不斷分配內(nèi)存,另一個(gè)負(fù)責(zé)持續(xù)寫(xiě)文件。在內(nèi)存限制為 900MB 左右的 cgroup 中啟動(dòng)了 3 個(gè)進(jìn)程。

$cat /etc/systemd/system/system-fio.slice.d/50-MemoryLimit.conf
[Slice]
MemoryLimit=973741824
>>> 973741824/1024.0/1024
928.632568359375 // 900MB 左右

按照 task group 方式檢查當(dāng)前 3 個(gè) ext4-repro-with-write 進(jìn)程的內(nèi)存使用量,可以看到內(nèi)存使用已經(jīng)接近 cgroup 內(nèi)存限制,提交 Transaction 的 task 27225 所在的 group 只占用了 37 MB,而另外兩個(gè)進(jìn)程各自占用 400 MB 以上。

crash> ps -G ext4-repro-with-write | sed 's/>//g' | sort -k 8,8 -n | awk '$8 ~ /[0-9]/{ $8 = $8/1024" MB"; print }'
27222 1 5 ffff9f53b5b9b150 RU 0.0 239712 37.8906 MB ext4-repro-with-write // 27225 所在的 group
27189 1 5 ffff9f52357c1070 IN 0.3 578292 403.246 MB ext4-repro-with-write
27153 1 5 ffff9f53fc35a0e0 UN 0.4 848900 487.488 MB ext4-repro-with-write // 需要被 Kill 的進(jìn)程

>>> 37.8906+403.246+487.488
928.6246

從 vmcore-dmesg 日志看,最近一次觸發(fā) OOM Kill 的進(jìn)程是 27153 ,但是內(nèi)存并沒(méi)有降下來(lái)。

[ 3891.873807] Memory cgroup out of memory: Kill process 27158 (ext4-repro-with-write) score 511 or sacrifice child
[ 3891.875313] Killed process 27153 (ext4-repro-with-write), UID 0, total-vm:848900kB, anon-rss:499124kB, file-rss:868kB, shmem-rss:0kB

看一下 27153 進(jìn)程,處于 UN 狀態(tài)而無(wú)法接受 Kill 信號(hào),所以內(nèi)存不可能得到釋放。

crash> ps ext4-repro-with-write
PID PPID CPU TASK ST %MEM VSZ RSS COMM
27153 1 5 ffff9f53fc35a0e0 UN 0.4 848900 499188 ext4-repro-with-write

如下圖,該進(jìn)程屬于上述類型 2 的進(jìn)程,它正在等到內(nèi)核 Journal 線程完成 Commit,死鎖條件成立。

crash> bt 27153
PID: 27153 TASK: ffff9f53fc35a0e0 CPU: 5 COMMAND: "ext4-repro-with-write"
#0 [ffff9f526e04fd70] __schedule at ffffffffb139e420
#1 [ffff9f526e04fe08] schedule at ffffffffb139eb09
#2 [ffff9f526e04fe18] jbd2_log_wait_commit at ffffffffc02054a5 [jbd2]
#3 [ffff9f526e04fe98] jbd2_complete_transaction at ffffffffc0206e21 [jbd2]
#4 [ffff9f526e04feb8] ext4_sync_file at ffffffffc03cff60 [ext4]
#5 [ffff9f526e04ff00] do_fsync at ffffffffb0e8ccf7
#6 [ffff9f526e04ff40] sys_fsync at ffffffffb0e8cff0
#7 [ffff9f526e04ff50] system_call_fastpath at ffffffffb13ac81e

Recap

根據(jù)以上冗長(zhǎng)的分析,OOM 導(dǎo)致 ext4 文件系統(tǒng)死鎖的時(shí)間線整理如下:

  • 系統(tǒng)內(nèi)有兩個(gè)用戶進(jìn)程,位于同一個(gè) cgroup 中,cgroup 上限制最大可用的內(nèi)存;
  • 進(jìn)程 1,分配大量?jī)?nèi)存,使得 cgroup 內(nèi)存使用量超過(guò)限制,OOM Killer 選擇該進(jìn)程殺死;該進(jìn)程正在執(zhí)行 ext4_sync_file(),在 jbd2_log_wait_commit() 等待進(jìn)程 2 的 Transaction 完成;此時(shí)該進(jìn)程處于不可中斷狀態(tài),無(wú)法被 Kill,內(nèi)存無(wú)法被釋放;
  • 進(jìn)程 2,處于 ext4_create()(或者其他 ext4_xxx 函數(shù))中,在 __getblk() 上等待可用的內(nèi)存;只有拿到足夠內(nèi)存,才能完成 ext4_journal_stop(),將 t_updates 遞減,其對(duì)應(yīng)的內(nèi)核 Journal 線程才能完成 jbd2_journal_commit_transaction();
  • (死鎖條件 1)進(jìn)程 1 在等待進(jìn)程 2 的 Transaction Commit 完成才能被 Kill 而釋放內(nèi)存;(死鎖條件 2)進(jìn)程 2 在等待進(jìn)程 1 的釋放內(nèi)存才能拿到內(nèi)存完成 Transaction Commit。

解決方案

在上述兩個(gè)死鎖條件中,只要我們破壞一個(gè)條件就可以避免死鎖發(fā)生。條件 1 是 jbd2 的實(shí)現(xiàn)要求,我們難以改變;條件 2 則有可能打破,例如,針對(duì)這種關(guān)鍵路徑上的內(nèi)存分配,允許 cgroup 的內(nèi)存暫時(shí)超過(guò)限制。

Linux VMM(Virtual Memory Manager)提供了 Get Free Page(GFP)Flags 來(lái)控制分配器的分配、釋放行為。其中有一個(gè) Flag 名為 __GFP_NOFAIL,在 2.6 內(nèi)核版本加入,其作用是 “This flag is used by a caller to indicate that the allocation should never fail and the allocator should keep trying to allocate indefinitely”。根據(jù)代碼確認(rèn),如果分配內(nèi)存時(shí) gfp_mask 攜帶了 __GFP_NOFAIL,那么 VMM 會(huì)盡量滿足分配需求,不再理會(huì) cgroup 的內(nèi)存限制。更詳細(xì)的 GFP 說(shuō)明請(qǐng)參考 Understanding the Linux Virtual Memory Manager: 6.4 ?Get Free Page (GFP) Flags(https://www.kernel.org/doc/gorman/html/understand/understand009.html#Sec:%20GFP%20Flags)。

我們看一下 __getblk() 的函數(shù)調(diào)用鏈,關(guān)鍵的函數(shù)如下,

__getblk
->__getblk_slow
->grow_buffers
->grow_dev_page
->find_or_create_page
->add_to_page_cache_lru
->__add_to_page_cache_locked
-> mem_cgroup_cache_charge
-> ...
->__mem_cgroup_try_charge

如下代碼所示,grow_dev_page() 在調(diào)用 find_or_create_page() 之前已經(jīng)攜帶了 __GFP_NOFAIL,按理來(lái)說(shuō),后續(xù)的內(nèi)存分配就不會(huì)受到 cgroup 限制了(__mem_cgroup_try_charge() )。但為什么 __getblk() 還是沒(méi)有成功呢?

static int
grow_dev_page(struct block_device *bdev, sector_t block,
pgoff_t index, int size, int sizebits)
{
...

gfp_mask = mapping_gfp_mask(inode->i_mapping) & ~__GFP_FS;
gfp_mask |= __GFP_MOVABLE;
/*
* XXX: __getblk_slow() can not really deal with failure and
* will endlessly loop on improvised global reclaim. Prefer
* looping in the allocator rather than here, at least that
* code knows what it's doing.
*/
gfp_mask |= __GFP_NOFAIL;

page = find_or_create_page(inode->i_mapping, index, gfp_mask);
if (!page)
return ret;

__GFP_NOFAIL 丟失

上文提到,3.10.0-862.el7.centos 是引入該問(wèn)題的版本。我們對(duì)照 3.10.0-693.el7.centos 和 3.10.0-862.el7.centos 兩個(gè)版本在上述路徑上的代碼差異,發(fā)現(xiàn)后者在函數(shù) __add_to_page_cache_locked() 中,增加了以下代碼。mapping_gfp_constraint() 會(huì)將傳遞的 gfp_mask 與 inode->i_mapping 支持的 flags 進(jìn)行按位與操作。根據(jù) vmcore 分析,確認(rèn) inode->i_mapping 是不支持 __GFP_NOFAIL 的,因此上層傳遞的 __GFP_NOFAIL 丟失了!

static int __add_to_page_cache_locked(struct page *page,
struct address_space *mapping,
pgoff_t offset, gfp_t gfp_mask,
void **shadowp)
{
...
gfp_mask = mapping_gfp_constraint(mapping, gfp_mask);

error = mem_cgroup_cache_charge(page, current->mm,
gfp_mask & GFP_RECLAIM_MASK);
...

在 Linux 的主線代碼歷史中并沒(méi)有在該位置添加過(guò)這行代碼。我們傾向于認(rèn)為是 RedHat 的維護(hù)者在移植以下 patch 出錯(cuò)了,mapping_gfp_constraint() 應(yīng)該針對(duì)部分操作作為 add_to_page_cache_lru() 的參數(shù)傳遞,而不是在隨后的 __add_to_page_cache_locked() 無(wú)條件執(zhí)行。

c20cd45eb017 mm: allow GFP_{FS,IO} for page_cache_read page cache allocation
c62d25556be6 mm, fs: introduce mapping_gfp_constraint()
6afdb859b710 mm: do not ignore mapping_gfp_mask in page cache allocation paths
diff --git a/mm/filemap.c b/mm/filemap.c
index 6ef3674c0763..1bb007624b53 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -1722,7 +1722,7 @@ static ssize_t do_generic_file_read(struct file *filp, loff_t *ppos,
goto out;
}
error = add_to_page_cache_lru(page, mapping, index,
- GFP_KERNEL & mapping_gfp_mask(mapping));
+ mapping_gfp_constraint(mapping, GFP_KERNEL));
if (error) {
page_cache_release(page);
if (error == -EEXIST) {
@@ -1824,7 +1824,7 @@ static int page_cache_read(struct file *file, pgoff_t offset)
return -ENOMEM;

ret = add_to_page_cache_lru(page, mapping, offset,
- GFP_KERNEL & mapping_gfp_mask(mapping));
+ mapping_gfp_constraint(mapping, GFP_KERNEL));
if (ret == 0)
ret = mapping->a_ops->readpage(file, page);
else if (ret == -EEXIST)

一個(gè)比較保險(xiǎn)的修復(fù)方法是,在 __add_to_page_cache_locked() 中判斷上層是否傳入 __GFP_NOFAIL,在 mapping_gfp_constraint() 之后把 __GFP_NOFAIL 加回來(lái)。相比直接刪掉 mapping_gfp_constraint(),這種改法不需要修改所有 add_to_page_cache_lru() 的 caller,盡量保持原有的邏輯。

保證 grow_dev_page() 始終使用 __GFP_NOFAIL

在主線代碼中,我們發(fā)現(xiàn)下面三個(gè) patch 加強(qiáng)了 grow_dev_page(),保證在所有場(chǎng)景中 grow_dev_page() 都使用 __GFP_NOFAIL。

bc48f001de12 buffer: eliminate the need to call free_more_memory() in getblk_slow()
94dc24c0c59a buffer: grow_dev_page() should use GFP_NOFAIL for all cases
640ab98fb362 buffer: have alloc_page_buffers() use __GFP_NOFAIL
commit 94dc24c0c59a224a093f110060d01c2c620f275a
Author: Jens Axboe
Date: Wed Sep 27 05:45:36 2017 -0600

buffer: grow_dev_page() should use __GFP_NOFAIL for all cases

We currently use it for find_or_create_page(), which means that it
cannot fail. Ensure we also pass in 'retry == true' to
alloc_page_buffers(), which also ensure that it cannot fail.

After this, there are no failure cases in grow_dev_page() that
occur because of a failed memory allocation.

最終修復(fù)方案

帶上上述兩方面修復(fù),我們的復(fù)現(xiàn)腳本再也沒(méi)有復(fù)現(xiàn)該問(wèn)題。目前我們已經(jīng)把該問(wèn)題和可能的修復(fù)方式匯報(bào)給 Redhat(https://bugzilla.redhat.com/show_bug.cgi?id=1911392),希望后續(xù)能夠得到官方確認(rèn)的最終修復(fù)方式。

總結(jié)

OOM Killer 是 Linux 提供的一項(xiàng)基本功能,然而這個(gè)場(chǎng)景相對(duì)比較罕見(jiàn),難以保證各個(gè)模塊在內(nèi)存分配時(shí)能夠很好地處理。對(duì)于服務(wù)提供方,一方面,我們需要盡可能保證我們的服務(wù)不會(huì)出現(xiàn)內(nèi)存泄漏或者 OOM,這非常困難卻是值得投入的。另一方面,Linux Kernel 的 OOM 處理也飽受大家詬病,有許多開(kāi)源項(xiàng)目幫助大家在用戶態(tài)做好 OOM 監(jiān)控,例如 facebookincubator/oomd(https://github.com/facebookincubator/oomd) 和 rfjakob/earlyoom(https://github.com/rfjakob/earlyoom),這樣就可以規(guī)避掉 Kernel OOM Killer 潛在的問(wèn)題。

對(duì)文件系統(tǒng)和內(nèi)核感興趣的同學(xué)也歡迎加入我們。


作者介紹

Jiewei,SmartX 研發(fā)工程師。SmartX 擁有國(guó)內(nèi)最頂尖的分布式存儲(chǔ)和超融合架構(gòu)研發(fā)團(tuán)隊(duì),是國(guó)內(nèi)超融合領(lǐng)域的技術(shù)領(lǐng)導(dǎo)者。


瀏覽 98
點(diǎn)贊
評(píng)論
收藏
分享

手機(jī)掃一掃分享

分享
舉報(bào)
評(píng)論
圖片
表情
推薦
點(diǎn)贊
評(píng)論
收藏
分享

手機(jī)掃一掃分享

分享
舉報(bào)

感谢您访问我们的网站,您可能还对以下资源感兴趣:

国产秋霞理论久久久电影-婷婷色九月综合激情丁香-欧美在线观看乱妇视频-精品国avA久久久久久久-国产乱码精品一区二区三区亚洲人-欧美熟妇一区二区三区蜜桃视频 日韩黄色片| 日韩高潮| 火淫玖玖免费精品| www.亚洲天堂| 女人久久久| 欧美一区二区三区免费| 天天干91| 成人H视频| 人妻少妇中文字幕久久牛牛| 国产主播在线观看| 伊人天天干| 婷婷色AV| 日本中文字幕不卡| 精品成人在线视频| 翔田千里无码一区| 夜夜夜操操操| 天天爽夜夜爽精品成人免费| 亚洲有码中文字幕| 强奷伦奷片91| 国产区av| 欧美精品91| 60分钟上大床又黄又爽| 天堂久久久久| 色色综合热| 大香蕉尹人在线视频| 欧美日韩在线观看一区| 国产三级在线观看| 中文字幕亚洲有码| 中文字幕一区二区三区精华液| av资源站| 在线观看污网站| 亚洲综合中文字幕在线| 精品码A片18| 亚洲男同tv| 做爱A片| 精品人妻中文字幕视频| 东京热综合影院| 亚洲中文在线视频| 欧美a视频| 日日夜夜av| 欧美图片小说| 国产精品成人影视| 国内自拍av| 欧美日韩成人在线观看| 性爱AV在线| 91一起草高清资源| 欧美成人精品激情在线观看| 亚洲一卡| 五月婷婷俺也去| 国产精品无码久久久久成人app | 综合色五月| 91蜜臀在线| 香蕉av在线播放| 无码人妻日韩精品一区二区三| 肏屄视频在线观看| 日韩三级小说| 国产午夜成人福利在线| 日韩福利在线观看| 操逼视频在线播放| 躁BBB躁BBB添BBBBBB| 99久| 天天操人妻| 人妻中文在线| 久久国产片| 国产伦精一品二品三品app| 欧美高清无码在线观看| 免费视频久久| 亚洲视频网站在线观看| 国产黄网站| 五月婷婷丁香网| 69国产精品无码免费| 免费人成视频在线| 国产字幕| 婷婷福利导航| 天天躁天干天干| 成人免费A片在线观看直播96| 亚洲AV人人夜夜澡人人| 免费一级AAAAA片在线播放| 国产成人无码精品一区秘二区 | 一区久久| 三级片中文| 亚洲无套内射| 男人天堂手机在线| 人人操人人摸人人爱| 一级无码视频| 亚洲无码成人视频| 欧美特黄AAAAAAAAA片| 臭小子晚上让你爽个够视频| 国产区AV| 黄色影院在线观看| 西西人体大胆ww4444多少集| 人人澡人人添人人爽人人| 亚洲在线视频免费观看| 国产精品a片| 成人A片免费看| 一级a一级a免费观看视频Al明星| 三级成人网| 日韩精品成人电影| 精品视频在线免费观看| 天天干天天日天天干| 日日碰狠狠| 日韩黄色片| 北条麻妃电影九九九| 北条麻妃电影九九九| 女人自慰在线观看| 色久悠悠综合网| 一区二区三区四区日韩| 国产欧美精品一区二区三区| 国产精品嫩草久久久久yw193| 一区二区三区四区无码在线| 在线观看日本黄| 国产AⅤ无码一区二区| 少妇精品久久久久久久久久| 亚洲H| 国产区欧美去区在线| 黄网免费观看| AV在线资源网| 操欧美逼| 在线播放你懂的| 在线免费黄片| AV三级无码| 国产91综合一区在线观看| 午夜AV在线播放| 亚洲一区黄色| 91视频在线观看免费大全| 激情色色| 91人人操人人| 在线观看免费成人网站| 国产深夜福利| 国产激情无码视频| 东京热免费视频| 免费看日P视频| 欧美作爱| 91精品国产乱码久久久久| 精品1区2区| 国产精品综合| 天天日天天日天天干| 99热青青草| 欧美日韩中文字幕在线| 日本黄色电影网址| 性色aV中文字幕| 人妻精品一区二区在线| 中文字幕一区二区三区人妻电影| 99热精品免费观看| 人人免费操| 台湾一区二区| 国产三级片在线免费观看| 一区二区三区在线观看视频| 伊人网站| 六月婷婷五月天| 天堂中文资源在线观看| 无码视频久久| 久色悠悠| 大香蕉性爱| 91久久欧美极品XXXXⅩ| 免费AV成人| 亚洲日韩网站在线观看| 久久av影院| 性福利导航| 国产办公室丝袜人妖| 久久国产性爱| www.亚洲天堂| 中国一级黄色A片| 97色碰| 欧美一区免费| 成人电影无码| 无码AV大香线蕉伊人| 久久久久久网站| 黄频免费观看| 69激情网| 操B视频免费看| 一级免费A片| 东北女人操逼| 成人视频免费观看18| 操逼第二页| 国产精品美女久久久久久久久| 婷婷五月天性爱| 国产精品无码av| 无码视频在线观看免费| 中文无码在线| 大香蕉在线视频观看| 中韩日美免费看的电影| 成人福利午夜A片公司| 狼友在线观看| 三级久久| 蜜桃传媒AV| 青青草免费在线观看| 亚洲第一色图| 亚洲小视频在线观看| 无码一区精品久久久成人| 天天澡天天爽日日AV| 久久久人妻无码精品蜜桃| 北岛玲丝袜办公室高跟| 人人看人人搂人人摸| 翔田AV无码秘三区| 欧美性爱在线观看| 安徽妇搡BBBB搡BBB| 超碰在线图片| 在线无码不卡| 日本成人视频| 色中文| 成人天堂一区二区三区| 夜夜爽妓女77777毛片A片| 五月婷婷丁香综合| 中文字幕无码在线观看| 中文字幕综合在线| 成人做爰黄A片免费看| Av一区二区三区| 天天天天天天干| 免费AV网站观看| 无码三级av| 人人射在线| 日韩久久网站| 免费色网站| 久久亚洲成人| 久久夜色视频网| 色婷婷香蕉在线一区二区| 成人无码精品亚洲| 无码偷拍| 久久精品熟妇丰满人妻99| 久久免费操| 亚州不卡| 操逼中文字幕| 一级特黄大片录像i| 激情婷婷 | 久久久久久免费| 2025精品视频| 不卡二区| 瘦精品无码一区二区三区四区五区六区七区八区 | 中文大香蕉视频| 黄色国产视频在线观看| AV网站在线免费观看| 久久午夜无码鲁丝片午夜精| 亚洲狠狠| 老熟女网站| 国产粉嫩在线观看| 国产成人AV在线播放| 国产av毛片| 国产日韩精品无码去免费专区国产| 欧美一级夜夜爽| 国产成人综合电影| 台湾成人视频| 久久偷拍视频| 无码AV电影在线观看| 五月天婷婷丁香综合视频| 波多野结衣无码视频在线观看| 一区二区高清无码视频| 国产在线观看91| 国产中文字幕在线视频| 五月天久久久久久| 在线天堂999| 亚洲视频天堂| 国产成人精品a视频| 国产一级二级三级片| 国产精品a片| 91愛爱| 91av在线播放| 中文字幕免费在线看一区七区| 中文字幕在线观看1| 久久系列观看完整指南| 亚洲无码久久精品| 97人妻天天摸天天爽天天| 人妻少妇一区二区三区| 欧美日韩狠狠操在线观看视频| 久久久精品| 成人动漫免费观看| 亚洲欧美日韩色图| 欧美日韩久久| 黄色大片视频| 日韩色综合| 午夜性爽视频男人的天堂| 免费视频亚洲| 一级AA视频| 亚洲秘无码一区二区三区蜜桃中文| 日韩无码电影网| 亚洲成人一区二区三区| 国产人妻人伦精品1国产丝袜| 国产理论片在线观看| 国产人妖视频| 伊人免费视频在线观看| 偷拍欧美日韩| 天堂在线社区| 又色又爽| 爱搞搞就搞搞| 中国老少配BBwBBwBBW| 喷潮视频| 日本三级网站| 在线观看日韩欧美| 精品人妻一区二区三区-国产精品| 大香蕉伊人影视| 久久久精品午夜人成欧洲亚洲韩国| 人人看人人摸人人操| 欧美最猛黑A片黑人猛交蜜桃视频 色噜噜狠狠一区二区三区300部 | 一区二区三区无码精品| 一级黄色电影免费看| 亚洲图片在线播放| 国产黄网站| 一区二区三区成人| 91欧美精品成人综合在线观看| 亚洲AV电影网| 欧美在线观看一区二区| 日韩高清无码网站| 超碰免费在线| 97超碰资源站| 国产午夜视频在线观看| 欧美a片在线看| 国产精品欧美精品| 午夜探花视频| 污污污污污www网站免费观看| 成人三区| 欧美精品不卡| 在线观看黄色电影| 51成人精品午夜福利| 亚洲无码精品久久| 蜜臀99| 国产视频第一页| 超碰91在线| 国产精品av在线播放| 成人香蕉网| 亚州不卡| 最新中文字幕在线视频| 精品交换一区二区三区无码| 狠狠干中文字幕| 2025中文在线观看最好看的电影 | 免费看欧美成人A片| 谁有毛片网站| 黄片在线免费观看| 亚洲无码专区视频| 亚洲av电影在线观看| av一区在线观看| 欧美A√| 日本黄色大片网站| 国产艹逼视频| 福利视频免费观看| 西西444www无码精品| 99在线视频播放| 日韩成人AV在线| 情趣视频网站| 亚洲成人影片在线观看| 亚洲AV五月天在线| 中文在线字幕免费观| 亚洲黄色电影| 一区在线免费观看| 国产成人一区二区三区A片免费| 国产一级片电影| 天天日夜夜爽| 黄色A片免费观看| 五月丁香婷婷综合| 91A视频| 亚洲视屏| 日本一级黄色A片| 波多野结衣无码在线| 九一成人电影| 久久亚洲视频| 亚洲无码性爱视频| 东京热在线观看| 做爰视频毛片下载蜜桃视频| 中文字幕激情精品| 亚洲天堂高清| 亚洲无码久久飞鱼网站| 免费肏逼视频| 18sav| 日本操b| 国产高清无码免费视频| 亚洲欧美国产日韩字幕| av色欲| 国产精品久久久久久久久久九秃 | 国产乱在线| 玖玖成人电影| 水果派解说AV无码一区| 日韩无码一区二区三区四区| 青青无码视频| 亚洲色五月| 黄片视频在线免费看| 久久女人| 五月婷在线观看| 躁BBB躁BBB躁BBBBBB日| 撒尿BBw搡BBwBBw| 中文字幕乱码视频32| 精品成人一区二区三区| 色婷婷五月激情| A片视频免费看| 婷婷热| 久操国产| 国产人体视频| 嫩BBB嗓BBBB榛BBBB| 国产乱伦内射| 黄片91| 色欲av伊人久久大香线蕉影院| 久久久午夜| 午夜在线观看视频18| 日韩欧美大片在线观看| 久久h| 亚洲自慰| 国产做受精品网站在线观看| 午夜成人AV| 米奇色色色| 久久99久久99久久99国内少妇精品 | 999一区二区三区| 大香蕉伊人青青草| 亚洲无码A片在线观看| 欧美黄色免费在线观看| 天天日天天干天天操| 骚BBBB槡BBB槡BBB| 少妇人妻无码| 男人天堂资源网| 亚洲a在线观看| 青青草黄色视频| 日逼视频免费看| av在线资源播放| youjizzcom日本| 无码人妻精品一区二区三区蜜桃91| 在线天堂v| 伊人999| 色老板综合| jizzjizz国产| 蜜臀av在线免费观看| 大鸡巴午夜爽视频电影| 中文电视剧字幕在线播放免费视频| 亚洲午夜精品久久久久久APP| 无码成人av| 中文字幕无码Av在线| 日韩一二三| 午夜福利视频3000| 天美果冻麻豆国产一区| 国产乱伦对白| 俺也去射| 丰满欧美熟妇免费视频| 国产熟女av| 西西掰穴| 乖我硬了让老子cao你小视频| 高清无码高潮| 国产精品操逼视频| 亚洲男女免费视频| 欧美午夜精品久久久| 三级黄色毛片| 人妻丰满熟妇av无码| 国产黄色网| 午夜麻豆| 久在草| 福利导航网| 在桌下含她的花蒂和舌头H视频 | 国产小毛片| 狠狠干在线视频| 在线免费看黄视频| 中文字幕乱码视频32| av资源在线播放| 色婷婷一二三精品A片| 色婷婷av| 亚洲人妻无码视频| 黄色免费视频网站| 97精品| 夜夜爽妓女77777毛片A片| 91N视频| 日韩欧美成人电影| 国产成人影视在线观看| 亚洲无码电影视频| 成人日韩欧美| 无码导航| 青草成人在线视频| 亚洲成人免费网站| 操操操无码| 精品少妇3p| 亚洲视频国产| 台湾久久| 操你啦青青草| 91成人精品视频| 91超碰在线播放| 91麻豆天美传媒在线| 天天做天天爱天天爽| 99国产在线观看免费视频| 国产日本在线视频| 91麻豆精品传媒国产| 高清无码在线视频观看| 伊人丁香| 日本免费黄色电影| 天干夜天干天天天爽视频| 中文字幕97| 国产无码久久| 五月婷婷五月| 欧美老女人的逼| 波多野结衣AV在线| 欧美一区二区三区精品| wwwxx国产| 屌国产精品| 另类老妇videos另类| www.199麻豆在线观看网站| 一区二区三区视屏| 伊人大香蕉久久| 操操av| 欧美三级不卡| 一本在线| www.黄色片| 91看片看婬黄大片Videos| 亚洲影院中文字幕| 亚洲天堂2017| 中文字幕35页| 欧一美一婬一伦一区二区三区自慰国 | 欧美色图综合网| 91福利网址| 亚洲欧美国产另类| 大肉大捧一进一出两腿| 色色A| 成人免费爱爱视频| 国产在线不卡年轻点的| 一级黄色a片| 国产视频激情| 水果派红桃AV解说| 俺去俺来也在线www色官网| 免费看黄片的网站| 大香蕉98| 大香蕉伊人青青草| 亚洲日韩精品秘在线观看| 成人性爱在线| 五月婷视频| 影音先锋女人资源| 波多野结衣黄色| 一级A黄片| 亚洲GV成人无码久久精品| 日本少妇激情视频| 亚洲精品一区二区三区在线观看| 亚洲无码一区二区三区| 天天天天天天天操| 一本色道久久综合狠狠躁| 国产无码专区| 一本色道久久综合无码人妻| 日韩天堂在线观看| 91在线无码精品入口电车| 91大铭哥| 中文字幕一区二区三区四虎在线| av第一页| 婷婷中文字幕亚洲| 亚洲黄色视频免费| 黄色操逼网站| 成人激情在线观看| 操精品| 围内精品久久久久久久久白丝制服| 欧美打炮网| 国产精品无码无套在线| 高清AV在线| 激情综合视频| 影音先锋麻豆传媒| 久久手机电影| 91在线无码| 国产成人精品123区免费视频| 久操资源站| 久久亚洲AV无码午夜麻豆| 国产91视频| 亚洲h| 亚洲AV偷拍| 91丨国产丨白浆| 91视频大全| 白浆四溢av| 狠狠操在线视频| 成人无码交配视频国产网站 | 亚洲天堂在线视频| 日本黄色三级视频| 能看的av| 男人天堂视频网站| 三须三级久久三级久久18| 丁香五月激情中文字幕| 女人毛片| 中文字幕国产在线| 裸体美女视频欧美18| 欧美少妇做爱| 亚洲日韩AV电影| 中国无码专区| 久艹视频在线观看| 91天堂网| 高清无码视频观看| 免费无码网站| 精品秘一区性综合三区| 吴梦梦一区二区三区| 国产AV影院| 超碰手机在线| 亚洲AV无码乱码精| 亚洲欧美在线免费观看| 天天综合天天| 日韩欧美中文字幕视频| 日韩在线欧美在线| 日韩不卡在线| 黄色av天堂| 秋霞中文字幕| 女生操逼网站| 超碰福利导航| 99re99热| 国产污视频| 国产精品18进进出出17c| 国产自偷自拍| 操大香蕉| 一级欧美一级日韩片| 西西444WWW无码大胆知乎| 91天天看| 日韩在线视频第一页| 国产精品久久久久的角色| 国产激情欧洲在线观看一区二区三区| 亚洲成人免费| 安徽妇搡BBB搡BBBB户外老太太 | 99在线观看视频在线高清| 久久久亚洲AV无码精品色午夜| 91超碰在线观看| 爽好紧别夹喷水网站| 久久久无码精品亚洲| 亚洲欧美国产视频| AV先锋资源| 亚洲青青| 国产在线一区二区三区四区| 97性爱视频| 久久久久久久三级片| 精品人妻一区二区三区四区不卡在| 国产夫妻在线| 西西444WWW大胆无视频软件亮点 | 国产在线视频91| 国产女人18毛片水真多成人如厕| 人妻HDHDHD96XXXX| 午夜视频18| v天堂在线观看| 国产免费一区二区三区四区| 天堂A片电影网站在线观看| 欧美激情一区| 麻豆免费成人传媒| 国产成人精品视频免费| 日韩免费一级| 授乳奶水x88MAV| 激情伊人| 中文字幕手机在线视频| 亚洲日韩在线免费观看| 黑人亚洲娇小videos∞| 色999亚洲人成色| 日本视频在线免费| 国产精品探花熟女| 国产乱国产乱老熟300视频| 制服毛片| 天天搞天天曰在线观看| 99精品热| 蜜桃av秘无码一区三区四| 日韩欧美在线视频观看| 蜜芽av在线| 日本欧美在线观看| 国内成人AV| 日本高清无码在线观看| 日韩天堂网| 丁香五月天av| av在线资源播放| 内射老太太| 欧美日韩色情| 欧美亚洲成人在线观看| 国产无码影视| 婷婷中文字幕亚洲| 国产黄色Av| 国产精品久久7777777精品无码 | 少妇高潮av久久久久久| 中文字幕中文字幕一区| 底流量AV电影在线| 91视频美女模特| 曰本中文字幕在线视频| 国产黄色视频在线观看| www.亚洲无码| 国产精品毛片| 婷婷激情五月| eeuss| 9I看片成人免费视频| jizz亚洲| 中字无码| 无码天堂| 亚洲综合伊人| av在线资源| 无码一区精品久久久成人| 成人性生活A级毛片网站| aa无码视频| 奇米成人片| 不卡无码中文字幕一区| 天堂AV色| 五月伊人婷婷| 玖玖爱资源站| AAA片网站| 无码人妻丰满熟妇精品区| 在线观看毛片网站| 久久草草热国产精| 亚洲国产av一区| 欧美A级黄片| 蜜桃无码一区| 国产精品国产三级国产专区52| 瑟瑟免费视频| 亚洲一区二区视频在线观看| 中文字幕永久在线视频| 国产又爽又黄免费观看| 在线中文字幕亚洲| 久久精品波多野结衣| 东北操逼视频| 97超碰资源总站| 欧美久久性爱| 国产黄色一级| 思思热视频在线观看| 成人动漫一区二区| 九九黄片| 伊人久久电影| 黄片网站视频| 亚洲WWW| 婷婷五月综合久久中文字幕| 91香蕉视频在线| 淫色人妻网| 亚洲黄色视频免费| 国产激情内射| 淫色AV| 天天日日日干| 国产白丝精品91爽爽久久| 在线天堂999| 欧美一级久久| 麻豆传媒在线播放| 张柏芝BBw搡BBBB槡BBBBHDfree | av在线资源观看| 久久久成人免费电影| 久久久久久久免费视频| 国产第一页在线| 黄色电影免费在线观看| 91在线观看| 黄色亚洲无码| 久久综合加勒比| 国产天堂av| 久久精品视频国产| 国产精品你懂得| 日皮视频在线免费观看| 在线黄色av| 蜜桃av秘无码一区二区三欧| 国产一二三四区| 欧美老妇另类| 免费在线观看AV| 日韩美女毛片| 国产一级a毛片| 亚洲三级视频在线播出| 翔田千里在线观看| 成人性爱视频网站| 91视频在线免费看| 日韩小视频在线观看| 欧美日韩高清在线| 手机AV在线| 免费一级婬片AA片观看| 日韩AV一区二区在线观看| 久草网在线观看| 成人先锋影音| 少妇婷婷| 在线天堂av| 超碰精品在线| 山东熟妇搡BBBB搡BBBB| 健身房被教练3p喷水了| 无码做爰欢H肉动漫网站在线看| 婷婷五月综合激情| 精品美女视频| 热久在线| 性久久久久久久| 黄网在线看| 激情麻豆论坛| 国产高清精品在线| 超碰综合| 亚洲秘av无码一区二区| av黄页| 中文字幕一区二区三区四虎在线| 在线无码免费| 亚洲无码视频在线观看高清| 久久三级片电影| 亚洲在线一区二区| a一级黄片| 壁特壁视频在线观看| 操b在线| 牛牛av| 超碰在线网| 翔田千里无码一区| 狠狠色五月亚洲91| 狠狠操狠狠操狠狠操| 四虎黄色| 韩国高清无码| 精品伊人大香蕉| 一区二区三区在线视频观看| 成人网址大全| 人人爽人人爱| 91人人澡人人爽人人看| 中文字幕免费在线看一区七区 | 俺也干| 五月丁香六月| 青青草99热| 国产a片免费观看| 精品无码人妻一区二区媚黑| 国产精品无码7777777| 99热精品在线播放| 永井玛丽亚av无码中出流出| 中文字幕精品一区久久久久| 青娱乐成人电影| 亚洲中文久久| 在线黄| 婷婷大香蕉| 欧美一级电影| 成人五区| 亚洲国产熟妇无码日韩| 精品无码秘人妻一区二区三区| 伊人网站| 亚洲成人77777| 影音先锋在线视频观看| 蜜桃av无码一区二区三区| 免费黄色一级片| 丁香婷婷色五月激情综合三级三级片欧美日韩国 | 内射视频网站| 亚洲欧洲视频在线观看| 亚洲人成人无码.www粉色| 三区在线| 青青草91| 2025最新偷拍| 久草大香蕉在线| 免费一级大片| 免费毛片网址| 黄色毛片,男人天堂| 三级片网页| 欧美人操逼视频| 日韩欧美午夜成人无码| 国产在线小视频| 94精品人人人| 亚洲高清视频免费| 中文国产字幕| 欧美成人午夜无码A片秀色直播| 色哟哟一区二区三区四区| 国产成人精品免费看视频| 亚洲三级电影| 婷婷五月天视频| 欧美熟女内射| 四川美人搡BBw搡BBw| 中文字幕人成人乱码亚洲电影| AV中文在线| 日韩国产综合| 欧美成年人视频| 天天日天天射天天干| 人妻电影亚洲av| 天天日天天噜| a片视频网站| 亚洲免费专区| 无码日韩av| 天天色色天天| 久久久久逼| www.日韩| 国产日韩欧美久久| 国产多人搡BBBB槡BBBB | 亚洲国产精品成人综合色五月| 99视频在线免费播放| 欧美一级在线免费观看| 日韩人妻一区二区三区| 91色色网| 特级西西西88大胆无码| av怡红院| 亚洲成人视频在线观看| 精品秘一区性综合三区| 国产成人无码精品久在线观看| 蜜桃av秘无码一区二区三区| 亚洲中文字幕播放| 欧美日韩在线视频一区| 97人妻一区二区三区| 久久久久久久久久久久久自慰小片 | 久操免费在线| 中文熟妇| 国产精品无码AV| 一级大毛片| 特一级A片| 日本親子亂子倫XXXX50路| 无码网址| 自慰一区二区| 精品国产污污免费网站入口| 国产九色91回来了| 亚洲无码免费在线视频| 最新无码在线| 国产高清在线观看| 天天干少妇| 91青青草在线| 亚洲一级黄| 亚洲超级高清无码第一在线视频观看 | 日韩熟妇人妻中文字幕| 亚洲AV偷拍| 日本高清无码| av无码不卡| 最新日韩无码| 国产AV高清| 免费视频亚洲| 看看AV| 人妻中文字幕网| 一区二区三区不卡在线| 青青青草视频| 四川少妇搡bbbb搡bbbb| 国产97在线视频| 高清无码视频在线免费观看| 乱子伦一区二区三区视频在线观看| 黄色视频免费在线观看网站| 香蕉久久a毛片| 亚洲欧洲在线播放| 日韩精品人妻中文字幕有| 欧美性爱中文字幕| 青草视频在线观看免费| 亚洲一区二区AV| 人人插人人| 人人摸人人操人人爱| 天天干天天舔| 欧美国产日韩另类| 国产熟女av| 超碰首页|