3atv精品不卡视频,97人人超碰国产精品最新,中文字幕av一区二区三区人妻少妇,久久久精品波多野结衣,日韩一区二区三区精品

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

arm Linux 中断管理机制

發布時間:2024/10/14 linux 64 豆豆
生活随笔 收集整理的這篇文章主要介紹了 arm Linux 中断管理机制 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

關鍵詞:GIC、IAR、EOI、SGI/PPI/SPI、中斷映射、中斷異常向量、中斷上下文、內核中斷線程、中斷注冊。

1.1 ARM支持中斷類型

ARM GIC-v2支持三種類型的中斷:

SGI:軟件觸發中斷(Software Generated Interrupt),通常用于多核間通訊,最多支持16個SGI中斷,硬件中斷號從ID0~ID15。SGI通常在Linux內核中被用作IPI中斷(inter-processor interrupts),并會送達到系統指定的CPU上。

PPI:私有外設中斷(Private Peripheral Interrupt),是每個CPU私有的中斷。最多支持16個PPI中斷,硬件中斷號從ID16~ID31。PPI通常會送達到指定的CPU上,應用場景有CPU本地時鐘。

SPI:公用外設中斷(Shared Peripheral Interrupt),最多可以支持988個外設中斷,硬件中斷號從ID32~ID1019。

1.2 GIC檢測中斷流程

GIC(Generic Interrupt Controller,通用中斷控制器)主要由兩部分組成,分別是仲裁單元(Distributor)和CPU接口模塊。

GIC仲裁單元為每一個中斷維護一個狀態機,分別是:inactive、pending、active and pending、active。

下面是來自IHI0048B GIC-V2規格書3.2.4 Interrupt handling state machine截圖:

?

GIC檢測中斷流程如下:

(1) 當GIC檢測到一個中斷發生時,會將該中斷標記為pending狀態(A1)。

(2) 對處于pending狀態的中斷,仲裁單元回確定目標CPU,將中斷請求發送到這個CPU上。

(3) 對于每個CPU,仲裁單元會從眾多pending狀態的中斷中選擇一個優先級最高的中斷,發送到目標CPU的CPU Interface模塊上。

(4) CPU Interface會決定這個中斷是否可以發送給CPU。如果該終端優先級滿足要求,GIC會發生一個中斷信號給該CPU。

(5) 當一個CPU進入中斷異常后,會去讀取GICC_IAR寄存器來響應該中斷(一般是Linux內核的中斷處理程序來讀寄存器)。寄存器會返回硬件中斷號(hardware interrupt ID),對于SGI中斷來說是返回源CPU的ID。

當GIC感知到軟件讀取了該寄存器后,又分為如下情況:

* 如果該中斷源是pending狀態,那么轉改將變成active。(C)

* 如果該中斷又重新產生,那么pending狀態變成active and pending。(D)

* 如果該中斷是active狀態,現在變成active and pending。(A2)

(6) 當處理器完成中斷服務,必須發送一個完成信號EOI(End Of Interrupt)給GIC控制器。軟件寫GICC_EOIR寄存器,狀態變成inactive。(E1)

補充:

(7) 對于level triggered類型中斷來說,當觸發電平消失,狀態從active and pending變成active。(B2)

常用路徑是A1->D->B2->E1。

1.2.1 GIC中斷搶占

GIC中斷控制器支持中斷優先級搶占,一個高優先級中斷可以搶占一個低優先級且處于active狀態的中斷,即GIC仲裁單元會記錄和比較當前優先級最高的pending狀態,然后去搶占當前中斷,并且發送這個最高優先級的中斷請求給CPU,CPU應答了高優先級中斷,暫停低優先級中斷服務,進而去處理高優先級中斷。

GIC會將pending狀態優先級最高的中斷請求發送給CPU。

1.2.2 Linux對中斷搶占處理

從GIC角度看,GIC會發送高優先級中斷請求給CPU。

但是目前CPU處于關中斷狀態,需要等低優先級中斷處理完畢,直到發送EOI給GIC

然后CPU才會響應pending狀態中優先級最高的中斷進行處理。

所以Linux下:

1. 高優先級中斷無法搶占正在執行的低優先級中斷。

2.同處于pending狀態的中斷,優先響應高優先級中斷進行處理。

1.3 GIC中斷時序

?

借助GIC-400 Figure B-1?Signaling physical interrupts 理解GIC內部工作原理。

M和N都是SPI類型的外設中斷,且通過FIQ來處理,高電平觸發,N的優先級比M高,他們的目標CPU相同。

(1) T1時刻:GIC的總裁單元檢測到中斷M的電平變化。

(2) T2時刻:仲裁單元設置中斷M的狀態為pending。

(3) T17時刻:CPU Interface模塊會拉低nFIQCPU[n]信號。在中斷M的狀態變成pending后,大概需要15個時鐘周期后會拉低nFIQCPU[n]信號來向CPU報告中斷請求(assertion)。仲裁單元需要這些時間來計算哪個是pending狀態下優先級最高的中斷。

(4) T42時刻:仲裁單元檢測到另外一個優先級更高的中斷N。

(5) T43時刻:仲裁單元用中斷N替換中斷M為當前pending狀態下優先級最高的中斷,并設置中斷N為pending狀態。

(6) T58時刻:經過tph個時鐘后,CPU Interface拉低你FIOCPU[n]信號來通知CPU。因為此信號在T17時刻已經被拉低,CPU Interface模塊會更新GICC_IAR寄存器的Interrupt ID域,該域的值變成中斷N的硬件中斷號。

(7) T61~T131時刻:Linux對中斷N的服務程序--------------------------------------------------------------中斷服務程序處理段,從GICC_IAR開始到GICC_EOIR結束。

  T61時刻:CPU(Linux中斷服務例程)讀取GICC_IAR寄存器,即軟件響應了中斷N。這時仲裁單元把中斷N的狀態從pending變成active and pending。讀取GICC_IAR

  T64時刻:在中斷N被Linux相應3個時鐘內,CPU Interface模塊完成對nFIQCPU[n]信號的deasserts,即拉高nFIQCPU[n]信號。

  T126時刻:外設也deassert了該中斷N。

  T128時刻:仲裁單元移出了中斷N的pending狀態。

  T131時刻:Linux服務程序把中斷N的硬件ID號寫入GICC_EOIR寄存器來完成中斷N的全部處理過程。寫GICC_EOIR

(8) T146時刻:在向GICC_EOIR寄存器寫入中斷N中斷號后的tph個時鐘后,仲裁單元會選擇下一個最高優先級中斷,即中斷M,發送中斷請求給CPU Interface模塊。CPU Interface會拉低nFIQCPU[n]信號來向CPU報告外設M的中斷請求。

(9) T211時刻:Linux中斷服務程序讀取GICC_IAR寄存器來響應中斷,仲裁單元設置中斷M的狀態為active and pending。

(10) T214時刻:在CPU響應中斷后的3個時鐘內,CPU Interface模塊拉高nFIOCPU[n]信號來完成deassert動作。

那么GICC_IAR和GICC_EOIR分別在Linux什么地方觸發的呢?

1.4 Cortex A15 A7實例

2. 硬件中斷號和Linux中斷號的映射

2.1 硬件中斷號:一個串口中斷實例

2.2 中斷控制器初始化

DTS中GIC定義于arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts:

gic: interrupt-controller@2c001000 {compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";------------------此設備的標識符是"arm,cortex-a15-gic"#interrupt-cells = <3>;#address-cells = <0>;interrupt-controller;----------------------------------------------------表示此設備是一個中斷控制器reg = <0 0x2c001000 0 0x1000>,<0 0x2c002000 0 0x1000>,<0 0x2c004000 0 0x2000>,<0 0x2c006000 0 0x2000>;interrupts = <1 9 0xf04>;};

struct irq_domain用于描述一個中斷控制器。

GIC中斷控制器在初始化時解析DTS信息中定義了幾個GIC控制器,每個GIC控制器注冊一個struct irq_domain數據結構。

struct irq_domain {struct list_head link;-------------------------用于將irq_domain連接到全局鏈表irq_domain_list中。const char *name;------------------------------中斷控制器名稱const struct irq_domain_ops *ops;--------------irq domain映射操作使用的方法集合void *host_data;unsigned int flags;/* Optional data */struct device_node *of_node;------------------對應中斷控制器的device nodestruct irq_domain_chip_generic *gc; #ifdef CONFIG_IRQ_DOMAIN_HIERARCHYstruct irq_domain *parent; #endif/* reverse map data. The linear map gets appended to the irq_domain */irq_hw_number_t hwirq_max;--------------------該irq domain支持中斷數量的最大值。unsigned int revmap_direct_max_irq;unsigned int revmap_size;---------------------線性映射的大小struct radix_tree_root revmap_tree;-----------Radix Tree映射的根節點unsigned int linear_revmap[];-----------------線性映射用到的lookup table }

struct irq_domain_ops定義了irq_domain方法集合,xlate從intspec中解析出硬件中斷號和中斷類型,intspec[0]和intspec[1]決定中斷號,intspec[2]決定中斷類型。

struct irq_domain_ops {int (*match)(struct irq_domain *d, struct device_node *node);int (*map)(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw);void (*unmap)(struct irq_domain *d, unsigned int virq);int (*xlate)(struct irq_domain *d, struct device_node *node,const u32 *intspec, unsigned int intsize,unsigned long *out_hwirq, unsigned int *out_type);#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY/* extended V2 interfaces to support hierarchy irq_domains */int (*alloc)(struct irq_domain *d, unsigned int virq,unsigned int nr_irqs, void *arg);void (*free)(struct irq_domain *d, unsigned int virq,unsigned int nr_irqs);void (*activate)(struct irq_domain *d, struct irq_data *irq_data);void (*deactivate)(struct irq_domain *d, struct irq_data *irq_data); #endif };static const struct irq_domain_ops gic_irq_domain_hierarchy_ops = {.xlate = gic_irq_domain_xlate,.alloc = gic_irq_domain_alloc,.free = irq_domain_free_irqs_top, };static int gic_irq_domain_xlate(struct irq_domain *d,struct device_node *controller,const u32 *intspec, unsigned int intsize,unsigned long *out_hwirq, unsigned int *out_type) { .../* Get the interrupt number and add 16 to skip over SGIs */*out_hwirq = intspec[1] + 16;--------------------------------------首先+16跳過SGI類型中斷/* For SPIs, we need to add 16 more to get the GIC irq ID number */if (!intspec[0]) {-------------------------------------------------如果是SPI類型中斷,還需要+16,跳過PPI類型中斷。ret = gic_routable_irq_domain_ops->xlate(d, controller,intspec,intsize,out_hwirq,out_type);if (IS_ERR_VALUE(ret))return ret;}*out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;---------------------中斷觸發類型,包括四種上升沿、下降沿、高電平、低電平。return ret; }static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,unsigned int nr_irqs, void *arg) {int i, ret;irq_hw_number_t hwirq;unsigned int type = IRQ_TYPE_NONE;struct of_phandle_args *irq_data = arg;ret = gic_irq_domain_xlate(domain, irq_data->np, irq_data->args,irq_data->args_count, &hwirq, &type);---------------首先根據args翻譯出硬件中斷號和中斷類型。if (ret)return ret;for (i = 0; i < nr_irqs; i++)gic_irq_domain_map(domain, virq + i, hwirq + i);---------------執行軟硬件的映射,并且根據中斷類型設置struct irq_desc->handle_irq處理函數。return 0; }void irq_domain_free_irqs_top(struct irq_domain *domain, unsigned int virq,unsigned int nr_irqs) {int i;for (i = 0; i < nr_irqs; i++) {irq_set_handler_data(virq + i, NULL);irq_set_handler(virq + i, NULL);}irq_domain_free_irqs_common(domain, virq, nr_irqs); }

針對SPI類型中斷,需要進行+16位移。?

static int gic_routable_irq_domain_xlate(struct irq_domain *d,struct device_node *controller,const u32 *intspec, unsigned int intsize,unsigned long *out_hwirq,unsigned int *out_type) {*out_hwirq += 16;return 0; }

gic_irq_domain_map()入參有struct irq_domain和軟硬件中斷號,主要分SGI/PPI一組,SPI一組。

主要工作由irq_domain_set_info()處理,irq_domain_set_hwirq_and_chip()通過Linux中斷號獲取struct irq_data數據結構,設置關聯硬件中斷號和struct irq_chip gic_chip關聯。

__irq_set_handler()設置中斷描述符irq_desc->handler_irq回調函數,對SPI類型來說就是handle_fasteoi_irq()。

static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,irq_hw_number_t hw) {if (hw < 32) {irq_set_percpu_devid(irq);-------------------------------PerCPU類型的中斷有自己的特殊flag。irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data,handle_percpu_devid_irq, NULL, NULL);set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);} else {irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data,handle_fasteoi_irq, NULL, NULL);set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);gic_routable_irq_domain_ops->map(d, irq, hw);}return 0; }void irq_domain_set_info(struct irq_domain *domain, unsigned int virq,irq_hw_number_t hwirq, struct irq_chip *chip,void *chip_data, irq_flow_handler_t handler,void *handler_data, const char *handler_name) {irq_domain_set_hwirq_and_chip(domain, virq, hwirq, chip, chip_data);__irq_set_handler(virq, handler, 0, handler_name);irq_set_handler_data(virq, handler_data); }int irq_domain_set_hwirq_and_chip(struct irq_domain *domain, unsigned int virq,irq_hw_number_t hwirq, struct irq_chip *chip,void *chip_data) {struct irq_data *irq_data = irq_domain_get_irq_data(domain, virq);if (!irq_data)return -ENOENT;irq_data->hwirq = hwirq;irq_data->chip = chip ? chip : &no_irq_chip;irq_data->chip_data = chip_data;return 0; }void __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,const char *name) {unsigned long flags;struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0); ...desc->handle_irq = handle;--------------------irq_desc->handler_irq和name賦值。desc->name = name; ... }

drivers/irqchip/irq-gic.c定義了"arm,cortex-a15-gic"的處理函數gic_of_init,gic_of_init是GIC控制器的初始化函數。

IRQCHIP_DECLARE(cortex_a15_gic, "arm,cortex-a15-gic", gic_of_init);static int gic_cnt __initdata;static int __init gic_of_init(struct device_node *node, struct device_node *parent) { ...gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset, node);if (!gic_cnt)gic_init_physaddr(node);if (parent) {irq = irq_of_parse_and_map(node, 0);gic_cascade_irq(gic_cnt, irq);}if (IS_ENABLED(CONFIG_ARM_GIC_V2M))gicv2m_of_init(node, gic_data[gic_cnt].domain);gic_cnt++;return 0; }

gic_init_bases的gic_nr是GIC控制器的序號,主要調用irq_domain_add_linear()分配并函數注冊一個irq_domain。

void __init gic_init_bases(unsigned int gic_nr, int irq_start,void __iomem *dist_base, void __iomem *cpu_base,u32 percpu_offset, struct device_node *node) {irq_hw_number_t hwirq_base;struct gic_chip_data *gic;int gic_irqs, irq_base, i;int nr_routable_irqs;BUG_ON(gic_nr >= MAX_GIC_NR);---------------------------gic_nr不超過系統規定的MAX_GIC_NRgic = &gic_data[gic_nr];--------------------------------struct gic_chip_data類型的全局變量gic_data,序號是GIC控制器序號 ... /** Initialize the CPU interface map to all CPUs.* It will be refined as each CPU probes its ID.*/for (i = 0; i < NR_GIC_CPU_IF; i++)gic_cpu_map[i] = 0xff;/** Find out how many interrupts are supported.* The GIC only supports up to 1020 interrupt sources.*/gic_irqs = readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_CTR) & 0x1f;------------計算GIC控制器最多支持的中斷源個數gic_irqs = (gic_irqs + 1) * 32;if (gic_irqs > 1020)-------------------------------------------------------GIC支持的最大中斷數據,此處為1020gic_irqs = 1020;gic->gic_irqs = gic_irqs;if (node) { /* DT case */const struct irq_domain_ops *ops = &gic_irq_domain_hierarchy_ops;--------------GICv2的struct irq_domain_ops ...gic->domain = irq_domain_add_linear(node, gic_irqs, ops, gic);-----------------注冊irq_domain,操作函數使用gic_irq_domain_hierarchy_ops} else { /* Non-DT case */ ...}if (WARN_ON(!gic->domain))return;if (gic_nr == 0) { #ifdef CONFIG_SMPset_smp_cross_call(gic_raise_softirq);register_cpu_notifier(&gic_cpu_notifier); #endifset_handle_irq(gic_handle_irq);-------在irq_handler中調用handle_arch_irq,這里將handle_arch_irq指向gic_handle_irq,實現了平臺中斷和具體GIC中斷的關聯。}gic_chip.flags |= gic_arch_extn.flags;gic_dist_init(gic);----------------------GIC Distributer部分初始化gic_cpu_init(gic);-----------------------GIC CPU Interface部分初始化gic_pm_init(gic);------------------------GIC PM相關初始化 }

irq_domain_add_linear()->__irq_domain_add()分配并初始化struct irq_domain。

struct irq_domain *__irq_domain_add(struct device_node *of_node, int size,irq_hw_number_t hwirq_max, int direct_max,const struct irq_domain_ops *ops,void *host_data) {struct irq_domain *domain;domain = kzalloc_node(sizeof(*domain) + (sizeof(unsigned int) * size),GFP_KERNEL, of_node_to_nid(of_node));-------------domain大小為struct irq_domain加上gic_irqs個unsigned int。if (WARN_ON(!domain))return NULL;/* Fill structure */INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);domain->ops = ops;domain->host_data = host_data;domain->of_node = of_node_get(of_node);domain->hwirq_max = hwirq_max;domain->revmap_size = size;domain->revmap_direct_max_irq = direct_max;irq_domain_check_hierarchy(domain);mutex_lock(&irq_domain_mutex);list_add(&domain->link, &irq_domain_list);----------------------將創建好的struct irq_domain加入全局鏈表irq_domain_list。mutex_unlock(&irq_domain_mutex);pr_debug("Added domain %s\n", domain->name);return domain; }

2.3 系統初始化之中斷號映射

上一小節是中斷控制器GIC的初始化,下面看看一個硬件中斷是如何映射到Linux空間的中斷的。

customize_machine()是arch_initcall階段調用,很靠前。

customize_machine ->of_platform_populate ->of_platform_bus_create ->of_amba_device_create ->of_amba_device_create

下面結合dtsi文件看看來龍去脈,arch/arm/boot/dts/vexpress-v2m.dtsi。

/dts-v1/;/ {model = "V2P-CA9";arm,hbi = <0x191>;arm,vexpress,site = <0xf>;compatible = "arm,vexpress,v2p-ca9", "arm,vexpress";interrupt-parent = <&gic>;#address-cells = <1>;#size-cells = <1>; ...gic: interrupt-controller@1e001000 {compatible = "arm,cortex-a9-gic";#interrupt-cells = <3>;#address-cells = <0>;interrupt-controller;reg = <0x1e001000 0x1000>,<0x1e000100 0x100>;}; ...smb {compatible = "simple-bus";#address-cells = <2>;#size-cells = <1>;ranges = <0 0 0x40000000 0x04000000>,<1 0 0x44000000 0x04000000>,<2 0 0x48000000 0x04000000>,<3 0 0x4c000000 0x04000000>,<7 0 0x10000000 0x00020000>;#interrupt-cells = <1>;interrupt-map-mask = <0 0 63>;interrupt-map = <0 0 0 &gic 0 0 4>,<0 0 1 &gic 0 1 4>, ... /include/ "vexpress-v2m.dtsi"}; }vexpress-v2m.dtsi文件:motherboard {model = "V2M-P1";arm,hbi = <0x190>;arm,vexpress,site = <0>;compatible = "arm,vexpress,v2m-p1", "simple-bus";#address-cells = <2>; /* SMB chipselect number and offset */#size-cells = <1>;#interrupt-cells = <1>;ranges; ...iofpga@7,00000000 {compatible = "arm,amba-bus", "simple-bus";#address-cells = <1>;#size-cells = <1>;ranges = <0 7 0 0x20000>; ...v2m_serial0: uart@09000 {compatible = "arm,pl011", "arm,primecell";reg = <0x09000 0x1000>;interrupts = <5>;clocks = <&v2m_oscclk2>, <&smbclk>;clock-names = "uartclk", "apb_pclk";}; ...};}

這里首先從根目錄下查找"simple-bus",從上面可以看出指向smb設備。

smb設備包含vexpress-v2m.dtsi文件,然后在of_platform_bus_create()中遍歷所有設備。

const struct of_device_id of_default_bus_match_table[] = {{ .compatible = "simple-bus", }, #ifdef CONFIG_ARM_AMBA{ .compatible = "arm,amba-bus", }, #endif /* CONFIG_ARM_AMBA */{} /* Empty terminated list */ };static int __init customize_machine(void) { ...of_platform_populate(NULL, of_default_bus_match_table,-----------------找到匹配"simple-bus"的設備,這里指向smb。NULL, NULL); ... }int of_platform_populate(struct device_node *root,const struct of_device_id *matches,const struct of_dev_auxdata *lookup,struct device *parent) { ...for_each_child_of_node(root, child) {rc = of_platform_bus_create(child, matches, lookup, parent, true);-----這里的root指向根目錄,即"/"。if (rc)break;} ... }static int of_platform_bus_create(struct device_node *bus,const struct of_device_id *matches,const struct of_dev_auxdata *lookup,struct device *parent, bool strict) {const struct of_dev_auxdata *auxdata;struct device_node *child;struct platform_device *dev;const char *bus_id = NULL;void *platform_data = NULL;int rc = 0;/* Make sure it has a compatible property */if (strict && (!of_get_property(bus, "compatible", NULL))) {pr_debug("%s() - skipping %s, no compatible prop\n",__func__, bus->full_name);return 0;}auxdata = of_dev_lookup(lookup, bus);if (auxdata) {bus_id = auxdata->name;platform_data = auxdata->platform_data;}if (of_device_is_compatible(bus, "arm,primecell")) {------當遇到匹配"arm,primecell"設備,創建amba設備。在ofpga@7,00000000中創建uart@09000設備。/** Don't return an error here to keep compatibility with older* device tree files.*/of_amba_device_create(bus, bus_id, platform_data, parent);return 0;}dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);if (!dev || !of_match_node(matches, bus))return 0;for_each_child_of_node(bus, child) {----------------遍歷smb下的所有"simple-bus"設備,這里可以嵌套幾層。從smb->motherboard->iofpga@7,00000000。pr_debug(" create child: %s\n", child->full_name);rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);if (rc) {of_node_put(child);break;}}of_node_set_flag(bus, OF_POPULATED_BUS);return rc; }

of_amba_device_create創建ARM AMBA類型設備,其中中斷部分交給irq_of_parse_and_map()處理。

static struct amba_device *of_amba_device_create(struct device_node *node,const char *bus_id,void *platform_data,struct device *parent) { .../* Decode the IRQs and address ranges */for (i = 0; i < AMBA_NR_IRQS; i++)dev->irq[i] = irq_of_parse_and_map(node, i); ... }

以uart@09000為例,irq_of_parse_and_map中的of_irq_parse_one()解析設備中的"interrupts"、"regs"等參數,參數放入struct of_phandle_args中,oirq->args[1]中存放中斷號5,oirq->np存放struct device_node。

irq_create_of_mapping()建立硬件中斷號到Linux中斷號的映射。

irq_create_of_mapping主要調用如下,主要工作交給__irq_domain_alloc_irqs()進行處理。

irq_create_of_mapping ->domain->ops->xlate--------------------------------- ->irq_find_mapping ->irq_domain_alloc_irqs ->__irq_domain_alloc_irqs ->irq_domain_alloc_descs ->irq_domain_alloc_irq_data ->irq_domain_alloc_irqs_recursive ->gic_irq_domain_alloc ->gic_irq_domain_map-----------------------進行硬件中斷號和軟件中斷號的映射 ->gic_irq_domain_set_info----------------設置重要參數到中斷描述符中 ->irq_domain_insert_irq

unsigned int irq_of_parse_and_map(struct device_node *dev, int index) {struct of_phandle_args oirq;if (of_irq_parse_one(dev, index, &oirq))return 0;return irq_create_of_mapping(&oirq); }unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data) {struct irq_domain *domain;irq_hw_number_t hwirq;unsigned int type = IRQ_TYPE_NONE;int virq;domain = irq_data->np ? irq_find_host(irq_data->np) : irq_default_domain;---找到設備所屬的struct irq_domain結構體。 .../* If domain has no translation, then we assume interrupt line */if (domain->ops->xlate == NULL)hwirq = irq_data->args[0];else {if (domain->ops->xlate(domain, irq_data->np, irq_data->args,-------調用gic_irq_domain_xlate()函數進行硬件中斷號到Linux中斷號的轉換。irq_data->args_count, &hwirq, &type))return 0;}if (irq_domain_is_hierarchy(domain)) {-------------------------可以分層掛載/** If we've already configured this interrupt,* don't do it again, or hell will break loose.*/virq = irq_find_mapping(domain, hwirq);-------------------從已有的linear_revmap中尋找Linux中斷號。if (virq)return virq;virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, irq_data);---------如果沒有找到,重新分配中斷映射。參數1表示每次只分配一個中斷。if (virq <= 0)return 0;} else { ...}/* Set type if specified and different than the current one */if (type != IRQ_TYPE_NONE &&type != irq_get_trigger_type(virq))irq_set_irq_type(virq, type);-----------------------------設置中斷觸發類型return virq; }

struct irq_desc定義了中斷描述符,irq_desc[]數組定義了NR_IRQS個中斷描述符,數組下標表示IRQ中斷號,通過IRQ中斷號可以找到對應中斷描述符。

struct irq_desc內置了struct irq_data結構體,struct irq_data的irq和hwirq分別對應軟件中斷號和硬件中斷號。通過這兩個成員,可以將硬件中斷號和軟件中斷號映射起來。

struct irq_chip定義了中斷控制器底層操作相關的方法集合。

struct irq_desc {struct irq_data irq_data;unsigned int __percpu *kstat_irqs;irq_flow_handler_t handle_irq;-----------------根據中斷號分類,不同類型中斷的處理handle。0~31對應handle_percpu_devid_irq;32~對應handle_fasteoi_irq。 #ifdef CONFIG_IRQ_PREFLOW_FASTEOIirq_preflow_handler_t preflow_handler; #endifstruct irqaction *action; /* IRQ action list */unsigned int status_use_accessors;unsigned int core_internal_state__do_not_mess_with_it;unsigned int depth; /* nested irq disables */unsigned int wake_depth; /* nested wake enables */unsigned int irq_count; /* For detecting broken IRQs */unsigned long last_unhandled; /* Aging timer for unhandled count */unsigned int irqs_unhandled;atomic_t threads_handled;int threads_handled_last;raw_spinlock_t lock;struct cpumask *percpu_enabled; #ifdef CONFIG_SMPconst struct cpumask *affinity_hint;struct irq_affinity_notify *affinity_notify; #ifdef CONFIG_GENERIC_PENDING_IRQcpumask_var_t pending_mask; #endif #endifunsigned long threads_oneshot;-------------是一個位圖,每個比特位代表正在處理的共享oneshot類型中斷的中斷線程。atomic_t threads_active;-------------------表示正在運行的中斷線程個數wait_queue_head_t wait_for_threads; #ifdef CONFIG_PM_SLEEPunsigned int nr_actions;unsigned int no_suspend_depth;unsigned int cond_suspend_depth;unsigned int force_resume_depth; #endif #ifdef CONFIG_PROC_FSstruct proc_dir_entry *dir; #endifint parent_irq;struct module *owner;const char *name; }struct irq_data {u32 mask;unsigned int irq;-----------------Linux軟件中斷號unsigned long hwirq;--------------硬件中斷號unsigned int node;unsigned int state_use_accessors;struct irq_chip *chip;struct irq_domain *domain; #ifdef CONFIG_IRQ_DOMAIN_HIERARCHYstruct irq_data *parent_data; #endifvoid *handler_data;void *chip_data;struct msi_desc *msi_desc;cpumask_var_t affinity; }struct irq_chip {const char *name;unsigned int (*irq_startup)(struct irq_data *data);-------------初始化中斷void (*irq_shutdown)(struct irq_data *data);----------------結束中斷void (*irq_enable)(struct irq_data *data);------------------使能中斷void (*irq_disable)(struct irq_data *data);-----------------關閉中斷void (*irq_ack)(struct irq_data *data);---------------------應答中斷void (*irq_mask)(struct irq_data *data);--------------------屏蔽中斷void (*irq_mask_ack)(struct irq_data *data);----------------應答并屏蔽中斷void (*irq_unmask)(struct irq_data *data);------------------解除中斷屏蔽void (*irq_eoi)(struct irq_data *data);---------------------發送EOI信號,表示硬件中斷處理已經完成。int (*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force);--------綁定中斷到某個CPUint (*irq_retrigger)(struct irq_data *data);----------------重新發送中斷到CPUint (*irq_set_type)(struct irq_data *data, unsigned int flow_type);----------------------------設置觸發類型int (*irq_set_wake)(struct irq_data *data, unsigned int on);-----------------------------------使能/關閉中斷在電源管理中的喚醒功能。void (*irq_bus_lock)(struct irq_data *data);void (*irq_bus_sync_unlock)(struct irq_data *data);void (*irq_cpu_online)(struct irq_data *data);void (*irq_cpu_offline)(struct irq_data *data);void (*irq_suspend)(struct irq_data *data);void (*irq_resume)(struct irq_data *data);void (*irq_pm_shutdown)(struct irq_data *data); ...unsigned long flags; }

gic_chip是特定中斷控制器的硬件操作函數集,對于GICv2有屏蔽/去屏蔽、EOI、設置中斷觸發類型、以及設置或者當前芯片狀態。

static const struct irq_chip gic_chip = {.irq_mask = gic_mask_irq,.irq_unmask = gic_unmask_irq,.irq_eoi = gic_eoi_irq,.irq_set_type = gic_set_type,.irq_get_irqchip_state = gic_irq_get_irqchip_state,.irq_set_irqchip_state = gic_irq_set_irqchip_state,.flags = IRQCHIP_SET_TYPE_MASKED |IRQCHIP_SKIP_SET_WAKE |IRQCHIP_MASK_ON_SUSPEND, };static void gic_mask_irq(struct irq_data *d) {gic_poke_irq(d, GIC_DIST_ENABLE_CLEAR); }static void gic_unmask_irq(struct irq_data *d) {gic_poke_irq(d, GIC_DIST_ENABLE_SET); }static void gic_eoi_irq(struct irq_data *d) {writel_relaxed(gic_irq(d), gic_cpu_base(d) + GIC_CPU_EOI); }static int gic_set_type(struct irq_data *d, unsigned int type) {void __iomem *base = gic_dist_base(d);unsigned int gicirq = gic_irq(d);/* Interrupt configuration for SGIs can't be changed */if (gicirq < 16)return -EINVAL;/* SPIs have restrictions on the supported types */if (gicirq >= 32 && type != IRQ_TYPE_LEVEL_HIGH &&type != IRQ_TYPE_EDGE_RISING)return -EINVAL;return gic_configure_irq(gicirq, type, base, NULL); }static int gic_irq_set_irqchip_state(struct irq_data *d,enum irqchip_irq_state which, bool val) {u32 reg;switch (which) {case IRQCHIP_STATE_PENDING:reg = val ? GIC_DIST_PENDING_SET : GIC_DIST_PENDING_CLEAR;break;case IRQCHIP_STATE_ACTIVE:reg = val ? GIC_DIST_ACTIVE_SET : GIC_DIST_ACTIVE_CLEAR;break;case IRQCHIP_STATE_MASKED:reg = val ? GIC_DIST_ENABLE_CLEAR : GIC_DIST_ENABLE_SET;break;default:return -EINVAL;}gic_poke_irq(d, reg);return 0; }static int gic_irq_get_irqchip_state(struct irq_data *d,enum irqchip_irq_state which, bool *val) {switch (which) {case IRQCHIP_STATE_PENDING:*val = gic_peek_irq(d, GIC_DIST_PENDING_SET);break;case IRQCHIP_STATE_ACTIVE:*val = gic_peek_irq(d, GIC_DIST_ACTIVE_SET);break;case IRQCHIP_STATE_MASKED:*val = !gic_peek_irq(d, GIC_DIST_ENABLE_SET);break;default:return -EINVAL;}return 0; }

irq_domain_alloc_irqs()調用__irq_domain_alloc_irqs()進行struct irq_desc、struct irq_data以及中斷映射的處理。

這里的參數nr_irqs一般為1,每次只處理一個中斷。

irq_domain_alloc_descs()->irq_alloc_descs()->__irq_alloc_descs()進行struct irq_desc的分配,返回的參數是Linux中斷號。

int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,unsigned int nr_irqs, int node, void *arg,bool realloc) { ...if (realloc && irq_base >= 0) {virq = irq_base;} else {virq = irq_domain_alloc_descs(irq_base, nr_irqs, 0, node);-------從allocated_irqs位圖中查找第一個nr_irqs個空閑的比特位,最終調用__irq_alloc_descs。if (virq < 0) {pr_debug("cannot allocate IRQ(base %d, count %d)\n",irq_base, nr_irqs);return virq;}}if (irq_domain_alloc_irq_data(domain, virq, nr_irqs)) {--------------分配struct irq_data數據結構。pr_debug("cannot allocate memory for IRQ%d\n", virq);ret = -ENOMEM;goto out_free_desc;}mutex_lock(&irq_domain_mutex);ret = irq_domain_alloc_irqs_recursive(domain, virq, nr_irqs, arg);----調用struct irq_domain中的alloc回調函數進行硬件中斷號和軟件中斷號的映射。if (ret < 0) {mutex_unlock(&irq_domain_mutex);goto out_free_irq_data;}for (i = 0; i < nr_irqs; i++)irq_domain_insert_irq(virq + i);mutex_unlock(&irq_domain_mutex);return virq; ... }int __ref __irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node,struct module *owner) { ...mutex_lock(&sparse_irq_lock);start = bitmap_find_next_zero_area(allocated_irqs, IRQ_BITMAP_BITS,from, cnt, 0);-------------------在allocated_irqs位圖中查找第一個連續cnt個為0的比特位區域。 ...bitmap_set(allocated_irqs, start, cnt);-------------bitmap_set()設置這些比特位,表示這些比特位已經被占用。mutex_unlock(&sparse_irq_lock);return alloc_descs(start, cnt, node, owner);--------這里要看是否定義了CONFIG_SPARSE_IRQ,如果定義了需要動態分配一個struct irq_desc數據結構,以Radix Tree方式存儲;沒有的話則從irq_desc全局變量中加上偏移即可。err:mutex_unlock(&sparse_irq_lock);return ret; }

irq_domain_alloc_irqs_recursive()會根據實際情況決定中斷控制器的遞歸處理,

static int irq_domain_alloc_irqs_recursive(struct irq_domain *domain,unsigned int irq_base,unsigned int nr_irqs, void *arg) {int ret = 0;struct irq_domain *parent = domain->parent;bool recursive = irq_domain_is_auto_recursive(domain);BUG_ON(recursive && !parent);if (recursive)ret = irq_domain_alloc_irqs_recursive(parent, irq_base,nr_irqs, arg);if (ret >= 0)ret = domain->ops->alloc(domain, irq_base, nr_irqs, arg);if (ret < 0 && recursive)irq_domain_free_irqs_recursive(parent, irq_base, nr_irqs);return ret; }

至此完成了中斷DeviceTree的解析,各數據結構的初始化,以及最主要的硬件中斷號到Linux中斷號的映射。

3. ARM底層中斷處理

ARM底層中斷處理的范圍是從中斷異常觸發,到irq_handler。

3.1 中斷硬件行為

外設有事件需要報告SoC時,通過和SoC鏈接的中斷管腳發送中斷信號,可能是邊沿觸發信號也可能是電平觸發信號。

中斷控制器會感知中斷信號,中斷控制器仲裁單元選擇優先級最高的中斷發送到CPU Interface,CPU Interface決定將中斷分發到哪個CPU核心。

GIC控制器和CPU核心之間通過一個nIRQ(IRQ request input line)信號來通知CPU。

CPU核心感知到中斷發生之后,硬件會做如下工作:

  • 保存中斷發生時CPSR寄存器內容到SPSR_irq寄存器中
  • 修改CPSR寄存器,讓CPU進入處理器模式(processor mode)中的IRQ模式,即修改CPSR寄存器中的M域設置為IRQ Mode。
  • 硬件自動關閉中斷IRQ或FIQ,即CPSR中的IRQ位或FIQ位置1。------------硬件自動關中斷
  • 保存返回地址到LR_irq寄存器中。
  • 硬件自動調轉到中斷向量表的IRQ向量。-------------------------------------------從此處開始進入軟件領域

當從中斷返回時需要軟件實現如下操作:

  • 從SPSR_irq寄存器中恢復數據到CPSR中。
  • 從LR_irq中恢復內容到PC中,從而返回到中斷點的下一個指令處執行。

3.2 中斷異常向量

3.2.1 中斷異常向量代碼段初始化

內核編譯時,異常向量表存放在可執行文件的__init段中:arch/arm/kernel/vmlinux.lds.S。

__vectors_start和__vectors_end指向vectors段的開始和結束地址,__stubs_start和__stubs_end存放異常向量stubs代碼段。兩者都是頁面對齊,大小都為一個頁面。

__vectors_start = .;.vectors 0 : AT(__vectors_start) {*(.vectors)----------------------------------保存.vectors段數據}. = __vectors_start + SIZEOF(.vectors);__vectors_end = .;__stubs_start = .;.stubs 0x1000 : AT(__stubs_start) {*(.stubs)------------------------------------存放.stubs段數據}. = __stubs_start + SIZEOF(.stubs);__stubs_end = .;

系統初始化時會把上述兩個段復制到高端地址處,即ixffff_0000:start_kernel->setup_arch->paging_init->devicemap_init。

static void __init devicemaps_init(const struct machine_desc *mdesc) {struct map_desc map;unsigned long addr;void *vectors;/** Allocate the vector page early.*/vectors = early_alloc(PAGE_SIZE * 2);-------------------------------分配兩個頁面用于映射到high vectors高端地址。early_trap_init(vectors);-------------------------------------------實現異常向量表的復制動作。.../** Create a mapping for the machine vectors at the high-vectors* location (0xffff0000). If we aren't using high-vectors, also* create a mapping at the low-vectors virtual address.*/map.pfn = __phys_to_pfn(virt_to_phys(vectors));---------------------vectors物理頁面號map.virtual = 0xffff0000;-------------------------------------------待映射到的虛擬地址0xffff_0000~0xffff_0fffmap.length = PAGE_SIZE;---------------------------------------------映射區間大小 #ifdef CONFIG_KUSER_HELPERSmap.type = MT_HIGH_VECTORS;-----------------------------------------映射到high vector #elsemap.type = MT_LOW_VECTORS; #endifcreate_mapping(&map);if (!vectors_high()) {map.virtual = 0;map.length = PAGE_SIZE * 2;map.type = MT_LOW_VECTORS;create_mapping(&map);}/* Now create a kernel read-only mapping */map.pfn += 1;map.virtual = 0xffff0000 + PAGE_SIZE;------------------------------映射到0xffff_1000~0xffff_1ffffmap.length = PAGE_SIZE;map.type = MT_LOW_VECTORS;create_mapping(&map); ... }

early_trap_init分別將__vectors_start和__stubs_start兩個頁面復制到分配的兩個頁面中。

void __init early_trap_init(void *vectors_base) { ...unsigned long vectors = (unsigned long)vectors_base;extern char __stubs_start[], __stubs_end[];extern char __vectors_start[], __vectors_end[];unsigned i;vectors_page = vectors_base;/** Poison the vectors page with an undefined instruction. This* instruction is chosen to be undefined for both ARM and Thumb* ISAs. The Thumb version is an undefined instruction with a* branch back to the undefined instruction.*/for (i = 0; i < PAGE_SIZE / sizeof(u32); i++)((u32 *)vectors_base)[i] = 0xe7fddef1;---------------------------第一個頁面全部填充未定義指令0xe7fddef1。/** Copy the vectors, stubs and kuser helpers (in entry-armv.S)* into the vector page, mapped at 0xffff0000, and ensure these* are visible to the instruction stream.*/memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);memcpy((void *)vectors + 0x1000, __stubs_start, __stubs_end - __stubs_start); ... }

3.2.2 中斷異常向量

中斷發生后,軟件跳轉到中斷向量表開始vector_irq執行,vector_irq在結尾的時候根據中斷發生點所在模式,決定跳轉到__irq_usr或者__irq_svc。

vector_irq在arch/arm/kernel/entry-armv.S由宏vector_stub定義。

關于correction==4,需要減去4字節才是返回地址?

vector_stub宏參數correction為4,。

正在執行指令A時發生了中斷,由于ARM流水線和指令預取等原因,pc指向A+8B處,那么必須等待指令A執行完畢才能處理該中斷,這時PC已經更新到A+12B處。

進入中斷響應前夕,pc寄存器的內容被裝入lr寄存器中,lr=pc-4,即A+8B地址處。

因此返回時要pc=lr-4,才是被中斷時要執行的下一條指令。所以lr要回退4B。

.section .vectors, "ax", %progbits __vectors_start:W(b) vector_rstW(b) vector_undW(ldr) pc, __vectors_start + 0x1000W(b) vector_pabtW(b) vector_dabtW(b) vector_addrexcptnW(b) vector_irq---------------------------------------------------------------跳轉到vector_irqW(b) vector_fiq/** Interrupt dispatcher*/vector_stub irq, IRQ_MODE, 4------------------------------------------------vector_stub宏定義了vector_irq.long __irq_usr @ 0 (USR_26 / USR_32).long __irq_invalid @ 1 (FIQ_26 / FIQ_32).long __irq_invalid @ 2 (IRQ_26 / IRQ_32).long __irq_svc @ 3 (SVC_26 / SVC_32)----------------------------svc模式數值是0b10011,與上0xf后就是3。.long __irq_invalid @ 4.long __irq_invalid @ 5.long __irq_invalid @ 6.long __irq_invalid @ 7.long __irq_invalid @ 8.long __irq_invalid @ 9.long __irq_invalid @ a.long __irq_invalid @ b.long __irq_invalid @ c.long __irq_invalid @ d.long __irq_invalid @ e.long __irq_invalid @ f

3.3 內核空間中斷處理__irq_svc

__irq_svc處理發生在內核空間的中斷,主要svc_entry保護中斷現場;irq_handler執行中斷處理;如果打開搶占功能,檢查是否可以搶占;最后svc_exit執行中斷退出處理。

__irq_svc:svc_entryirq_handler#ifdef CONFIG_PREEMPT-----------------------------------------------------中斷處理結束后,發生搶占的地方?get_thread_info tskldr r8, [tsk, #TI_PREEMPT] @ get preempt count--------------獲取thread_info->preempt_cpunt變量;preempt_count為0,說明可以搶占進程;preempt_count大于0,表示不能搶占。ldr r0, [tsk, #TI_FLAGS] @ get flags------------------------獲取thread_info->flags變量teq r8, #0 @ if preempt count != 0movne r0, #0 @ force flags to 0tst r0, #_TIF_NEED_RESCHED-----------------------------------------判斷是否設置了_TIF_NEED_RESCHED標志位blne svc_preempt #endifsvc_exit r5, irq = 1 @ return from exceptionUNWIND(.fnend ) ENDPROC(__irq_svc)

svc_entry將中斷現場保存到內核棧中,主要是struct pt_regs中的寄存器。

.macro svc_entry, stack_hole=0, trace=1UNWIND(.fnstart )UNWIND(.save {r0 - pc} )sub sp, sp, #(S_FRAME_SIZE + \stack_hole - 4) #ifdef CONFIG_THUMB2_KERNELSPFIX( str r0, [sp] ) @ temporarily savedSPFIX( mov r0, sp )SPFIX( tst r0, #4 ) @ test original stack alignmentSPFIX( ldr r0, [sp] ) @ restored #elseSPFIX( tst sp, #4 ) #endifSPFIX( subeq sp, sp, #4 )stmia sp, {r1 - r12}ldmia r0, {r3 - r5}add r7, sp, #S_SP - 4 @ here for interlock avoidancemov r6, #-1 @ "" "" "" ""add r2, sp, #(S_FRAME_SIZE + \stack_hole - 4)SPFIX( addeq r2, r2, #4 )str r3, [sp, #-4]! @ save the "real" r0 copied@ from the exception stackmov r3, lr@@ We are now ready to fill in the remaining blanks on the stack:@@ r2 - sp_svc@ r3 - lr_svc@ r4 - lr_<exception>, already fixed up for correct return/restart@ r5 - spsr_<exception>@ r6 - orig_r0 (see pt_regs definition in ptrace.h)@stmia r7, {r2 - r6}.if \trace #ifdef CONFIG_TRACE_IRQFLAGSbl trace_hardirqs_off #endif.endif.endm

svc_exit準備返回中斷現場,然后通過ldmia指令從棧中恢復15個寄存器,包括pc內容,至此整個中斷完成并返回。

.macro svc_exit, rpsr, irq = 0...msr spsr_cxsf, \rpsrldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr.endm

irq_handler進入高層中斷處理。

4. 高層中斷處理

irq_handler匯編宏是ARCH層和高層中斷處理分割線,在這里從匯編跳轉到C進行GIC相關處理。

前面介紹了一個中斷是如何從硬件中斷號映射到Linux中斷號的,那么當一個中斷產生后它從應將到軟件識別中斷號,再到轉換成Linux中斷號是什么路徑呢?

這里就從irq_handler開始分析流程:

irq_handler() ->handle_arch_irq()->gic_handle_irq() ->handle_domain_irq()->__handle_domain_irq()-------------讀取IAR寄存器,響應中斷,獲取硬件中斷號 ->irq_find_mapping()------------------------------------------------將硬件中斷號轉變成Linux中斷號 ->generic_handle_irq()---------------------------------------------之后的操作都是Linux中斷號 ->handle_percpu_devid_irq()-----------------------------------SGI/PPI類型中斷處理 ->handle_fasteoi_irq()--------------------------------------------SPI類型中斷處理 ->handle_irq_event()->handle_irq_event_percpu()------執行中斷處理核心函數 ->action->handler-----------------------------------------------執行primary handler。 ->__irq_wake_thread()----------------------------------------根據需要喚醒中斷內核線程

4.1 irq_handler

irq_handler宏調用handle_arch_irq函數,這個函數set_handle_irq注冊,GICv2對應gic_handle_irq。

.macro irq_handler #ifdef CONFIG_MULTI_IRQ_HANDLERldr r1, =handle_arch_irqmov r0, spadr lr, BSYM(9997f)ldr pc, [r1] #elsearch_irq_handler_default #endif 9997:.endm

4.2 gic_handle_irq

git_init_bases設置handle_arch_irq為gic_handle_irq。

void __init gic_init_bases(unsigned int gic_nr, int irq_start,void __iomem *dist_base, void __iomem *cpu_base,u32 percpu_offset, struct device_node *node) { ...if (gic_nr == 0) { ...set_handle_irq(gic_handle_irq);} ... }void __init set_handle_irq(void (*handle_irq)(struct pt_regs *)) {if (handle_arch_irq)return;handle_arch_irq = handle_irq; }

gic_handle_irq對將中斷分為兩組:SGI、PPI/SPI。

SGI類型中斷交給handle_IPI()處理;PPI/SPI類型交給handle_domain_irq處理。

static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) {u32 irqstat, irqnr;struct gic_chip_data *gic = &gic_data[0];void __iomem *cpu_base = gic_data_cpu_base(gic);do {irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);---讀取IAR寄存器,表示響應中斷。irqnr = irqstat & GICC_IAR_INT_ID_MASK;-----------------GICC_IAR_INT_ID_MASK為0x3ff,即低10位,所以中斷最多從0~1023。if (likely(irqnr > 15 && irqnr < 1021)) {handle_domain_irq(gic->domain, irqnr, regs);continue;}if (irqnr < 16) {---------------------------------------SGI類型的中斷是CPU核間通信所用,只有定義了CONFIG_SMP才有意義。writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);----直接寫EOI寄存器,表示結束中斷。 #ifdef CONFIG_SMPhandle_IPI(irqnr, regs);----------------------------irqnr表示SGI中斷類型 #endifcontinue;}break;} while (1); }

handle_domain_irq調用__handle_domain_irq,其中lookup置為true。

irq_enter顯式告訴Linux內核現在要進入中斷上下文了,在處理完中斷后調用irq_exit告訴Linux已經完成中斷處理過程。

int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,bool lookup, struct pt_regs *regs) {struct pt_regs *old_regs = set_irq_regs(regs);unsigned int irq = hwirq;int ret = 0;irq_enter();-----------------------------------------------通過顯式增加hardirq域計數,通知Linux進入中斷上下文#ifdef CONFIG_IRQ_DOMAINif (lookup)irq = irq_find_mapping(domain, hwirq);-----------------根據硬件中斷號找到對應的軟件中斷號 #endif/** Some hardware gives randomly wrong interrupts. Rather* than crashing, do something sensible.*/if (unlikely(!irq || irq >= nr_irqs)) {ack_bad_irq(irq);ret = -EINVAL;} else {generic_handle_irq(irq);--------------------------------開始具體某一個中斷的處理,此處irq已經是Linux中斷號。}irq_exit();-------------------------------------------------退出中斷上下文set_irq_regs(old_regs);return ret; }

irq_find_mapping在struct irq_domain中根據hwirq找到Linux環境的irq。

unsigned int irq_find_mapping(struct irq_domain *domain,irq_hw_number_t hwirq) {struct irq_data *data; .../* Check if the hwirq is in the linear revmap. */if (hwirq < domain->revmap_size)return domain->linear_revmap[hwirq];----------------linear_revmap[]在__irq_domain_alloc_irqs()->irq_domain_insert_irq()時賦值。 ... }

generic_handle_irq參數是irq號,irq_to_desc()根據irq號找到對應的struct irq_desc。

然后調用irq_desc->handle_irq處理對應的中斷。

int generic_handle_irq(unsigned int irq) {struct irq_desc *desc = irq_to_desc(irq);if (!desc)return -EINVAL;generic_handle_irq_desc(irq, desc);return 0; }static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc) {desc->handle_irq(irq, desc); }

關于desc->handle_irq來歷,在每個中斷注冊的時候,由gic_irq_domain_map根據hwirq號決定。

在gic_irq_domain_map的時候根據hw號決定handle,hw硬件中斷號小于32指向handle_percpu_devid_irq,其他情況指向handle_fasteoi_irq。

void __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,const char *name) { ...desc->handle_irq = handle;desc->name = name; ... }

handle_percpu_devid_irq處理0~31的SGI/PPI類型中斷,首先響應IAR,然后執行handler,最后發送EOI。

void handle_percpu_devid_irq(unsigned int irq, struct irq_desc *desc) {struct irq_chip *chip = irq_desc_get_chip(desc);struct irqaction *action = desc->action;void *dev_id = raw_cpu_ptr(action->percpu_dev_id);irqreturn_t res;kstat_incr_irqs_this_cpu(irq, desc);if (chip->irq_ack)chip->irq_ack(&desc->irq_data);trace_irq_handler_entry(irq, action);res = action->handler(irq, dev_id);trace_irq_handler_exit(irq, action, res);if (chip->irq_eoi)chip->irq_eoi(&desc->irq_data);-------------------調用gic_eoi_irq()函數 }

irq_enter和irq_exit顯式地處理hardirq域計數,兩者之間的部分屬于中斷上下文。

/** Enter an interrupt context.*/ void irq_enter(void) {rcu_irq_enter();if (is_idle_task(current) && !in_interrupt()) {/** Prevent raise_softirq from needlessly waking up ksoftirqd* here, as softirq will be serviced on return from interrupt.*/local_bh_disable();tick_irq_enter();_local_bh_enable();}__irq_enter();---------------------------------------------顯式增加hardirq域計數 }#define __irq_enter() \do { \account_irq_enter_time(current); \preempt_count_add(HARDIRQ_OFFSET); \----------------顯式增加hardirq域計數trace_hardirq_enter(); \} while (0)void irq_exit(void) { #ifndef __ARCH_IRQ_EXIT_IRQS_DISABLEDlocal_irq_disable(); #elseWARN_ON_ONCE(!irqs_disabled()); #endifaccount_irq_exit_time(current);preempt_count_sub(HARDIRQ_OFFSET);---------------------------顯式減少hardirq域計數if (!in_interrupt() && local_softirq_pending())--------------當前不處于中斷上下文,且有pending的softirq,進行softirq處理。invoke_softirq();tick_irq_exit();rcu_irq_exit();trace_hardirq_exit(); /* must be last! */ }

4.2.1 中斷上下文

判斷當前進程是處于中斷上下文,還是進程上下文依賴于preempt_count,這個變量在struct thread_info中。

preempt_count計數共32bit,從低到高依次是:

#define PREEMPT_BITS 8 #define SOFTIRQ_BITS 8 #define HARDIRQ_BITS 4 #define NMI_BITS 1 #define hardirq_count() (preempt_count() & HARDIRQ_MASK)-----------------硬件中斷計數 #define softirq_count() (preempt_count() & SOFTIRQ_MASK)-----------------軟中斷計數 #define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK \----包括NMI、硬中斷、軟中斷三者計數| NMI_MASK))/** Are we doing bottom half or hardware interrupt processing?** in_irq() - We're in (hard) IRQ context* in_softirq() - We have BH disabled, or are processing softirqs* in_interrupt() - We're in NMI,IRQ,SoftIRQ context or have BH disabled* in_serving_softirq() - We're in softirq context* in_nmi() - We're in NMI context* in_task() - We're in task context** Note: due to the BH disabled confusion: in_softirq(),in_interrupt() really* should not be used in new code.*/ #define in_irq() (hardirq_count())----------------------------判斷是否正在硬件中斷上下文 #define in_softirq() (softirq_count())------------------------判斷是否正在處理軟中斷或者禁止BH。 #define in_interrupt() (irq_count())--------------------------判斷是否處于NMI、硬中斷、軟中斷三者之一或者兼有上下文 #define in_serving_softirq() (softirq_count() & SOFTIRQ_OFFSET)---判斷是否處于軟中斷上下文。 #define in_nmi() (preempt_count() & NMI_MASK)-----------------判斷是否處于NMI上下文 #define in_task() (!(preempt_count() & \(NMI_MASK | HARDIRQ_MASK | SOFTIRQ_OFFSET)))------判斷是否處于進程上下文

思考:in_softirq()和in_serving_softirq()區別?in_interrupt()和in_task()中關于SOFTIRQ_MASK和SOFTIRQ_OFFSET區別?

4.3 handle_fasteoi_irq

handle_fsteoi_irq處理SPI類型的中斷,將主要工作交給handle_irq_event()。

handle_irq_event_percpu()首先處理action->handler,有需要則喚醒中斷內核線程,執行action->thread_fn。

void handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc) {struct irq_chip *chip = desc->irq_data.chip;raw_spin_lock(&desc->lock);if (!irq_may_run(desc))goto out;desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);kstat_incr_irqs_this_cpu(irq, desc);/** If its disabled or no action available* then mask it and get out of here:*/if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {---如果該中斷沒有指定action描述符或該中斷被關閉了IRQD_IRQ_DISABLED,設置該中斷狀態為IRQS_PENDING,且mask_irq()屏蔽該中斷。desc->istate |= IRQS_PENDING;mask_irq(desc);goto out;}if (desc->istate & IRQS_ONESHOT)----------------------------------------如果中斷是IRQS_ONESHOT,不支持中斷嵌套,那么應該調用mask_irq()來屏蔽該中斷源。mask_irq(desc);preflow_handler(desc);--------------------------------------------------取決于是否定義了freflow_handler()handle_irq_event(desc);cond_unmask_eoi_irq(desc, chip);----------------------------------------根據不同條件執行unmask_irq()解除中斷屏蔽,或者執行irq_chip->irq_eoi發送EOI信號,通知GIC中斷處理完畢。raw_spin_unlock(&desc->lock);return; out:if (!(chip->flags & IRQCHIP_EOI_IF_HANDLED))chip->irq_eoi(&desc->irq_data);raw_spin_unlock(&desc->lock); }

handle_irq_event調用handle_irq_event_percpu,執行action->handler(),如有需要喚醒內核中斷線程執行action->thread_fn。

irqreturn_t handle_irq_event(struct irq_desc *desc) {struct irqaction *action = desc->action;irqreturn_t ret;desc->istate &= ~IRQS_PENDING;--------------------------清除IRQS_PENDING標志位irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);---------設置IRQD_IRQ_INPROGRESS標志位,表示正在處理硬件中斷。raw_spin_unlock(&desc->lock);ret = handle_irq_event_percpu(desc, action);raw_spin_lock(&desc->lock);irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);-------清除IRQD_IRQ_INPROGRESS標志位,表示中斷處理結束。return ret; }irqreturn_t handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action) {irqreturn_t retval = IRQ_NONE;unsigned int flags = 0, irq = desc->irq_data.irq;do {----------------------------------------------------遍歷中斷描述符中的action鏈表,依次執行每個action元素中的primary handler回調函數action->handler。irqreturn_t res;trace_irq_handler_entry(irq, action);res = action->handler(irq, action->dev_id);---------執行struct irqaction的handler函數。trace_irq_handler_exit(irq, action, res);if (WARN_ONCE(!irqs_disabled(),"irq %u handler %pF enabled interrupts\n",irq, action->handler))local_irq_disable();---------------------------switch (res) {case IRQ_WAKE_THREAD:-------------------------------去喚醒內核中斷線程/** Catch drivers which return WAKE_THREAD but* did not set up a thread function*/if (unlikely(!action->thread_fn)) {warn_no_thread(irq, action);----------------輸出一個打印表示沒有中斷處理函數break;}__irq_wake_thread(desc, action);----------------喚醒此中斷對應的內核線程/* Fall through to add to randomness */case IRQ_HANDLED:-----------------------------------已經處理完畢,可以結束。flags |= action->flags;break;default:break;}retval |= res;action = action->next;} while (action);add_interrupt_randomness(irq, flags);if (!noirqdebug)note_interrupt(irq, desc, retval);return retval; }

4.3.1 喚醒中斷內核線程

__irq_wake_thread喚醒對應中斷的內核線程。

void __irq_wake_thread(struct irq_desc *desc, struct irqaction *action) {/** In case the thread crashed and was killed we just pretend that* we handled the interrupt. The hardirq handler has disabled the* device interrupt, so no irq storm is lurking.*/if (action->thread->flags & PF_EXITING)return;/** Wake up the handler thread for this action. If the* RUNTHREAD bit is already set, nothing to do.*/if (test_and_set_bit(IRQTF_RUNTHREAD, &action->thread_flags))--------------若已經對IRQF_RUNTHREAD置位,表示已經處于喚醒中,該函數直接返回。return;desc->threads_oneshot |= action->thread_mask;--------------------thread_mask在共享中斷中,每一個action有一個比特位來表示。thread_oneshot每個比特位表示正在處理的共享oneshot類型中斷的中斷線程。atomic_inc(&desc->threads_active);-------------------------------活躍中斷線程計數wake_up_process(action->thread);---------------------------------喚醒action的thread內核線程 }

4.3.2 創建內核中斷線程

irq_thread在中斷注冊的時候,如果條件滿足同時創建rq/xx-xx內核中斷線程,線程優先級是49(99-50),調度策略是SCHED_FIFO。

static int __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) { .../** Create a handler thread when a thread function is supplied* and the interrupt does not nest into another interrupt* thread.*/if (new->thread_fn && !nested) {struct task_struct *t;static const struct sched_param param = {.sched_priority = MAX_USER_RT_PRIO/2,-------------------------------設置irq內核線程的優先級,在/proc/xxx/sched中看到的prio為MAX_RT_PRIO-1-sched_priority。};t = kthread_create(irq_thread, new, "irq/%d-%s", irq,new->name);--------------------------------------------------創建線程名為irq/xxx-xxx的內核線程,線程執行函數是irq_thread。 ...sched_setscheduler_nocheck(t, SCHED_FIFO, &param);----------------------設置進程調度策略為SCHED_FIFO。/** We keep the reference to the task struct even if* the thread dies to avoid that the interrupt code* references an already freed task_struct.*/get_task_struct(t);new->thread = t;-------------------------------------------------------將當前線程和irq_action關聯起來set_bit(IRQTF_AFFINITY, &new->thread_flags);--------------------------對中斷線程設置CPU親和性} ... }

4.3.3 內核中斷線程執行

irq_thread是中斷線程的執行函數,在irq_wait_for_interrupt()中等待。

irq_wait_for_interrupt()中判斷IRQTF_RUNTHREAD標志位,沒有置位則schedule()換出CPU,進行睡眠。

直到__irq_wake_thread()置位了IRQTF_RUNTHREAD,并且wake_up_process()后,irq_wait_for_interrupt()返回0。

static int irq_thread(void *data) {struct callback_head on_exit_work;struct irqaction *action = data;struct irq_desc *desc = irq_to_desc(action->irq);irqreturn_t (*handler_fn)(struct irq_desc *desc,struct irqaction *action);if (force_irqthreads && test_bit(IRQTF_FORCED_THREAD,&action->thread_flags))handler_fn = irq_forced_thread_fn;elsehandler_fn = irq_thread_fn;init_task_work(&on_exit_work, irq_thread_dtor);task_work_add(current, &on_exit_work, false);irq_thread_check_affinity(desc, action);while (!irq_wait_for_interrupt(action)) {irqreturn_t action_ret;irq_thread_check_affinity(desc, action);action_ret = handler_fn(desc, action);-----------執行中斷內核線程函數if (action_ret == IRQ_HANDLED)atomic_inc(&desc->threads_handled);----------增加threads_handled計數wake_threads_waitq(desc);------------------------喚醒wait_for_threads等待隊列}/** This is the regular exit path. __free_irq() is stopping the* thread via kthread_stop() after calling* synchronize_irq(). So neither IRQTF_RUNTHREAD nor the* oneshot mask bit can be set. We cannot verify that as we* cannot touch the oneshot mask at this point anymore as* __setup_irq() might have given out currents thread_mask* again.*/task_work_cancel(current, irq_thread_dtor);return 0; }static int irq_wait_for_interrupt(struct irqaction *action) {set_current_state(TASK_INTERRUPTIBLE);while (!kthread_should_stop()) {if (test_and_clear_bit(IRQTF_RUNTHREAD,&action->thread_flags)) {------------判斷thread_flags是否設置IRQTF_RUNTHREAD標志位,如果設置則設置當前狀態TASK_RUNNING并返回0。此處和__irq_wake_thread中設置IRQTF_RUNTHREAD對應。__set_current_state(TASK_RUNNING);return 0;}schedule();-----------------------------------------換出CPU,在此等待睡眠set_current_state(TASK_INTERRUPTIBLE);}__set_current_state(TASK_RUNNING);return -1; }static irqreturn_t irq_thread_fn(struct irq_desc *desc,struct irqaction *action) {irqreturn_t ret;ret = action->thread_fn(action->irq, action->dev_id);---執行中斷內核線程函數,為request_threaded_irq注冊中斷參數thread_fn。irq_finalize_oneshot(desc, action);---------------------針對oneshot類型中斷收尾處理,主要是去屏蔽中斷。return ret; }

irq_finalize_oneshot()對ontshot類型的中斷進行收尾操作。

static void irq_finalize_oneshot(struct irq_desc *desc,struct irqaction *action) {if (!(desc->istate & IRQS_ONESHOT) ||action->handler == irq_forced_secondary_handler)return; again:chip_bus_lock(desc);raw_spin_lock_irq(&desc->lock);/** Implausible though it may be we need to protect us against* the following scenario:** The thread is faster done than the hard interrupt handler* on the other CPU. If we unmask the irq line then the* interrupt can come in again and masks the line, leaves due* to IRQS_INPROGRESS and the irq line is masked forever.** This also serializes the state of shared oneshot handlers* versus "desc->threads_onehsot |= action->thread_mask;" in* irq_wake_thread(). See the comment there which explains the* serialization.*/if (unlikely(irqd_irq_inprogress(&desc->irq_data))) {-----------必須等待硬件中斷處理程序清除IRQD_IRQ_INPROGRESS標志位,見handle_irq_event()。因為該標志位表示硬件中斷處理程序正在處理硬件中斷,直到硬件中斷處理完畢才會清除該標志。raw_spin_unlock_irq(&desc->lock);chip_bus_sync_unlock(desc);cpu_relax();goto again;}/** Now check again, whether the thread should run. Otherwise* we would clear the threads_oneshot bit of this thread which* was just set.*/if (test_bit(IRQTF_RUNTHREAD, &action->thread_flags))goto out_unlock;desc->threads_oneshot &= ~action->thread_mask;if (!desc->threads_oneshot && !irqd_irq_disabled(&desc->irq_data) &&irqd_irq_masked(&desc->irq_data))unmask_threaded_irq(desc);----------------------------------執行EOI或者去中斷屏蔽。out_unlock:raw_spin_unlock_irq(&desc->lock);chip_bus_sync_unlock(desc); }

至此一個中斷的執行完畢。

4.4 如何保證IRQS_ONESHOT不嵌套?

5. 注冊中斷

5.1 中斷、線程、中斷線程化

中斷處理程序包括上半部硬件中斷處理程序,下半部處理機制,包括軟中斷、tasklet、workqueue、中斷線程化。

當一個外設中斷發生后,內核會執行一個函數來響應該中斷,這個函數通常被稱為中斷處理程序或中斷服務例程。

上半部硬件中斷處理運行在中斷上下文中,要求快速完成并且退出中斷。

中斷線程化是實時Linux項目開發的一個新特性,目的是降低中斷處理對系統實時延遲的影響。

在LInux內核里,中斷具有最高優先級,只要有中斷發生,內核會暫停手頭的工作轉向中斷處理,等到所有掛起等待的中斷和軟終端處理完畢后才會執行進程調度,因此這個過程會造成實時任務得不到及時處理。

中斷上下文總是搶占進程上下文,中斷上下文不僅是中斷處理程序,還包括softirq、tasklet等,中斷上下文成了優化Linux實時性的最大挑戰之一。

5.2 中斷注冊接口

IRQF_*描述的中斷標志位用于request_threaded_irq()申請中斷時描述該中斷的特性。

IRQS_*的中斷標志位是位于struct irq_desc數據結構的istate成員,也即core_internal_state__do_not_mess_with_it。

IRQD_*是struct irq_data數據結構中的state_use_accessors成員一組中斷標志位,通常用于描述底層中斷狀態。

關于IRQF_ONESHOT特別解釋:必須在硬件中斷處理結束之后才能重新使能中斷;線程化中斷處理過程中保持中斷線處于關閉狀態,直到該中斷線上所有thread_fn執行完畢。

#define IRQF_TRIGGER_NONE 0x00000000 #define IRQF_TRIGGER_RISING 0x00000001---------------------------上升沿觸發 #define IRQF_TRIGGER_FALLING 0x00000002--------------------------下降沿觸發 #define IRQF_TRIGGER_HIGH 0x00000004-----------------------------高電平觸發 #define IRQF_TRIGGER_LOW 0x00000008------------------------------地電平觸發 #define IRQF_TRIGGER_MASK (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | \IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)--------四種觸發類型 #define IRQF_TRIGGER_PROBE 0x00000010#define IRQF_SHARED 0x00000080-------------------------------多個設備共享一個中斷號 #define IRQF_PROBE_SHARED 0x00000100-----------------------------中斷處理程序允許sharing mismatch發生 #define __IRQF_TIMER 0x00000200------------------------------標記一個時鐘中斷 #define IRQF_PERCPU 0x00000400-------------------------------屬于某個特定CPU的中斷 #define IRQF_NOBALANCING 0x00000800------------------------------禁止在多CPU之間做中斷均衡 #define IRQF_IRQPOLL 0x00001000------------------------------中斷被用作輪詢 #define IRQF_ONESHOT 0x00002000------------------------------一次性觸發中斷,不允許嵌套。 #define IRQF_NO_SUSPEND 0x00004000---------------------------在系統睡眠過程中不要關閉該中斷 #define IRQF_FORCE_RESUME 0x00008000-----------------------------在系統喚醒過程中必須搶孩子打開該中斷 #define IRQF_NO_THREAD 0x00010000----------------------------表示該中斷不會給線程化 #define IRQF_EARLY_RESUME 0x00020000 #define IRQF_COND_SUSPEND 0x00040000#define IRQF_TIMER (__IRQF_TIMER | IRQF_NO_SUSPEND | IRQF_NO_THREAD)enum {IRQS_AUTODETECT = 0x00000001,-------------------處于自動偵測狀態IRQS_SPURIOUS_DISABLED = 0x00000002,----------------被視為“偽中斷”并被禁用IRQS_POLL_INPROGRESS = 0x00000008,------------------正處于輪詢調用actionIRQS_ONESHOT = 0x00000020,----------------------表示只執行一次,由IRQF_ONESHOT轉換而來,在中斷線程化執行完成后需要小心對待,見irq_finalize_oneshot()。IRQS_REPLAY = 0x00000040,-----------------------重新發送一次中斷IRQS_WAITING = 0x00000080,----------------------處于等待狀態IRQS_PENDING = 0x00000200,----------------------該中斷被掛起IRQS_SUSPENDED = 0x00000800,--------------------該中斷被暫停 };enum {IRQD_TRIGGER_MASK = 0xf,-------------------------該中斷觸發類型IRQD_SETAFFINITY_PENDING = (1 << 8),IRQD_NO_BALANCING = (1 << 10),IRQD_PER_CPU = (1 << 11),IRQD_AFFINITY_SET = (1 << 12),IRQD_LEVEL = (1 << 13),IRQD_WAKEUP_STATE = (1 << 14),IRQD_MOVE_PCNTXT = (1 << 15),IRQD_IRQ_DISABLED = (1 << 16),--------------------該中斷處于關閉狀態IRQD_IRQ_MASKED = (1 << 17),------------------該中斷被屏蔽中IRQD_IRQ_INPROGRESS = (1 << 18),------------------該中斷正在被處理中IRQD_WAKEUP_ARMED = (1 << 19),IRQD_FORWARDED_TO_VCPU = (1 << 20), };

struct irqaction是每個中斷的irqaction描述符。

struct irqaction {irq_handler_t handler;-----------primary handler函數指針void *dev_id;----------------傳遞給中斷處理程序的參數void __percpu *percpu_dev_id;struct irqaction *next;irq_handler_t thread_fn;---------中斷線程處理程序的函數指針struct task_struct *thread;----------中斷線程的task_struct數據結構unsigned int irq;----------------Linux軟件中斷號unsigned int flags;--------------注冊中斷時用的中斷標志位,IRQF_*。unsigned long thread_flags;------中斷線程相關標志位unsigned long thread_mask;-------在共享中斷中,每一個action有一個比特位來表示。const char *name;----------------中斷線程名稱struct proc_dir_entry *dir; } ____cacheline_internodealigned_in_smp;

request_irq調用request_threaded_irq進行中斷注冊,只是少了一個thread_fn參數。這也是兩則的區別所在,request_irq不能注冊線程化中斷。

irq:Linux軟件中斷號,不是硬件中斷號。

handler:指primary handler,也即request_irq的中斷處理函數handler。

thread_fn:中斷線程化的處理函數。

irqflags:中斷標志位,見IRQF_*解釋。

devname:中斷名稱。

dev_id:傳遞給中斷處理程序的參數。

handler和thread_fn分別被賦給action->handler和action->thread_fn,組合如下:

?

handler

thread_fn

?

1

先執行handler,然后條件執行thread_fn。

2

×

等同于request_irq()

3

×

handler=irq_default_primary_handler

4

×

×

返回-EINVAL

很多request_threaded_irq()使用第3種組合,irq_default_primary_handler()返回IRQ_WAKE_THREAD,將工作交給thread_fn進行處理。

第2種組合相當于request_irq()。

第4種組合不被允許,因為中斷得不到任何處理。

第1種組合較復雜,在handler根據實際情況返回IRQ_WAKE_THREAD(喚醒內核中斷線程)或者IRQ_HANDLED(中斷已經處理完畢,不需要喚醒中斷內核線程)。

request_threaded_irq()對參數進行檢查之后,分配struct irqaction并填充,然后將注冊工作交給__setup_irq()。

static inline int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev) {return request_threaded_irq(irq, handler, NULL, flags, name, dev); }int request_threaded_irq(unsigned int irq, irq_handler_t handler,irq_handler_t thread_fn, unsigned long irqflags,const char *devname, void *dev_id) { ...if (((irqflags & IRQF_SHARED) && !dev_id) ||-----------------------------共享中斷設備必須傳遞啊dev_id參數來區分是哪個共享外設的中斷(!(irqflags & IRQF_SHARED) && (irqflags & IRQF_COND_SUSPEND)) ||((irqflags & IRQF_NO_SUSPEND) && (irqflags & IRQF_COND_SUSPEND)))return -EINVAL;desc = irq_to_desc(irq);--------------------------------------------------通過Linux中斷號找到對應中斷描述符struct irq_desc。if (!desc)return -EINVAL; ...if (!handler) {if (!thread_fn)return -EINVAL;---------------------------------------------------handler和thread_fn不能同時為NULLhandler = irq_default_primary_handler;--------------------------------沒有設置handler,irq_default_primary_handler()默認返回IRQ_WAKE_THREAD。}action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);-------------------分配struct irqaction,并填充相應成員if (!action)return -ENOMEM;action->handler = handler;action->thread_fn = thread_fn;action->flags = irqflags;action->name = devname;action->dev_id = dev_id;chip_bus_lock(desc);-------------------------------------------------------調用desc->irq_data.chip->irq_bus_lock()進行加鎖保護retval = __setup_irq(irq, desc, action);chip_bus_sync_unlock(desc);if (retval)kfree(action); ...return retval; }

5.3 __setup_irq

一張圖

__setup_irq()首先做參數檢查,然后根據需要創建中斷內核線程,這期間處理中斷嵌套、oneshot、中斷共享等問題。

還設置了中斷觸發類型設置,中斷使能等工作。最后根據需要喚醒中斷內核線程,并創建此中斷相關sysfs節點。

/** Internal function to register an irqaction - typically used to* allocate special interrupts that are part of the architecture.*/ static int __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) {struct irqaction *old, **old_ptr;unsigned long flags, thread_mask = 0;int ret, nested, shared = 0;cpumask_var_t mask;if (!desc)return -EINVAL;if (desc->irq_data.chip == &no_irq_chip)----------------------表示沒有正確初始化中斷控制器,對于GICv2在gic_irq_domain_alloc()中指定chip為gic_chip。return -ENOSYS;if (!try_module_get(desc->owner))return -ENODEV;/** Check whether the interrupt nests into another interrupt* thread.*/nested = irq_settings_is_nested_thread(desc);-----------------對于設置了_IRQ_NESTED_THREAD嵌套類型的中斷描述符,必須指定thread_fn。if (nested) {if (!new->thread_fn) {ret = -EINVAL;goto out_mput;}/** Replace the primary handler which was provided from* the driver for non nested interrupt handling by the* dummy function which warns when called.*/new->handler = irq_nested_primary_handler;} else {if (irq_settings_can_thread(desc))-----------------------判斷該中斷是否可以被線程化,如果沒有設置_IRQ_NOTHREAD表示可以被強制線程化。irq_setup_forced_threading(new);}/** Create a handler thread when a thread function is supplied* and the interrupt does not nest into another interrupt* thread.*/if (new->thread_fn && !nested) {-----------------------------對不支持嵌套的線程化中斷創建一個內核線程,實時SCHED_FIFO,優先級為50的實時線程。struct task_struct *t;static const struct sched_param param = {.sched_priority = MAX_USER_RT_PRIO/2,};t = kthread_create(irq_thread, new, "irq/%d-%s", irq,new->name);-----------------------------------由irq、中斷號、中斷名組成的中斷線程名,處理函數是irq_thread()。if (IS_ERR(t)) {ret = PTR_ERR(t);goto out_mput;}sched_setscheduler_nocheck(t, SCHED_FIFO, &param);get_task_struct(t);new->thread = t;set_bit(IRQTF_AFFINITY, &new->thread_flags);}if (!alloc_cpumask_var(&mask, GFP_KERNEL)) {ret = -ENOMEM;goto out_thread;}/** Drivers are often written to work w/o knowledge about the* underlying irq chip implementation, so a request for a* threaded irq without a primary hard irq context handler* requires the ONESHOT flag to be set. Some irq chips like* MSI based interrupts are per se one shot safe. Check the* chip flags, so we can avoid the unmask dance at the end of* the threaded handler for those.*/if (desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)----------表示該中斷控制器不支持中斷嵌套,所以flags去掉IRQF_ONESHOT。new->flags &= ~IRQF_ONESHOT;raw_spin_lock_irqsave(&desc->lock, flags);old_ptr = &desc->action;old = *old_ptr;if (old) {-----------------------------------------------------old指向desc->action指向的鏈表,old不為空說明已經有中斷添加到中斷描述符irq_desc中,說明這是一個共享中斷。shared=1。 .../* add new interrupt at end of irq queue */do {/** Or all existing action->thread_mask bits,* so we can find the next zero bit for this* new action.*/thread_mask |= old->thread_mask;old_ptr = &old->next;old = *old_ptr;} while (old);shared = 1;}/** Setup the thread mask for this irqaction for ONESHOT. For* !ONESHOT irqs the thread mask is 0 so we can avoid a* conditional in irq_wake_thread().*/if (new->flags & IRQF_ONESHOT) {/** Unlikely to have 32 resp 64 irqs sharing one line,* but who knows.*/if (thread_mask == ~0UL) {ret = -EBUSY;goto out_mask;}new->thread_mask = 1 << ffz(thread_mask);} else if (new->handler == irq_default_primary_handler &&---------非IRQF_ONESHOT類型中斷,且handler使用默認irq_default_primary_handler(),如果中斷觸發類型是LEVEL,如果中斷出發后不清中斷容易引發中斷風暴。提醒驅動開發者,沒有primary handler且中斷控制器不支持硬件oneshot,必須顯式指定IRQF_ONESHOT表示位。!(desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)) {pr_err("Threaded irq requested with handler=NULL and !ONESHOT for irq %d\n",irq);ret = -EINVAL;goto out_mask;}if (!shared) {-------------------------------------------------非共享中斷情況ret = irq_request_resources(desc);if (ret) {pr_err("Failed to request resources for %s (irq %d) on irqchip %s\n",new->name, irq, desc->irq_data.chip->name);goto out_mask;}init_waitqueue_head(&desc->wait_for_threads);/* Setup the type (level, edge polarity) if configured: */if (new->flags & IRQF_TRIGGER_MASK) {ret = __irq_set_trigger(desc, irq,-------------------調用gic_chip->irq_set_type設置中斷觸發類型。new->flags & IRQF_TRIGGER_MASK);if (ret)goto out_mask;}desc->istate &= ~(IRQS_AUTODETECT | IRQS_SPURIOUS_DISABLED | \IRQS_ONESHOT | IRQS_WAITING);irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);---------清IRQD_IRQ_INPROGRESS標志位if (new->flags & IRQF_PERCPU) {irqd_set(&desc->irq_data, IRQD_PER_CPU);irq_settings_set_per_cpu(desc);}if (new->flags & IRQF_ONESHOT)desc->istate |= IRQS_ONESHOT;if (irq_settings_can_autoenable(desc))irq_startup(desc, true);else/* Undo nested disables: */desc->depth = 1;/* Exclude IRQ from balancing if requested */if (new->flags & IRQF_NOBALANCING) {irq_settings_set_no_balancing(desc);irqd_set(&desc->irq_data, IRQD_NO_BALANCING);}/* Set default affinity mask once everything is setup */setup_affinity(irq, desc, mask);} else if (new->flags & IRQF_TRIGGER_MASK) { ..}new->irq = irq;*old_ptr = new;irq_pm_install_action(desc, new);/* Reset broken irq detection when installing new handler */desc->irq_count = 0;desc->irqs_unhandled = 0;/** Check whether we disabled the irq via the spurious handler* before. Reenable it and give it another chance.*/if (shared && (desc->istate & IRQS_SPURIOUS_DISABLED)) {desc->istate &= ~IRQS_SPURIOUS_DISABLED;__enable_irq(desc, irq);}raw_spin_unlock_irqrestore(&desc->lock, flags);/** Strictly no need to wake it up, but hung_task complains* when no hard interrupt wakes the thread up.*/if (new->thread)wake_up_process(new->thread);------------------------------如果該中斷被線程化,那么就喚醒該內核線程。這里每個中斷對應一個線程。register_irq_proc(irq, desc);----------------------------------創建/proc/irq/xxx/目錄及其節點。new->dir = NULL;register_handler_proc(irq, new);-------------------------------以action->name創建目錄free_cpumask_var(mask);return 0; ... }

irq_setup_forced_threading()判斷是否強制當前中斷線程化,然后對thread_flags置位IRQTF_FORCED_THREAD表示此中斷被強制線程化。

將原來的primary handler弄到中斷線程中去執行,原來的primary handler換成irq_default_primary_handler。

并設置secondary的primary handler指向irq_forced_secondary_handler(),原來的thread_fn移到secondary的中線程中執行。

static int irq_setup_forced_threading(struct irqaction *new) {if (!force_irqthreads)---------------------------------------------如果內核啟動參數包含threadirqs,則支持強制線程化。或者CONFIG_PREEMPT_RT_BASE實時補丁打開,這里也強制線程化。return 0;if (new->flags & (IRQF_NO_THREAD | IRQF_PERCPU | IRQF_ONESHOT))----和線程化矛盾的標志位。return 0;new->flags |= IRQF_ONESHOT;----------------------------------------強制線程化的中斷都置位IRQF_ONESHOT。if (new->handler != irq_default_primary_handler && new->thread_fn) {/* Allocate the secondary action */new->secondary = kzalloc(sizeof(struct irqaction), GFP_KERNEL);if (!new->secondary)return -ENOMEM;new->secondary->handler = irq_forced_secondary_handler;new->secondary->thread_fn = new->thread_fn;new->secondary->dev_id = new->dev_id;new->secondary->irq = new->irq;new->secondary->name = new->name;}/* Deal with the primary handler */set_bit(IRQTF_FORCED_THREAD, &new->thread_flags);new->thread_fn = new->handler;new->handler = irq_default_primary_handler;return 0; }

setup_irq()、request_threaded_irq()、request_irq()都是對__setup_irq()的包裹。

request_irq()調用request_threaded_irq(),只是少了thread_fn。

request_thraded_irq()和setup_irq()的區別在于,setup_irq()入參是struct irqaction ,而request_threaded_irq()在內部組裝struct irqaction。

6. 一個中斷的生命

經過上面的分析可以看出一個中斷從產生、執行,到最終結束的流程。這里我們用樹形代碼路徑來簡要分析一下一個中斷的生命周期。

vector_irq()->vector_irq()->__irq_svc()->svc_entry()---------------------------------------------------------------保護中斷現場->irq_handler()->gic_handle_irq()--------------------------------------------具體到GIC中斷控制器對應的就是gic_handle_irq(),此處從架構相關進入了GIC相關處理。->GIC_CPU_INTACK------------------------------------------------讀取IAR寄存器,響應中斷->handle_domain_irq()->irq_enter()--------------------------------------------------------進入硬中斷上下文->generic_handle_irq()->generic_handle_irq_desc()->handle_fasteoi_irq()--------------------根據中斷號分辨不同類型的中斷,對應不同處理函數,這里中斷號取大于等于32。->handle_irq_event()->handle_irq_event_percpu()->action->handler()---------------------------對應到特定中斷的處理函數,即上半部->__irq_wake_thread()-----------------------------------------------------如果中斷函數處理返回IRQ_WAKE_THREAD,則喚醒中斷線程進行處理,但不是立即執行中斷線程。->irq_exit()---------------------------------------退出硬中斷上下文。視情況處理軟中斷。->invoke_softirq()-----------------處理軟中斷,超出一定條件任務就會交給軟中斷線程處理。->GIC_CPU_EOI--------------------------寫EOI寄存器,表示結束中斷。至此GIC才會接收新的硬件中斷,此前一直是屏蔽硬件中斷的。->svc_exit-----------------------------------------------------------------恢復中斷現場

從上面的分析可以看出:

  • 中斷上半部的處理是關硬件中斷的,這里的關硬件中斷是GIC就不接收中斷處理。直到寫EOI之后,GIC仲裁單元才會重新選擇中斷進行處理。
  • 軟中斷運行于軟中斷上下文中,但是仍然是關硬件中斷的,這里需要特別注意,軟中斷需要快速處理并且不能睡眠。
  • 不是所有軟中斷都運行于軟中斷上下文中,部分軟中斷任務可能會交給ksoftirqd線程處理。
  • 包括IRQ_WAKE_THREAD、ksoftirqd、woker等喚醒線程的情況,都不會在中斷上下文中進行處理。中斷上下文中所做的處理只是喚醒,執行時機交給系統調度。
  • 如果要提高Linux實時性,有兩個要點:一是將上半部線程化;另一個是將軟中斷都交給ksoftirqd線程處理。
與50位技術專家面對面20年技術見證,附贈技術全景圖

總結

以上是生活随笔為你收集整理的arm Linux 中断管理机制的全部內容,希望文章能夠幫你解決所遇到的問題。

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

欧美丰满熟妇xxxx性ppx人交 | 国精产品一区二区三区 | 激情爆乳一区二区三区 | 亚洲精品成人福利网站 | 色婷婷av一区二区三区之红樱桃 | 国产成人无码av片在线观看不卡 | 天海翼激烈高潮到腰振不止 | 午夜熟女插插xx免费视频 | 天堂а√在线地址中文在线 | 蜜桃臀无码内射一区二区三区 | 久久国语露脸国产精品电影 | 2020最新国产自产精品 | 丰满岳乱妇在线观看中字无码 | 国产三级精品三级男人的天堂 | 在线 国产 欧美 亚洲 天堂 | 国产精品视频免费播放 | 六月丁香婷婷色狠狠久久 | 99久久婷婷国产综合精品青草免费 | 欧美老人巨大xxxx做受 | 亚洲中文字幕在线无码一区二区 | 最新国产麻豆aⅴ精品无码 | 图片区 小说区 区 亚洲五月 | 丰满岳乱妇在线观看中字无码 | 东北女人啪啪对白 | 精品久久8x国产免费观看 | 亚洲欧洲日本无在线码 | 国产精品免费大片 | 1000部啪啪未满十八勿入下载 | 日韩亚洲欧美中文高清在线 | 中文字幕日韩精品一区二区三区 | 熟妇激情内射com | 亚洲成a人片在线观看无码3d | 欧洲精品码一区二区三区免费看 | 秋霞成人午夜鲁丝一区二区三区 | 呦交小u女精品视频 | 日韩精品无码免费一区二区三区 | 色婷婷香蕉在线一区二区 | 欧美性猛交内射兽交老熟妇 | 亚洲国产av精品一区二区蜜芽 | 成 人影片 免费观看 | 欧美野外疯狂做受xxxx高潮 | 蜜桃臀无码内射一区二区三区 | 一个人看的视频www在线 | 国产农村乱对白刺激视频 | 国精产品一品二品国精品69xx | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | 免费国产成人高清在线观看网站 | 少妇愉情理伦片bd | 狠狠cao日日穞夜夜穞av | 伊人久久大香线蕉av一区二区 | 国产精品无套呻吟在线 | 亚洲成色在线综合网站 | 亚洲欧美色中文字幕在线 | 日本精品高清一区二区 | 成人免费视频在线观看 | 在线精品亚洲一区二区 | 国产精品爱久久久久久久 | 精品乱码久久久久久久 | 成人无码精品一区二区三区 | 久久国产劲爆∧v内射 | 亚洲一区二区三区播放 | 欧美黑人性暴力猛交喷水 | www国产精品内射老师 | 国产三级久久久精品麻豆三级 | 中文字幕av日韩精品一区二区 | 少妇性l交大片欧洲热妇乱xxx | 国产后入清纯学生妹 | 波多野结衣乳巨码无在线观看 | 欧美精品无码一区二区三区 | 中文字幕乱码人妻无码久久 | 无码一区二区三区在线观看 | 国产精品美女久久久久av爽李琼 | 国产偷抇久久精品a片69 | 久在线观看福利视频 | 亚洲色偷偷男人的天堂 | 性生交大片免费看女人按摩摩 | 人人妻人人澡人人爽人人精品 | 婷婷丁香六月激情综合啪 | 色偷偷人人澡人人爽人人模 | 精品无码一区二区三区的天堂 | 国产精品人妻一区二区三区四 | 国产偷国产偷精品高清尤物 | 人妻少妇精品无码专区动漫 | 久久久精品国产sm最大网站 | 国产真实伦对白全集 | 国产情侣作爱视频免费观看 | 无码福利日韩神码福利片 | 熟女俱乐部五十路六十路av | 日本乱人伦片中文三区 | 国产熟妇另类久久久久 | 亚洲精品国产精品乱码不卡 | 国产两女互慰高潮视频在线观看 | 丰满肥臀大屁股熟妇激情视频 | 伊人久久大香线焦av综合影院 | 亚洲色偷偷偷综合网 | 最新版天堂资源中文官网 | 亚欧洲精品在线视频免费观看 | 无码一区二区三区在线观看 | 内射老妇bbwx0c0ck | 无码毛片视频一区二区本码 | 日韩人妻系列无码专区 | 成人av无码一区二区三区 | 狠狠噜狠狠狠狠丁香五月 | 亚洲乱码国产乱码精品精 | 丁香花在线影院观看在线播放 | 亚洲色成人中文字幕网站 | 日韩人妻无码一区二区三区久久99 | 97se亚洲精品一区 | 国产精品成人av在线观看 | 婷婷丁香五月天综合东京热 | 国产亚洲精品久久久久久久 | 日韩精品无码一区二区中文字幕 | 一二三四在线观看免费视频 | 无码帝国www无码专区色综合 | 人妻互换免费中文字幕 | 日日天日日夜日日摸 | 欧美日韩视频无码一区二区三 | 人人爽人人澡人人人妻 | 久精品国产欧美亚洲色aⅴ大片 | 国产性生大片免费观看性 | 久久久久久国产精品无码下载 | 成人精品天堂一区二区三区 | 亚洲色欲色欲欲www在线 | 少妇高潮一区二区三区99 | 欧美 亚洲 国产 另类 | 熟妇人妻中文av无码 | 无遮挡国产高潮视频免费观看 | 在线a亚洲视频播放在线观看 | 久久久久亚洲精品男人的天堂 | 久久精品丝袜高跟鞋 | 国产人妻久久精品二区三区老狼 | 国产精品美女久久久久av爽李琼 | 狠狠色噜噜狠狠狠7777奇米 | 极品尤物被啪到呻吟喷水 | 国内精品一区二区三区不卡 | 欧美怡红院免费全部视频 | 草草网站影院白丝内射 | 久久综合香蕉国产蜜臀av | aa片在线观看视频在线播放 | 青春草在线视频免费观看 | 亚洲精品中文字幕乱码 | 亚洲一区二区观看播放 | 久9re热视频这里只有精品 | 最新国产乱人伦偷精品免费网站 | 亚洲一区二区三区在线观看网站 | 日本大香伊一区二区三区 | 久久www免费人成人片 | 亚洲 高清 成人 动漫 | 亚洲精品一区二区三区婷婷月 | 久久精品国产一区二区三区 | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 久久无码专区国产精品s | 在线看片无码永久免费视频 | 亚洲国产一区二区三区在线观看 | 少妇太爽了在线观看 | 国产偷抇久久精品a片69 | 免费无码的av片在线观看 | 两性色午夜视频免费播放 | 日本精品人妻无码免费大全 | 国产亚洲精品久久久久久久久动漫 | 中文字幕无码人妻少妇免费 | 日本高清一区免费中文视频 | 波多野结衣av在线观看 | 亚洲精品成人福利网站 | 我要看www免费看插插视频 | 亚洲精品www久久久 | 欧美精品无码一区二区三区 | 一本大道久久东京热无码av | 玩弄人妻少妇500系列视频 | 亚洲另类伦春色综合小说 | 无码播放一区二区三区 | 中文字幕乱码人妻无码久久 | 亚洲人成网站色7799 | 国产精品无码一区二区桃花视频 | 亚洲精品www久久久 | 强开小婷嫩苞又嫩又紧视频 | 日韩视频 中文字幕 视频一区 | 正在播放东北夫妻内射 | 无码任你躁久久久久久久 | 四虎国产精品免费久久 | 亚洲精品一区二区三区四区五区 | 精品国精品国产自在久国产87 | 天天摸天天透天天添 | 国精产品一区二区三区 | 国产午夜精品一区二区三区嫩草 | 国产内射爽爽大片视频社区在线 | 精品厕所偷拍各类美女tp嘘嘘 | 成人免费视频一区二区 | 国产精品第一区揄拍无码 | 免费看男女做好爽好硬视频 | 男女作爱免费网站 | 初尝人妻少妇中文字幕 | 人妻尝试又大又粗久久 | 无遮挡啪啪摇乳动态图 | 国产人妖乱国产精品人妖 | 久久久精品人妻久久影视 | 久久精品成人欧美大片 | 少妇愉情理伦片bd | 76少妇精品导航 | 无遮挡啪啪摇乳动态图 | 丰满肥臀大屁股熟妇激情视频 | 国产热a欧美热a在线视频 | 亚洲成av人片天堂网无码】 | 丰满肥臀大屁股熟妇激情视频 | 亚洲日韩中文字幕在线播放 | 国产激情综合五月久久 | 中文毛片无遮挡高清免费 | 欧美日韩一区二区免费视频 | 亚洲熟妇色xxxxx亚洲 | 成人动漫在线观看 | 乱码av麻豆丝袜熟女系列 | 欧洲熟妇色 欧美 | 午夜福利一区二区三区在线观看 | 久久久久国色av免费观看性色 | 国产精品亚洲lv粉色 | 风流少妇按摩来高潮 | 在线成人www免费观看视频 | 秋霞成人午夜鲁丝一区二区三区 | 亚洲国产午夜精品理论片 | 国产香蕉尹人综合在线观看 | 久久精品国产一区二区三区 | 免费人成在线视频无码 | 东北女人啪啪对白 | 中文字幕无码av激情不卡 | 中文字幕无码日韩专区 | 两性色午夜免费视频 | 亚洲中文无码av永久不收费 | 亚洲国产精品成人久久蜜臀 | 无套内射视频囯产 | 国产成人无码专区 | 国产人妻大战黑人第1集 | 天堂无码人妻精品一区二区三区 | 亚洲区欧美区综合区自拍区 | 欧美一区二区三区视频在线观看 | 国产精品人人妻人人爽 | 国产熟妇高潮叫床视频播放 | 久久久成人毛片无码 | 国产手机在线αⅴ片无码观看 | 在线播放免费人成毛片乱码 | 三级4级全黄60分钟 | 少妇无码吹潮 | 婷婷综合久久中文字幕蜜桃三电影 | 国产亚洲日韩欧美另类第八页 | 中文字幕乱妇无码av在线 | 亚洲男人av天堂午夜在 | 久久久久99精品成人片 | 欧洲精品码一区二区三区免费看 | 久久99热只有频精品8 | 精品久久久中文字幕人妻 | 国产人妻精品午夜福利免费 | 日本一区二区三区免费播放 | 欧美xxxx黑人又粗又长 | 久久综合久久自在自线精品自 | 亚洲国产精品一区二区第一页 | 极品嫩模高潮叫床 | 婷婷丁香五月天综合东京热 | 在线а√天堂中文官网 | 亚洲国产欧美日韩精品一区二区三区 | 久久这里只有精品视频9 | 亚洲自偷精品视频自拍 | 久久zyz资源站无码中文动漫 | 精品亚洲成av人在线观看 | 国产成人无码av在线影院 | 青青青手机频在线观看 | 国产亚洲欧美在线专区 | 青青久在线视频免费观看 | 中文字幕无码视频专区 | 日韩人妻少妇一区二区三区 | 久久zyz资源站无码中文动漫 | 久久无码专区国产精品s | 亚洲欧美日韩综合久久久 | 亚洲色无码一区二区三区 | 中文字幕日产无线码一区 | 国产av久久久久精东av | 男女猛烈xx00免费视频试看 | 国产精品视频免费播放 | 国产97色在线 | 免 | 99riav国产精品视频 | 免费国产黄网站在线观看 | 麻豆蜜桃av蜜臀av色欲av | 欧美国产日韩久久mv | 国产成人精品一区二区在线小狼 | 午夜嘿嘿嘿影院 | 亚洲综合另类小说色区 | 久久精品一区二区三区四区 | 成人欧美一区二区三区 | 国产亚洲精品久久久久久国模美 | 国产成人无码午夜视频在线观看 | 国产精品无码mv在线观看 | 亚洲日韩av一区二区三区中文 | 亚洲综合无码一区二区三区 | 日本一区二区更新不卡 | 久久久久久九九精品久 | 亚洲 欧美 激情 小说 另类 | 精品无码成人片一区二区98 | 国产成人无码av片在线观看不卡 | 亚洲综合伊人久久大杳蕉 | 激情内射日本一区二区三区 | 少妇人妻大乳在线视频 | 亚洲综合无码一区二区三区 | 精品久久久久香蕉网 | 鲁大师影院在线观看 | 一本大道久久东京热无码av | 亚洲色大成网站www | 日韩成人一区二区三区在线观看 | 日日夜夜撸啊撸 | 无遮挡国产高潮视频免费观看 | 欧美日本精品一区二区三区 | 久激情内射婷内射蜜桃人妖 | 精品国产精品久久一区免费式 | 国产亚洲精品久久久久久 | 又色又爽又黄的美女裸体网站 | 国产亚av手机在线观看 | 97夜夜澡人人爽人人喊中国片 | 99麻豆久久久国产精品免费 | a片免费视频在线观看 | 强开小婷嫩苞又嫩又紧视频 | 午夜精品久久久内射近拍高清 | 人妻aⅴ无码一区二区三区 | 国产成人精品视频ⅴa片软件竹菊 | 久9re热视频这里只有精品 | 国产精品久久久午夜夜伦鲁鲁 | 亚洲精品一区二区三区大桥未久 | 久久99精品久久久久婷婷 | 亚洲国产一区二区三区在线观看 | 日本一卡二卡不卡视频查询 | 亚洲成a人片在线观看无码 | 久久午夜无码鲁丝片秋霞 | 东京热无码av男人的天堂 | 久久久精品人妻久久影视 | 精品久久久无码中文字幕 | 国内精品九九久久久精品 | 久久精品国产大片免费观看 | 在线 国产 欧美 亚洲 天堂 | 久久久久免费看成人影片 | 国产精华av午夜在线观看 | 国産精品久久久久久久 | 午夜福利不卡在线视频 | 久久人人爽人人爽人人片ⅴ | 一本久道高清无码视频 | 亚洲综合精品香蕉久久网 | 一本久道久久综合狠狠爱 | 内射爽无广熟女亚洲 | 精品水蜜桃久久久久久久 | 国产肉丝袜在线观看 | 女人和拘做爰正片视频 | 55夜色66夜色国产精品视频 | 亚欧洲精品在线视频免费观看 | av人摸人人人澡人人超碰下载 | 欧美精品在线观看 | 福利一区二区三区视频在线观看 | 日本丰满护士爆乳xxxx | 中文字幕无码av激情不卡 | 扒开双腿吃奶呻吟做受视频 | 九月婷婷人人澡人人添人人爽 | 亚洲综合无码一区二区三区 | 久久综合给合久久狠狠狠97色 | 亚洲综合无码一区二区三区 | 99er热精品视频 | 乱码午夜-极国产极内射 | 免费无码肉片在线观看 | 成熟女人特级毛片www免费 | 国产黄在线观看免费观看不卡 | 国产内射老熟女aaaa | 国产精品久久久久7777 | 国产在线精品一区二区三区直播 | 国产av无码专区亚洲awww | 亚洲欧美日韩综合久久久 | 国产精品资源一区二区 | 亚洲无人区一区二区三区 | 成人无码精品一区二区三区 | 娇妻被黑人粗大高潮白浆 | 色综合视频一区二区三区 | 男人和女人高潮免费网站 | 国产精品亚洲lv粉色 | 在线播放亚洲第一字幕 | 亚洲va中文字幕无码久久不卡 | av人摸人人人澡人人超碰下载 | 欧美日韩综合一区二区三区 | 人妻中文无码久热丝袜 | 自拍偷自拍亚洲精品被多人伦好爽 | 色综合久久中文娱乐网 | 99久久精品日本一区二区免费 | 国产成人久久精品流白浆 | 欧美真人作爱免费视频 | 亚洲欧美日韩国产精品一区二区 | 日韩人妻无码中文字幕视频 | 亚洲成av人片在线观看无码不卡 | 亚洲va中文字幕无码久久不卡 | 一区二区传媒有限公司 | 久久97精品久久久久久久不卡 | 高中生自慰www网站 | 人人澡人人透人人爽 | 久久精品无码一区二区三区 | 内射巨臀欧美在线视频 | 老熟妇仑乱视频一区二区 | 久久久久久国产精品无码下载 | 中文字幕人妻丝袜二区 | 久久久久久av无码免费看大片 | 国产成人久久精品流白浆 | 88国产精品欧美一区二区三区 | 中文无码精品a∨在线观看不卡 | 老熟妇乱子伦牲交视频 | 亚洲va中文字幕无码久久不卡 | 久久精品99久久香蕉国产色戒 | 99久久久无码国产精品免费 | 九一九色国产 | 午夜福利不卡在线视频 | 欧美亚洲国产一区二区三区 | 国产欧美亚洲精品a | 内射后入在线观看一区 | 亚洲综合伊人久久大杳蕉 | 国产性生交xxxxx无码 | 国内精品人妻无码久久久影院 | 国产国语老龄妇女a片 | 无码国产激情在线观看 | 久久久久成人片免费观看蜜芽 | 婷婷色婷婷开心五月四房播播 | 亚洲自偷自拍另类第1页 | 久久国语露脸国产精品电影 | 女人被男人爽到呻吟的视频 | 内射巨臀欧美在线视频 | 国产黄在线观看免费观看不卡 | 精品国产青草久久久久福利 | 亚洲国产av美女网站 | 波多野结衣av一区二区全免费观看 | 欧美熟妇另类久久久久久不卡 | 国精品人妻无码一区二区三区蜜柚 | 色婷婷综合激情综在线播放 | 久久久久se色偷偷亚洲精品av | 国产区女主播在线观看 | 久久久久成人精品免费播放动漫 | 国产农村乱对白刺激视频 | 国产人妻精品一区二区三区 | 高潮毛片无遮挡高清免费 | av无码电影一区二区三区 | 午夜熟女插插xx免费视频 | 国产 精品 自在自线 | 九月婷婷人人澡人人添人人爽 | 丰满少妇人妻久久久久久 | 国产成人无码av一区二区 | 免费看男女做好爽好硬视频 | 亚洲国产精品一区二区第一页 | 女人色极品影院 | 国产午夜亚洲精品不卡下载 | 亚洲人成影院在线无码按摩店 | 精品夜夜澡人妻无码av蜜桃 | 亚洲 a v无 码免 费 成 人 a v | 娇妻被黑人粗大高潮白浆 | 日韩人妻无码一区二区三区久久99 | 精品日本一区二区三区在线观看 | 永久黄网站色视频免费直播 | 最近中文2019字幕第二页 | 人人澡人人透人人爽 | 又粗又大又硬毛片免费看 | 特大黑人娇小亚洲女 | 玩弄少妇高潮ⅹxxxyw | 特级做a爰片毛片免费69 | 人妻aⅴ无码一区二区三区 | 一本加勒比波多野结衣 | 男女性色大片免费网站 | 国产精品久久福利网站 | 色婷婷久久一区二区三区麻豆 | 未满成年国产在线观看 | 日日天日日夜日日摸 | 性做久久久久久久久 | 成人亚洲精品久久久久 | 无码av免费一区二区三区试看 | 精品国产精品久久一区免费式 | 国产精品成人av在线观看 | 中文亚洲成a人片在线观看 | 国产人妻人伦精品 | 国内精品人妻无码久久久影院蜜桃 | 帮老师解开蕾丝奶罩吸乳网站 | 国产内射老熟女aaaa | 老太婆性杂交欧美肥老太 | av无码久久久久不卡免费网站 | 欧美 日韩 人妻 高清 中文 | 亚洲国产av精品一区二区蜜芽 | 影音先锋中文字幕无码 | 日韩av无码一区二区三区不卡 | 日韩视频 中文字幕 视频一区 | 国产麻豆精品精东影业av网站 | 国精产品一品二品国精品69xx | 少妇人妻偷人精品无码视频 | 色综合久久久无码中文字幕 | 免费国产成人高清在线观看网站 | 国产激情艳情在线看视频 | 亚洲欧美日韩成人高清在线一区 | 亚洲日韩精品欧美一区二区 | 人妻少妇精品无码专区二区 | 国产高清av在线播放 | 欧美喷潮久久久xxxxx | 特黄特色大片免费播放器图片 | 在线观看欧美一区二区三区 | 精品人妻人人做人人爽 | 久久精品中文闷骚内射 | 国产精品美女久久久网av | 国产69精品久久久久app下载 | 国产xxx69麻豆国语对白 | 国产人妻精品午夜福利免费 | 色偷偷av老熟女 久久精品人妻少妇一区二区三区 | 亚洲精品午夜无码电影网 | 九九在线中文字幕无码 | 国产97人人超碰caoprom | 两性色午夜视频免费播放 | 97资源共享在线视频 | 99久久精品国产一区二区蜜芽 | 国产性生大片免费观看性 | 国产成人精品必看 | 国产精品福利视频导航 | 国产综合在线观看 | 国产午夜精品一区二区三区嫩草 | 亚洲一区二区三区 | 99re在线播放 | 一本久道久久综合婷婷五月 | 欧美丰满熟妇xxxx性ppx人交 | 久久 国产 尿 小便 嘘嘘 | 久久亚洲日韩精品一区二区三区 | 久久综合香蕉国产蜜臀av | 久久国产自偷自偷免费一区调 | 九九综合va免费看 | 在线播放免费人成毛片乱码 | 无套内谢的新婚少妇国语播放 | 性生交片免费无码看人 | 国产成人精品视频ⅴa片软件竹菊 | 色综合久久久无码中文字幕 | 人人爽人人爽人人片av亚洲 | 国产在线一区二区三区四区五区 | 内射爽无广熟女亚洲 | 亚洲精品一区二区三区在线 | 在线精品亚洲一区二区 | 亚洲国精产品一二二线 | 一本精品99久久精品77 | 熟女俱乐部五十路六十路av | 日日橹狠狠爱欧美视频 | 国产精品第一区揄拍无码 | 97夜夜澡人人爽人人喊中国片 | 久久99精品国产.久久久久 | 激情内射日本一区二区三区 | av香港经典三级级 在线 | 色诱久久久久综合网ywww | 亚洲无人区午夜福利码高清完整版 | 天天躁日日躁狠狠躁免费麻豆 | 婷婷五月综合激情中文字幕 | 乌克兰少妇xxxx做受 | 久久国内精品自在自线 | 日韩精品无码一本二本三本色 | 亚洲精品一区二区三区在线 | 99久久久无码国产精品免费 | 亚洲综合另类小说色区 | ass日本丰满熟妇pics | 97色伦图片97综合影院 | 中文字幕av无码一区二区三区电影 | 动漫av网站免费观看 | 无码人妻丰满熟妇区五十路百度 | 亚洲国产精品美女久久久久 | 免费无码av一区二区 | 无码国产乱人伦偷精品视频 | 好男人社区资源 | 久久久久久国产精品无码下载 | 亚洲成在人网站无码天堂 | 国产卡一卡二卡三 | 麻豆果冻传媒2021精品传媒一区下载 | 欧美熟妇另类久久久久久多毛 | 亚洲色欲色欲欲www在线 | 国产精品人妻一区二区三区四 | 久久精品99久久香蕉国产色戒 | 精品少妇爆乳无码av无码专区 | 久久久www成人免费毛片 | 97久久国产亚洲精品超碰热 | 丁香啪啪综合成人亚洲 | 高潮毛片无遮挡高清免费 | 2020久久超碰国产精品最新 | 亚洲天堂2017无码 | 人人爽人人爽人人片av亚洲 | 久久久中文字幕日本无吗 | 亚洲人成人无码网www国产 | 5858s亚洲色大成网站www | 国产精品高潮呻吟av久久 | 中文字幕日产无线码一区 | 精品午夜福利在线观看 | 2019nv天堂香蕉在线观看 | 丰满人妻一区二区三区免费视频 | 久久久久亚洲精品男人的天堂 | 国产成人精品无码播放 | 又湿又紧又大又爽a视频国产 | 久久精品国产精品国产精品污 | 国产成人无码专区 | 性史性农村dvd毛片 | 亚洲午夜福利在线观看 | 亚洲精品久久久久中文第一幕 | 亚拍精品一区二区三区探花 | 好男人www社区 | 99久久精品国产一区二区蜜芽 | 免费看男女做好爽好硬视频 | 国产精品.xx视频.xxtv | 亚洲成a人一区二区三区 | 成人毛片一区二区 | 国产亚洲精品久久久久久大师 | 无码人妻少妇伦在线电影 | 色婷婷综合中文久久一本 | 日本一卡2卡3卡四卡精品网站 | 又紧又大又爽精品一区二区 | 亚洲无人区一区二区三区 | 全球成人中文在线 | 欧美熟妇另类久久久久久多毛 | 亚洲精品一区二区三区在线观看 | 日韩精品a片一区二区三区妖精 | 中文字幕日韩精品一区二区三区 | 国产成人午夜福利在线播放 | 欧美日本日韩 | 国产激情无码一区二区app | 国产av无码专区亚洲awww | 国产精品多人p群无码 | 人人妻在人人 | 4hu四虎永久在线观看 | 性欧美疯狂xxxxbbbb | 日韩精品无码免费一区二区三区 | 色一情一乱一伦一视频免费看 | 国产一区二区三区四区五区加勒比 | 国产高清av在线播放 | 九一九色国产 | 熟女体下毛毛黑森林 | 亚洲欧洲中文日韩av乱码 | 久久www免费人成人片 | 秋霞特色aa大片 | 波多野结衣aⅴ在线 | 97色伦图片97综合影院 | 国产美女极度色诱视频www | 成人免费视频视频在线观看 免费 | 亚洲综合无码一区二区三区 | 日韩少妇白浆无码系列 | 无码成人精品区在线观看 | 久久亚洲国产成人精品性色 | 日日噜噜噜噜夜夜爽亚洲精品 | 日韩av无码中文无码电影 | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 国内少妇偷人精品视频免费 | 少妇被粗大的猛进出69影院 | 中文字幕亚洲情99在线 | 亚洲aⅴ无码成人网站国产app | 亚洲色成人中文字幕网站 | 亚洲国产av精品一区二区蜜芽 | 国产欧美熟妇另类久久久 | 18禁黄网站男男禁片免费观看 | 色综合久久久无码中文字幕 | 午夜福利试看120秒体验区 | 国产精品免费大片 | 久久婷婷五月综合色国产香蕉 | 亚洲成a人一区二区三区 | 高清国产亚洲精品自在久久 | 丁香花在线影院观看在线播放 | 日本一卡2卡3卡四卡精品网站 | 人妻少妇精品无码专区二区 | 中国女人内谢69xxxx | 亚洲国产高清在线观看视频 | 久久精品国产大片免费观看 | 日韩人妻无码一区二区三区久久99 | 中文亚洲成a人片在线观看 | 国产在线无码精品电影网 | 亚洲 高清 成人 动漫 | 免费人成在线观看网站 | 亚洲理论电影在线观看 | 熟妇人妻无乱码中文字幕 | 99在线 | 亚洲 | 性生交大片免费看女人按摩摩 | 荫蒂被男人添的好舒服爽免费视频 | 无码av中文字幕免费放 | 又湿又紧又大又爽a视频国产 | 亚洲一区二区三区国产精华液 | 日日麻批免费40分钟无码 | 狠狠亚洲超碰狼人久久 | 日本大香伊一区二区三区 | a片免费视频在线观看 | 国产美女精品一区二区三区 | 无套内谢的新婚少妇国语播放 | 麻豆av传媒蜜桃天美传媒 | 亚洲日韩av片在线观看 | 久久亚洲日韩精品一区二区三区 | 国产高清不卡无码视频 | 丝袜足控一区二区三区 | 亚洲热妇无码av在线播放 | 亚洲精品成人福利网站 | 精品无码一区二区三区爱欲 | 丰满少妇人妻久久久久久 | 一二三四社区在线中文视频 | 久久国产精品二国产精品 | 国产精品内射视频免费 | 给我免费的视频在线观看 | 久久久久成人片免费观看蜜芽 | 丝袜足控一区二区三区 | 国产成人一区二区三区别 | 无码一区二区三区在线观看 | 精品成在人线av无码免费看 | 国产真实夫妇视频 | 九月婷婷人人澡人人添人人爽 | 免费国产黄网站在线观看 | 欧美性生交活xxxxxdddd | 国产亚洲精品久久久久久久久动漫 | 久久国产精品偷任你爽任你 | 国产一区二区三区日韩精品 | 国产精品第一区揄拍无码 | 国产av一区二区三区最新精品 | 国产亚洲人成a在线v网站 | 国产精品毛片一区二区 | 日本爽爽爽爽爽爽在线观看免 | 国产偷自视频区视频 | 精品厕所偷拍各类美女tp嘘嘘 | 久久久久se色偷偷亚洲精品av | www国产亚洲精品久久网站 | 亚洲人成网站免费播放 | 精品国产精品久久一区免费式 | 精品无码一区二区三区的天堂 | 中文字幕无码免费久久9一区9 | 麻豆国产人妻欲求不满谁演的 | 久久97精品久久久久久久不卡 | 人人妻人人澡人人爽人人精品浪潮 | 少妇性l交大片欧洲热妇乱xxx | 扒开双腿吃奶呻吟做受视频 | 日本精品少妇一区二区三区 | 扒开双腿吃奶呻吟做受视频 | 99精品视频在线观看免费 | 亚洲日韩一区二区 | 一本大道伊人av久久综合 | 国产精品久久久久久亚洲影视内衣 | 久久人人爽人人爽人人片ⅴ | 午夜理论片yy44880影院 | 日韩欧美群交p片內射中文 | 老熟女乱子伦 | 国内精品人妻无码久久久影院蜜桃 | 国产精品美女久久久久av爽李琼 | aa片在线观看视频在线播放 | 青青草原综合久久大伊人精品 | 中文字幕av无码一区二区三区电影 | 大屁股大乳丰满人妻 | √天堂中文官网8在线 | 色诱久久久久综合网ywww | 久久久久国色av免费观看性色 | 无码午夜成人1000部免费视频 | 亚洲の无码国产の无码影院 | 捆绑白丝粉色jk震动捧喷白浆 | 国模大胆一区二区三区 | 成人精品天堂一区二区三区 | 国产精品久久久久久亚洲毛片 | 玩弄少妇高潮ⅹxxxyw | 夜夜高潮次次欢爽av女 | 台湾无码一区二区 | 成在人线av无码免费 | 亚洲一区二区观看播放 | 国产超碰人人爽人人做人人添 | 国产三级精品三级男人的天堂 | 国内丰满熟女出轨videos | 国产精品久久久久7777 | 久久久www成人免费毛片 | 久久精品人人做人人综合 | 六月丁香婷婷色狠狠久久 | 香港三级日本三级妇三级 | 精品久久久无码中文字幕 | 欧美日本免费一区二区三区 | 日韩在线不卡免费视频一区 | 美女黄网站人色视频免费国产 | 日本www一道久久久免费榴莲 | 久久成人a毛片免费观看网站 | 性生交大片免费看l | 中文字幕av伊人av无码av | 少妇厨房愉情理9仑片视频 | 精品无码成人片一区二区98 | 欧美人与禽猛交狂配 | 国模大胆一区二区三区 | 丰满少妇女裸体bbw | 老太婆性杂交欧美肥老太 | 人妻互换免费中文字幕 | 亚洲人成网站色7799 | 亚洲午夜无码久久 | 久久久久免费精品国产 | 国产香蕉尹人视频在线 | 天天躁日日躁狠狠躁免费麻豆 | 欧美一区二区三区视频在线观看 | 3d动漫精品啪啪一区二区中 | 国产精品自产拍在线观看 | 午夜精品一区二区三区在线观看 | 精品偷拍一区二区三区在线看 | 动漫av一区二区在线观看 | 少妇高潮喷潮久久久影院 | 欧美变态另类xxxx | 麻豆国产人妻欲求不满 | 特大黑人娇小亚洲女 | 国产麻豆精品精东影业av网站 | 亚洲国产午夜精品理论片 | 亚洲无人区午夜福利码高清完整版 | 久久久www成人免费毛片 | 色情久久久av熟女人妻网站 | 88国产精品欧美一区二区三区 | 在线a亚洲视频播放在线观看 | a片免费视频在线观看 | 免费视频欧美无人区码 | 国产精品亚洲综合色区韩国 | 在线观看免费人成视频 | 中文字幕av日韩精品一区二区 | 久久精品人人做人人综合试看 | 久久精品国产日本波多野结衣 | 久久午夜无码鲁丝片秋霞 | 少妇高潮喷潮久久久影院 | 成年美女黄网站色大免费全看 | 国产av剧情md精品麻豆 | 日本在线高清不卡免费播放 | av无码久久久久不卡免费网站 | 奇米影视7777久久精品人人爽 | 300部国产真实乱 | 亚洲熟妇色xxxxx欧美老妇y | 中文毛片无遮挡高清免费 | 久久综合给久久狠狠97色 | 国产精品无码成人午夜电影 | 性欧美疯狂xxxxbbbb | 国产成人久久精品流白浆 | 男女性色大片免费网站 | 久久精品人人做人人综合 | 色噜噜亚洲男人的天堂 | 女人被男人爽到呻吟的视频 | 色偷偷av老熟女 久久精品人妻少妇一区二区三区 | 色婷婷香蕉在线一区二区 | 欧美日韩亚洲国产精品 | 国产精品免费大片 | 国产97人人超碰caoprom | 青青草原综合久久大伊人精品 | 亚洲 激情 小说 另类 欧美 | 久久亚洲精品成人无码 | 国产尤物精品视频 | 国产亲子乱弄免费视频 | 欧美肥老太牲交大战 | 久久国产精品精品国产色婷婷 | 老太婆性杂交欧美肥老太 | 国产免费久久精品国产传媒 | 国产免费久久久久久无码 | 免费乱码人妻系列无码专区 | 97夜夜澡人人双人人人喊 | 超碰97人人射妻 | 黄网在线观看免费网站 | 少妇无套内谢久久久久 | 老子影院午夜精品无码 | 色爱情人网站 | 亚洲日韩乱码中文无码蜜桃臀网站 | 一本大道伊人av久久综合 | 国产明星裸体无码xxxx视频 | 永久免费观看美女裸体的网站 | 色婷婷欧美在线播放内射 | 国产精品99爱免费视频 | 在线a亚洲视频播放在线观看 | 久久久久国色av免费观看性色 | 欧美丰满少妇xxxx性 | 国内精品人妻无码久久久影院 | 免费无码一区二区三区蜜桃大 | 少妇久久久久久人妻无码 | 51国偷自产一区二区三区 | 久久综合激激的五月天 | 中文无码成人免费视频在线观看 | 亚洲中文字幕无码一久久区 | 欧美肥老太牲交大战 | 熟妇人妻激情偷爽文 | 日日橹狠狠爱欧美视频 | 牲欲强的熟妇农村老妇女视频 | 亚洲精品国偷拍自产在线麻豆 | 欧美人与禽猛交狂配 | 十八禁视频网站在线观看 | 精品久久久中文字幕人妻 | 色综合久久久久综合一本到桃花网 | 中文字幕av伊人av无码av | 亚洲国产成人a精品不卡在线 | 丰满少妇弄高潮了www | 中文字幕无码免费久久99 | 九九综合va免费看 | 亚洲精品综合五月久久小说 | 久久精品丝袜高跟鞋 | 欧美性猛交xxxx富婆 | 色噜噜亚洲男人的天堂 | 久久精品国产亚洲精品 | 欧美熟妇另类久久久久久不卡 | 男女性色大片免费网站 | 日韩精品一区二区av在线 | 老熟妇乱子伦牲交视频 | 麻豆蜜桃av蜜臀av色欲av | 99久久婷婷国产综合精品青草免费 | 日本熟妇人妻xxxxx人hd | 中文字幕乱妇无码av在线 | 亚洲成a人一区二区三区 | 麻豆国产人妻欲求不满 | 日韩av无码一区二区三区 | 中文字幕日产无线码一区 | 中文字幕无码日韩欧毛 | 亚洲va欧美va天堂v国产综合 | 久久久久久久人妻无码中文字幕爆 | 欧美三级a做爰在线观看 | 人妻aⅴ无码一区二区三区 | 久久综合香蕉国产蜜臀av | 98国产精品综合一区二区三区 | 老熟妇仑乱视频一区二区 | 亚洲 激情 小说 另类 欧美 | 日韩精品无码一本二本三本色 | 麻豆国产丝袜白领秘书在线观看 | 欧美丰满老熟妇xxxxx性 | 国产无套粉嫩白浆在线 | 欧美zoozzooz性欧美 | 人人妻人人澡人人爽人人精品 | 激情五月综合色婷婷一区二区 | 麻豆国产人妻欲求不满谁演的 | 2020最新国产自产精品 | 无套内谢的新婚少妇国语播放 | 激情国产av做激情国产爱 | 西西人体www44rt大胆高清 | 人人妻人人澡人人爽欧美精品 | 伊在人天堂亚洲香蕉精品区 | 老司机亚洲精品影院 | www国产亚洲精品久久网站 | 免费男性肉肉影院 | 久久精品无码一区二区三区 | 成人性做爰aaa片免费看 | 国产人妻大战黑人第1集 | 国内精品久久毛片一区二区 | 大地资源网第二页免费观看 | 国产精品无码一区二区桃花视频 | 国产两女互慰高潮视频在线观看 | 老熟女重囗味hdxx69 | 正在播放老肥熟妇露脸 | 亚洲国产av精品一区二区蜜芽 | 国产香蕉97碰碰久久人人 | 99久久99久久免费精品蜜桃 | 少妇被黑人到高潮喷出白浆 | 最新版天堂资源中文官网 | 性开放的女人aaa片 | 国产香蕉尹人视频在线 | 精品少妇爆乳无码av无码专区 | 欧美高清在线精品一区 | 免费无码av一区二区 | 欧美国产日韩亚洲中文 | 国产精品va在线播放 | 无码av免费一区二区三区试看 | 熟妇人妻激情偷爽文 | 国产人妻人伦精品 | 久久国产精品二国产精品 | 在线a亚洲视频播放在线观看 | 无码国产激情在线观看 | 无码精品国产va在线观看dvd | 成人一在线视频日韩国产 | 东京热一精品无码av | 强开小婷嫩苞又嫩又紧视频 | 九九在线中文字幕无码 | 色窝窝无码一区二区三区色欲 | 亚洲成色www久久网站 | 亚洲国产欧美在线成人 | 日韩亚洲欧美精品综合 | 亚洲综合无码久久精品综合 | 波多野结衣乳巨码无在线观看 | 国产无av码在线观看 | 久久久成人毛片无码 | 亚洲啪av永久无码精品放毛片 | 骚片av蜜桃精品一区 | 久久久成人毛片无码 | 亚洲天堂2017无码 | 在线观看欧美一区二区三区 | 狂野欧美性猛xxxx乱大交 | 强辱丰满人妻hd中文字幕 | 欧美日韩人成综合在线播放 | 国产97在线 | 亚洲 | 少妇性荡欲午夜性开放视频剧场 | 国产成人精品三级麻豆 | 亚洲日韩乱码中文无码蜜桃臀网站 | 99久久婷婷国产综合精品青草免费 | 性色欲网站人妻丰满中文久久不卡 | 夫妻免费无码v看片 | 国内老熟妇对白xxxxhd | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 天堂а√在线地址中文在线 | 亚洲精品一区二区三区大桥未久 | 熟妇人妻无乱码中文字幕 | 国产精品美女久久久网av | 国产成人久久精品流白浆 | 婷婷综合久久中文字幕蜜桃三电影 | 欧美刺激性大交 | 一本色道久久综合狠狠躁 | 中文字幕 亚洲精品 第1页 | 无码国产色欲xxxxx视频 | 国内揄拍国内精品人妻 | 国产激情无码一区二区 | 国产人妻精品一区二区三区不卡 | 国产一精品一av一免费 | 精品国产麻豆免费人成网站 | 亚洲日韩av一区二区三区中文 | 伊人久久大香线蕉亚洲 | 日日碰狠狠躁久久躁蜜桃 | 人人妻人人澡人人爽欧美精品 | 波多野结衣 黑人 | 夜精品a片一区二区三区无码白浆 | 天堂无码人妻精品一区二区三区 | 欧美性猛交内射兽交老熟妇 | 亚洲精品一区三区三区在线观看 | 日欧一片内射va在线影院 | 成年女人永久免费看片 | 人妻互换免费中文字幕 | 亚洲国产一区二区三区在线观看 | 99er热精品视频 | 搡女人真爽免费视频大全 | 一本久久a久久精品亚洲 | 牲欲强的熟妇农村老妇女 | 国产口爆吞精在线视频 | 日本精品少妇一区二区三区 | 亚洲aⅴ无码成人网站国产app | 55夜色66夜色国产精品视频 | 中文字幕人妻无码一区二区三区 | 夜夜躁日日躁狠狠久久av | 成在人线av无码免观看麻豆 | 欧美老熟妇乱xxxxx | 俺去俺来也在线www色官网 | 亚洲自偷自偷在线制服 | 国产精品无码成人午夜电影 | www成人国产高清内射 | 久久国产精品萌白酱免费 | 久久天天躁狠狠躁夜夜免费观看 | 精品 日韩 国产 欧美 视频 | 国产精品无套呻吟在线 | 无码国模国产在线观看 | 国产精品久久久久影院嫩草 | 漂亮人妻洗澡被公强 日日躁 | 在线精品国产一区二区三区 | 天堂а√在线中文在线 | 亚洲精品成人福利网站 | 国产艳妇av在线观看果冻传媒 | 久热国产vs视频在线观看 | 人妻无码αv中文字幕久久琪琪布 | 纯爱无遮挡h肉动漫在线播放 | 国产精品无码一区二区桃花视频 | 成人精品视频一区二区 | 好爽又高潮了毛片免费下载 | 亚洲精品一区二区三区在线观看 | 无码av免费一区二区三区试看 | 女人被男人躁得好爽免费视频 | 精品无码国产自产拍在线观看蜜 | 在线精品国产一区二区三区 | 欧美激情内射喷水高潮 | 曰韩无码二三区中文字幕 | 在线观看免费人成视频 | 色综合视频一区二区三区 | 欧美日本精品一区二区三区 | 亚洲精品久久久久中文第一幕 | 免费无码一区二区三区蜜桃大 | 国产精品自产拍在线观看 | 精品国产福利一区二区 | 国内精品人妻无码久久久影院 | 丰满少妇熟乱xxxxx视频 | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | 午夜嘿嘿嘿影院 | 久久 国产 尿 小便 嘘嘘 | 国产9 9在线 | 中文 | 亚欧洲精品在线视频免费观看 | 动漫av网站免费观看 | 亚洲无人区一区二区三区 | 欧美成人免费全部网站 | 成人免费视频视频在线观看 免费 | 九月婷婷人人澡人人添人人爽 | 国产无套内射久久久国产 | 台湾无码一区二区 | 国产亚洲视频中文字幕97精品 | 精品国产一区av天美传媒 | 日韩成人一区二区三区在线观看 | 少妇性俱乐部纵欲狂欢电影 | 麻花豆传媒剧国产免费mv在线 | 97精品国产97久久久久久免费 | 99riav国产精品视频 | 亚洲国产av精品一区二区蜜芽 | 亚洲人成影院在线无码按摩店 | 欧洲欧美人成视频在线 | 色窝窝无码一区二区三区色欲 | 熟妇人妻无乱码中文字幕 | 亚洲s色大片在线观看 | 久久精品成人欧美大片 | 国内精品久久毛片一区二区 | 人妻互换免费中文字幕 | 国产无遮挡又黄又爽免费视频 | 丰满人妻被黑人猛烈进入 | 啦啦啦www在线观看免费视频 | 亚洲国产精品久久久久久 | 男女猛烈xx00免费视频试看 | 老子影院午夜伦不卡 | 精品久久8x国产免费观看 | 夫妻免费无码v看片 | 老头边吃奶边弄进去呻吟 | 国产超碰人人爽人人做人人添 | 免费观看又污又黄的网站 | 久久精品国产精品国产精品污 | 国产成人无码av一区二区 | 九九热爱视频精品 | 女人和拘做爰正片视频 | 最近中文2019字幕第二页 | 亚洲欧美日韩综合久久久 | 久久综合网欧美色妞网 | 国产一区二区三区四区五区加勒比 | 亚洲人亚洲人成电影网站色 | 女人被男人躁得好爽免费视频 | 免费观看激色视频网站 | 男女超爽视频免费播放 | 免费国产成人高清在线观看网站 | 精品厕所偷拍各类美女tp嘘嘘 | 久久久久久久人妻无码中文字幕爆 | 色噜噜亚洲男人的天堂 | 亚洲午夜久久久影院 | 国产亚洲日韩欧美另类第八页 | 久久久精品欧美一区二区免费 | 天天躁夜夜躁狠狠是什么心态 | 欧美阿v高清资源不卡在线播放 | 中文精品久久久久人妻不卡 | 国产香蕉97碰碰久久人人 | 高中生自慰www网站 | 性欧美videos高清精品 | 夜夜夜高潮夜夜爽夜夜爰爰 | 亚洲gv猛男gv无码男同 | 又大又紧又粉嫩18p少妇 | 国产亚洲欧美在线专区 | 国产精品多人p群无码 | 国产两女互慰高潮视频在线观看 | 亚洲の无码国产の无码步美 | 日韩精品无码免费一区二区三区 | 中文字幕 亚洲精品 第1页 | 日日躁夜夜躁狠狠躁 | 麻豆av传媒蜜桃天美传媒 | 精品厕所偷拍各类美女tp嘘嘘 | 中文精品无码中文字幕无码专区 | 桃花色综合影院 | 亚洲日韩乱码中文无码蜜桃臀网站 | 国产成人精品一区二区在线小狼 | 300部国产真实乱 | 久久成人a毛片免费观看网站 | 欧美国产日韩久久mv | 亚洲精品一区二区三区在线 | 午夜福利不卡在线视频 | 成 人 网 站国产免费观看 | 黑人粗大猛烈进出高潮视频 | 一本一道久久综合久久 | 动漫av网站免费观看 | 精品亚洲成av人在线观看 | 欧美 亚洲 国产 另类 | 高潮毛片无遮挡高清免费视频 | 亚洲精品久久久久中文第一幕 | 亚洲精品午夜国产va久久成人 | 青草视频在线播放 | 国产深夜福利视频在线 | 亚洲综合久久一区二区 | 老司机亚洲精品影院 | 日韩精品久久久肉伦网站 | 免费观看激色视频网站 | 亚洲国产精品久久久天堂 | 女人高潮内射99精品 | 蜜桃视频韩日免费播放 | 黄网在线观看免费网站 | 内射白嫩少妇超碰 | 亚洲最大成人网站 | www国产亚洲精品久久久日本 | 中文字幕无码热在线视频 | 成人精品视频一区二区 | 人人爽人人澡人人高潮 | 国产99久久精品一区二区 | 一个人免费观看的www视频 | 熟妇女人妻丰满少妇中文字幕 | 午夜精品一区二区三区的区别 | 色欲av亚洲一区无码少妇 | 久久综合狠狠综合久久综合88 | 国产色视频一区二区三区 | 亚洲の无码国产の无码影院 | 男女作爱免费网站 | 377p欧洲日本亚洲大胆 | 麻豆人妻少妇精品无码专区 | 中文精品无码中文字幕无码专区 | 久久久精品人妻久久影视 | 强奷人妻日本中文字幕 | 国产精品多人p群无码 | 无码国内精品人妻少妇 | 日韩人妻无码一区二区三区久久99 | 久久久婷婷五月亚洲97号色 | 亚洲精品国产精品乱码视色 | 日本乱偷人妻中文字幕 | 激情人妻另类人妻伦 | 老熟女重囗味hdxx69 | 俄罗斯老熟妇色xxxx | 国产乱人偷精品人妻a片 | 午夜免费福利小电影 | 久久 国产 尿 小便 嘘嘘 | 丰满少妇人妻久久久久久 | 亚洲精品国产第一综合99久久 | 曰本女人与公拘交酡免费视频 | 野外少妇愉情中文字幕 | 国语精品一区二区三区 | 国产精品人人爽人人做我的可爱 | 国产午夜精品一区二区三区嫩草 | 亚洲精品中文字幕乱码 | 一本久久伊人热热精品中文字幕 | 1000部啪啪未满十八勿入下载 | 国产极品美女高潮无套在线观看 | 国产av一区二区精品久久凹凸 | 永久免费精品精品永久-夜色 | 国内精品人妻无码久久久影院 | 成 人 网 站国产免费观看 | 亚洲综合精品香蕉久久网 | 色综合久久中文娱乐网 | 国产精品亚洲一区二区三区喷水 | 99er热精品视频 | 国产亚洲精品久久久久久大师 | 日日摸天天摸爽爽狠狠97 | 一本大道伊人av久久综合 | 国产免费观看黄av片 | 亚洲国产精华液网站w | 2019午夜福利不卡片在线 | 国产极品美女高潮无套在线观看 | 国产小呦泬泬99精品 | 少妇人妻大乳在线视频 | 日韩 欧美 动漫 国产 制服 | 国产偷抇久久精品a片69 | 国产精品手机免费 | 国产亚洲欧美日韩亚洲中文色 | 国产精品欧美成人 | 亚洲综合在线一区二区三区 | 狠狠色丁香久久婷婷综合五月 | 久久 国产 尿 小便 嘘嘘 | 欧美自拍另类欧美综合图片区 | 真人与拘做受免费视频一 | 国产内射爽爽大片视频社区在线 | 牲欲强的熟妇农村老妇女 | 强奷人妻日本中文字幕 | 亚洲综合无码久久精品综合 | 人妻人人添人妻人人爱 | 亚洲欧洲日本综合aⅴ在线 | 激情人妻另类人妻伦 | 乱码午夜-极国产极内射 | 国产 精品 自在自线 | 东京一本一道一二三区 | 国产精品亚洲а∨无码播放麻豆 | 熟妇人妻激情偷爽文 | 亚洲人成影院在线无码按摩店 | 亚洲精品一区二区三区四区五区 | 亚洲国产成人av在线观看 | 在线观看国产一区二区三区 | 欧美三级a做爰在线观看 | 水蜜桃亚洲一二三四在线 | 18精品久久久无码午夜福利 | 久久精品国产99久久6动漫 | 亚洲熟妇自偷自拍另类 | 麻豆精产国品 | 扒开双腿吃奶呻吟做受视频 | 熟妇激情内射com | 国产香蕉97碰碰久久人人 | 久久亚洲精品中文字幕无男同 | 久久国内精品自在自线 | 性生交片免费无码看人 | 九九热爱视频精品 | 男女作爱免费网站 | 日本护士毛茸茸高潮 | 免费观看激色视频网站 | 丰满少妇熟乱xxxxx视频 | 又大又硬又黄的免费视频 | 国产精品欧美成人 | 人妻与老人中文字幕 | 国产在线精品一区二区高清不卡 | 亚洲国产精品美女久久久久 | 亚洲一区二区三区国产精华液 | 特黄特色大片免费播放器图片 | 中国女人内谢69xxxx | 人人妻人人澡人人爽欧美一区 | 成人精品视频一区二区 | 永久免费观看美女裸体的网站 | 国产成人无码专区 | 久久综合九色综合97网 | 国产莉萝无码av在线播放 | 人妻少妇精品无码专区二区 | 国产色在线 | 国产 | 亚洲人成人无码网www国产 | а√资源新版在线天堂 | 99riav国产精品视频 | 亚洲人成影院在线无码按摩店 | 国产免费无码一区二区视频 | 麻豆国产人妻欲求不满谁演的 | 丝袜足控一区二区三区 | 少妇厨房愉情理9仑片视频 | 亚洲欧美国产精品专区久久 | 欧美成人家庭影院 | 久久国产精品二国产精品 | 亚洲区小说区激情区图片区 | 欧美日韩在线亚洲综合国产人 | 超碰97人人射妻 | 人人妻人人澡人人爽欧美一区 | 日本欧美一区二区三区乱码 | 学生妹亚洲一区二区 | 亚洲日韩av一区二区三区中文 | 国产在线aaa片一区二区99 | 在线播放免费人成毛片乱码 | 成人试看120秒体验区 | 国产一区二区三区四区五区加勒比 | 日韩视频 中文字幕 视频一区 | 国产特级毛片aaaaaaa高清 | 久久成人a毛片免费观看网站 | 国产亚洲精品久久久久久国模美 | 岛国片人妻三上悠亚 | 好屌草这里只有精品 | 亚洲国产成人av在线观看 | 久久亚洲精品中文字幕无男同 | 日韩精品无码一区二区中文字幕 | 清纯唯美经典一区二区 | 国产精品嫩草久久久久 | 中文字幕日韩精品一区二区三区 | 国产无遮挡吃胸膜奶免费看 | 露脸叫床粗话东北少妇 | 亚洲色在线无码国产精品不卡 | 亚洲成av人片在线观看无码不卡 | 成人无码精品一区二区三区 | 无码人妻av免费一区二区三区 | 在线精品国产一区二区三区 | 少妇的肉体aa片免费 | 露脸叫床粗话东北少妇 | 99视频精品全部免费免费观看 | 国产两女互慰高潮视频在线观看 | 色综合视频一区二区三区 | 亚洲国产精品毛片av不卡在线 | av在线亚洲欧洲日产一区二区 | 一本无码人妻在中文字幕免费 | 中文字幕无线码 | 亚洲自偷自偷在线制服 | 亚洲国产欧美国产综合一区 | 日本熟妇乱子伦xxxx | 亚洲精品一区二区三区四区五区 | 波多野结衣高清一区二区三区 | 欧美丰满少妇xxxx性 | 国产精品香蕉在线观看 | 中文精品无码中文字幕无码专区 | 男女作爱免费网站 | 久久精品99久久香蕉国产色戒 | 又大又硬又爽免费视频 | 亚洲欧美色中文字幕在线 | 少妇愉情理伦片bd | 亚洲成av人片在线观看无码不卡 | 久久久精品人妻久久影视 | 免费视频欧美无人区码 | 国产97色在线 | 免 | 亚洲男人av香蕉爽爽爽爽 | 亚洲国产精品久久人人爱 | 久久亚洲中文字幕无码 | 男女性色大片免费网站 | 欧美 亚洲 国产 另类 | 久久亚洲精品中文字幕无男同 | 久久综合给合久久狠狠狠97色 | 中文字幕精品av一区二区五区 | 久久zyz资源站无码中文动漫 | 麻豆精产国品 | 欧美亚洲国产一区二区三区 | 久久伊人色av天堂九九小黄鸭 | 国产精品香蕉在线观看 | 黑人粗大猛烈进出高潮视频 | 99国产精品白浆在线观看免费 | 亚洲色大成网站www | 国产三级精品三级男人的天堂 | 男女下面进入的视频免费午夜 | 免费看少妇作爱视频 | 无码av免费一区二区三区试看 | 精品国产一区二区三区四区在线看 | 欧美日本免费一区二区三区 | 欧美日韩综合一区二区三区 | 午夜男女很黄的视频 | 国产综合在线观看 | 国产特级毛片aaaaaaa高清 | 国产精品对白交换视频 | 国产精品人人妻人人爽 | 成 人 免费观看网站 | 又湿又紧又大又爽a视频国产 | 国产精品亚洲а∨无码播放麻豆 | 无码一区二区三区在线 | 亚洲欧洲中文日韩av乱码 | 一个人看的视频www在线 | 人妻少妇精品视频专区 | 久久国语露脸国产精品电影 | 装睡被陌生人摸出水好爽 | 天天燥日日燥 | 国产亚洲精品久久久ai换 | 欧美日韩人成综合在线播放 | 午夜理论片yy44880影院 | 中文字幕无线码 | 欧美性生交xxxxx久久久 | 亚洲а∨天堂久久精品2021 | 国产精品无码一区二区桃花视频 | 日本xxxx色视频在线观看免费 | 国产精品久久久久9999小说 | 日韩成人一区二区三区在线观看 | 色婷婷av一区二区三区之红樱桃 | 国产乱人伦偷精品视频 | 麻豆国产97在线 | 欧洲 | 国产一精品一av一免费 | 人人超人人超碰超国产 | 中文字幕亚洲情99在线 | 18无码粉嫩小泬无套在线观看 | 久久人人爽人人爽人人片ⅴ | 日本乱人伦片中文三区 | 粉嫩少妇内射浓精videos | 亚洲欧美国产精品专区久久 | 亚无码乱人伦一区二区 | 波多野结衣av一区二区全免费观看 | 国产美女极度色诱视频www | 国产三级久久久精品麻豆三级 | 欧美变态另类xxxx | 狠狠cao日日穞夜夜穞av | 99久久99久久免费精品蜜桃 | 99精品国产综合久久久久五月天 | 色一情一乱一伦一区二区三欧美 | 18禁黄网站男男禁片免费观看 | 亚洲狠狠婷婷综合久久 | 精品久久久中文字幕人妻 | 久久亚洲精品中文字幕无男同 | 色欲av亚洲一区无码少妇 | 日韩av激情在线观看 | 一区二区三区乱码在线 | 欧洲 | 永久免费精品精品永久-夜色 | 国产精品爱久久久久久久 | 麻豆md0077饥渴少妇 | 成人一区二区免费视频 | 日日摸天天摸爽爽狠狠97 | 成 人 免费观看网站 | 伊在人天堂亚洲香蕉精品区 | 精品国产麻豆免费人成网站 | 欧美老妇交乱视频在线观看 | 伊在人天堂亚洲香蕉精品区 | 国产精品.xx视频.xxtv | 久久亚洲日韩精品一区二区三区 | 欧美喷潮久久久xxxxx | 中文字幕亚洲情99在线 | 国产激情精品一区二区三区 | 亚洲国产欧美在线成人 | 搡女人真爽免费视频大全 | 激情内射日本一区二区三区 | 久久精品人人做人人综合试看 | 国产激情无码一区二区app | 无码成人精品区在线观看 | 草草网站影院白丝内射 | 极品尤物被啪到呻吟喷水 | 国产亚洲精品久久久久久国模美 | 亚洲成av人影院在线观看 | 色 综合 欧美 亚洲 国产 | 装睡被陌生人摸出水好爽 | 欧美日韩一区二区三区自拍 | 国产精品欧美成人 | 少妇高潮一区二区三区99 | 国内精品久久毛片一区二区 | 在线精品亚洲一区二区 | 精品无码国产一区二区三区av | 精品水蜜桃久久久久久久 | 久久久精品456亚洲影院 | 国产精品久久久久久久影院 | 国产在线一区二区三区四区五区 | 天天摸天天透天天添 | 在线亚洲高清揄拍自拍一品区 | 一个人看的视频www在线 | 俄罗斯老熟妇色xxxx | 国产一区二区三区日韩精品 | 亚洲成a人片在线观看无码3d | 在线а√天堂中文官网 | 国产97色在线 | 免 | 欧美老妇交乱视频在线观看 | 日本爽爽爽爽爽爽在线观看免 | 亚洲一区二区三区 | 无码人妻丰满熟妇区五十路百度 | 中文字幕无码免费久久99 | 亚洲精品午夜无码电影网 | 午夜理论片yy44880影院 | 精品一区二区三区无码免费视频 | 高潮喷水的毛片 | 2020久久香蕉国产线看观看 | 美女极度色诱视频国产 | 六十路熟妇乱子伦 | 亚洲色欲色欲欲www在线 | 国产成人精品视频ⅴa片软件竹菊 | 成人精品一区二区三区中文字幕 | 国产免费久久久久久无码 | 国产两女互慰高潮视频在线观看 | 国产成人久久精品流白浆 | 精品一区二区不卡无码av | 国产手机在线αⅴ片无码观看 | 亚洲一区二区三区含羞草 | 亚洲a无码综合a国产av中文 | 99久久人妻精品免费二区 | 色 综合 欧美 亚洲 国产 | 狠狠色噜噜狠狠狠7777奇米 | 中文字幕无码av波多野吉衣 | 精品国产一区二区三区av 性色 | 婷婷丁香六月激情综合啪 | 日本va欧美va欧美va精品 | 国产特级毛片aaaaaa高潮流水 | 波多野结衣高清一区二区三区 | 国产午夜福利100集发布 | 日本免费一区二区三区最新 | 亚洲国产精品无码一区二区三区 | 伊人久久大香线焦av综合影院 | 久久久久se色偷偷亚洲精品av | 成在人线av无码免费 | 亚洲 a v无 码免 费 成 人 a v | 久久97精品久久久久久久不卡 | aⅴ在线视频男人的天堂 | 性啪啪chinese东北女人 | 久久人妻内射无码一区三区 | 51国偷自产一区二区三区 | 欧美丰满熟妇xxxx | 亚洲日本va中文字幕 | 亚洲狠狠色丁香婷婷综合 | 国产内射老熟女aaaa | 日本xxxx色视频在线观看免费 | 性欧美videos高清精品 | 中文字幕av日韩精品一区二区 | 精品久久久久久亚洲精品 | 欧美丰满少妇xxxx性 | 性欧美疯狂xxxxbbbb | 国产精品内射视频免费 | 在线 国产 欧美 亚洲 天堂 | av在线亚洲欧洲日产一区二区 | 性欧美熟妇videofreesex | 在线观看国产午夜福利片 | 天堂久久天堂av色综合 | 成人免费视频视频在线观看 免费 | 无码纯肉视频在线观看 | 成人免费视频视频在线观看 免费 | 精品国产一区av天美传媒 | 天堂а√在线地址中文在线 | 亚洲综合色区中文字幕 | 福利一区二区三区视频在线观看 | 少妇性l交大片欧洲热妇乱xxx | 国产精品办公室沙发 | 亚洲精品国偷拍自产在线麻豆 | 国产成人无码专区 | 亚洲综合无码久久精品综合 | 四虎国产精品一区二区 | 亚洲熟妇自偷自拍另类 | 国产精品美女久久久网av | 性做久久久久久久免费看 | 狠狠色丁香久久婷婷综合五月 | 欧美老妇交乱视频在线观看 | 日韩欧美中文字幕公布 | 国产成人无码区免费内射一片色欲 | 丰腴饱满的极品熟妇 | 国产精品多人p群无码 | 十八禁视频网站在线观看 | 中文毛片无遮挡高清免费 | 婷婷六月久久综合丁香 | 精品无码一区二区三区爱欲 | 亚洲熟妇色xxxxx欧美老妇 | 国产成人精品优优av | 熟妇人妻中文av无码 | 永久免费精品精品永久-夜色 | 在线欧美精品一区二区三区 | 亚洲熟妇色xxxxx欧美老妇 | 任你躁在线精品免费 | 国产av无码专区亚洲awww | 亚洲 高清 成人 动漫 | 亚洲国产精品美女久久久久 | 99视频精品全部免费免费观看 | www成人国产高清内射 | 少妇性l交大片欧洲热妇乱xxx | 少妇太爽了在线观看 | 久久久国产精品无码免费专区 | 国产两女互慰高潮视频在线观看 | 亚洲大尺度无码无码专区 | 一本大道久久东京热无码av | 亚洲精品一区二区三区大桥未久 | 男人扒开女人内裤强吻桶进去 | 国产成人综合在线女婷五月99播放 | 国产亚洲欧美在线专区 | 成人性做爰aaa片免费看不忠 | 香港三级日本三级妇三级 | 国产人成高清在线视频99最全资源 | 天天爽夜夜爽夜夜爽 | 夜夜躁日日躁狠狠久久av | 中文字幕无线码 | 伦伦影院午夜理论片 | 99久久无码一区人妻 | 亚洲一区二区三区香蕉 | 国产内射老熟女aaaa | 无码av最新清无码专区吞精 | 一本久道久久综合婷婷五月 | 婷婷丁香五月天综合东京热 | 夜夜夜高潮夜夜爽夜夜爰爰 | 国产成人无码一二三区视频 | 强开小婷嫩苞又嫩又紧视频 | 久久综合给合久久狠狠狠97色 | 丰满少妇熟乱xxxxx视频 | 精品久久久中文字幕人妻 | 大乳丰满人妻中文字幕日本 | 久久久久久久久蜜桃 | 性生交大片免费看l | 久久精品女人天堂av免费观看 | 午夜无码人妻av大片色欲 | 国产手机在线αⅴ片无码观看 | 精品久久综合1区2区3区激情 | 日韩人妻无码中文字幕视频 | 国产人妻久久精品二区三区老狼 | 欧美精品国产综合久久 | 国产人妻精品一区二区三区不卡 | 国语精品一区二区三区 | 国产莉萝无码av在线播放 | 国产乱人偷精品人妻a片 | 亚洲乱码日产精品bd | 日本又色又爽又黄的a片18禁 | 丝袜美腿亚洲一区二区 | 九九热爱视频精品 | 亚洲熟熟妇xxxx | 狂野欧美性猛xxxx乱大交 | 亚洲色欲色欲天天天www | 人人妻人人藻人人爽欧美一区 | 国产一区二区三区精品视频 | 久久精品丝袜高跟鞋 | 亚洲中文字幕乱码av波多ji | 亚洲成a人一区二区三区 | 亚洲日韩一区二区 | 宝宝好涨水快流出来免费视频 | 国产激情综合五月久久 | 欧美日韩色另类综合 | 我要看www免费看插插视频 | 麻豆国产人妻欲求不满 | 影音先锋中文字幕无码 | 久久亚洲精品中文字幕无男同 | 午夜理论片yy44880影院 | 亚洲成a人一区二区三区 | 精品无码成人片一区二区98 | 国产精品无码永久免费888 | 国产精品对白交换视频 | 三级4级全黄60分钟 | 伊人色综合久久天天小片 | 亚洲 另类 在线 欧美 制服 | 又色又爽又黄的美女裸体网站 | v一区无码内射国产 | 国产精品亚洲五月天高清 | 东京热一精品无码av | 麻豆国产人妻欲求不满谁演的 | 精品国产一区二区三区四区在线看 | 成人试看120秒体验区 | 毛片内射-百度 | 国产午夜无码视频在线观看 | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 兔费看少妇性l交大片免费 | 小泽玛莉亚一区二区视频在线 | av无码不卡在线观看免费 | 国产性生交xxxxx无码 | 国内少妇偷人精品视频 | 亚洲成色www久久网站 | 真人与拘做受免费视频一 | 国产成人综合色在线观看网站 | 国产午夜无码精品免费看 | 狠狠噜狠狠狠狠丁香五月 | 国产精品久免费的黄网站 | 人妻天天爽夜夜爽一区二区 | 亚洲人成人无码网www国产 | 性生交大片免费看女人按摩摩 | 一本色道久久综合狠狠躁 | 噜噜噜亚洲色成人网站 | 久久精品人妻少妇一区二区三区 | 无码播放一区二区三区 | 亚洲综合伊人久久大杳蕉 | 国产sm调教视频在线观看 | 曰韩少妇内射免费播放 | 99久久久国产精品无码免费 | 曰本女人与公拘交酡免费视频 | 熟妇人妻无乱码中文字幕 | 女人高潮内射99精品 | 亚洲精品一区二区三区大桥未久 | 自拍偷自拍亚洲精品被多人伦好爽 | 成人毛片一区二区 | 亚洲中文字幕在线观看 | 精品水蜜桃久久久久久久 | 少妇的肉体aa片免费 | 国产成人无码一二三区视频 | 亚洲色欲色欲天天天www | 免费无码av一区二区 | a片在线免费观看 | 2019nv天堂香蕉在线观看 | 国产高潮视频在线观看 | 婷婷六月久久综合丁香 | 自拍偷自拍亚洲精品被多人伦好爽 |