日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

linux kernel的中斷子系統(tǒng)之(四):High level irq event handler

 老匹夫 2015-04-08

linux kernel的中斷子系統(tǒng)之(四):High level irq event handler

作者:linuxer 發(fā)布于:2014-8-28 20:00 分類(lèi):中斷子系統(tǒng)

一、前言

當(dāng)外設(shè)觸發(fā)一次中斷后,一個(gè)大概的處理過(guò)程是:

1、具體CPU architecture相關(guān)的模塊會(huì)進(jìn)行現(xiàn)場(chǎng)保護(hù),然后調(diào)用machine driver對(duì)應(yīng)的中斷處理handler

2、machine driver對(duì)應(yīng)的中斷處理handler中會(huì)根據(jù)硬件的信息獲取HW interrupt ID,并且通過(guò)irq domain模塊翻譯成IRQ number

3、調(diào)用該IRQ number對(duì)應(yīng)的high level irq event handler,在這個(gè)high level的handler中,會(huì)通過(guò)和interupt controller交互,進(jìn)行中斷處理的flow control(處理中斷的嵌套、搶占等),當(dāng)然最終會(huì)遍歷該中斷描述符的IRQ action list,調(diào)用外設(shè)的specific handler來(lái)處理該中斷

4、具體CPU architecture相關(guān)的模塊會(huì)進(jìn)行現(xiàn)場(chǎng)恢復(fù)。

上面的1、4這兩個(gè)步驟在linux kernel的中斷子系統(tǒng)之(六):ARM中斷處理過(guò)程中已經(jīng)有了較為細(xì)致的描述,步驟2在linux kernel的中斷子系統(tǒng)之(二):irq domain介紹中介紹,本文主要描述步驟3,也就是linux中斷子系統(tǒng)的high level irq event handler。

 

注:這份文檔充滿(mǎn)了猜測(cè)和空想,很多地方描述可能是有問(wèn)題的,不過(guò)我還是把它發(fā)出來(lái),拋磚引玉,希望可以引發(fā)大家討論。

 

一、如何進(jìn)入high level irq event handler

1、從具體CPU architecture的中斷處理到machine相關(guān)的處理模塊

說(shuō)到具體的CPU,我們還是用ARM為例好了。對(duì)于ARM,我們?cè)?a href="http://www./linux_kenrel/irq_handler.html">ARM中斷處理文檔中已經(jīng)有了較為細(xì)致的描述。這里我們看看如何從從具體CPU的中斷處理到machine相關(guān)的處理模塊 ,其具體代碼如下:

    .macro    irq_handler
#ifdef CONFIG_MULTI_IRQ_HANDLER
    ldr    r1, =handle_arch_irq
    mov    r0, sp
    adr    lr, BSYM(9997f)
    ldr    pc, [r1]
#else
    arch_irq_handler_default
#endif
9997:
    .endm

其實(shí),直接從CPU的中斷處理跳轉(zhuǎn)到通用中斷處理模塊是不可能的,中斷處理不可能越過(guò)interrupt controller這個(gè)層次。一般而言,通用中斷處理模塊會(huì)提供一些通用的中斷代碼處理庫(kù),然后由interrupt controller這個(gè)層次的代碼調(diào)用這些通用中斷處理的完成整個(gè)的中斷處理過(guò)程。“interrupt controller這個(gè)層次的代碼”是和硬件中斷系統(tǒng)設(shè)計(jì)相關(guān)的,例如:系統(tǒng)中有多少個(gè)interrupt contrller,每個(gè)interrupt controller是如何控制的?它們是如何級(jí)聯(lián)的?我們稱(chēng)這些相關(guān)的驅(qū)動(dòng)模塊為machine interrupt driver。

在上面的代碼中,如果配置了MULTI_IRQ_HANDLER的話(huà),ARM中斷處理則直接跳轉(zhuǎn)到一個(gè)叫做handle_arch_irq函數(shù),如果系統(tǒng)中只有一個(gè)類(lèi)型的interrupt controller(可能是多個(gè)interrupt controller,例如使用兩個(gè)級(jí)聯(lián)的GIC),那么handle_arch_irq可以在interrupt controller初始化的時(shí)候設(shè)定。代碼如下:

……

if (gic_nr == 0) {
        set_handle_irq(gic_handle_irq);
}

……

gic_nr是GIC的編號(hào),linux kernel初始化過(guò)程中,每發(fā)現(xiàn)一個(gè)GIC,都是會(huì)指向GIC driver的初始化函數(shù)的,不過(guò)對(duì)于第一個(gè)GIC,gic_nr等于0,對(duì)于第二個(gè)GIC,gic_nr等于1。當(dāng)然handle_arch_irq這個(gè)函數(shù)指針不是per CPU的變量,是全部CPU共享的,因此,初始化一次就OK了。

當(dāng)使用多種類(lèi)型的interrupt controller的時(shí)候(例如HW 系統(tǒng)使用了S3C2451這樣的SOC,這時(shí)候,系統(tǒng)有兩種interrupt controller,一種是GPIO type,另外一種是SOC上的interrupt controller),則不適合在interrupt controller中進(jìn)行設(shè)定,這時(shí)候,可以考慮在machine driver中設(shè)定。在這種情況下,handle_arch_irq 這個(gè)函數(shù)是在setup_arch函數(shù)中根據(jù)machine driver設(shè)定,具體如下:

handle_arch_irq = mdesc->handle_irq;

關(guān)于MULTI_IRQ_HANDLER這個(gè)配置項(xiàng),我們可以再多說(shuō)幾句。當(dāng)然,其實(shí)這個(gè)配置項(xiàng)的名字已經(jīng)出賣(mài)它了。multi irq handler就是說(shuō)系統(tǒng)中有多個(gè)irq handler,可以在run time的時(shí)候指定。為何要run time的時(shí)候,從多個(gè)handler中選擇一個(gè)呢?HW interrupt block難道不是固定的嗎?我的理解(猜想)是:一個(gè)kernel的image支持多個(gè)HW platform,對(duì)于不同的HW platform,在運(yùn)行時(shí)檢查HW platform的類(lèi)型,設(shè)定不同的irq handler。

2、interrupt controller相關(guān)的代碼

我們還是以2個(gè)級(jí)聯(lián)的GIC為例來(lái)描述interrupt controller相關(guān)的代碼。代碼如下:

static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
    u32 irqstat, irqnr;
    struct gic_chip_data *gic = &gic_data[0];-----獲取root GIC的硬件描述符
    void __iomem *cpu_base = gic_data_cpu_base(gic); 獲取root GIC mapping到CPU地址空間的信息

    do {
        irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);---獲取HW interrupt ID
        irqnr = irqstat & ~0x1c00;

        if (likely(irqnr > 15 && irqnr < 1021)) {----SPI和PPI的處理
            irqnr = irq_find_mapping(gic->domain, irqnr);---將HW interrupt ID轉(zhuǎn)成IRQ number
            handle_IRQ(irqnr, regs);----處理該IRQ number
            continue;
        }
        if (irqnr < 16) {-----IPI類(lèi)型的中斷處理
            writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
#ifdef CONFIG_SMP
            handle_IPI(irqnr, regs);
#endif
            continue;
        }
        break;
    } while (1);
}

更多關(guān)于GIC相關(guān)的信息,請(qǐng)參考linux kernel的中斷子系統(tǒng)之(七):GIC代碼分析。對(duì)于ARM處理器,handle_IRQ代碼如下:

void handle_IRQ(unsigned int irq, struct pt_regs *regs)
{

……
        generic_handle_irq(irq);

……
}

3、調(diào)用high level handler

調(diào)用high level handler的代碼邏輯非常簡(jiǎn)單,如下:

int generic_handle_irq(unsigned int irq)
{
    struct irq_desc *desc = irq_to_desc(irq); ---通過(guò)IRQ number獲取該irq的描述符

    if (!desc)
        return -EINVAL;
    generic_handle_irq_desc(irq, desc);----調(diào)用high level的irq handler來(lái)處理該IRQ
    return 0;
}

static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
{
    desc->handle_irq(irq, desc);
}

 

二、理解high level irq event handler需要的知識(shí)準(zhǔn)備

1、自動(dòng)探測(cè)IRQ

一個(gè)硬件驅(qū)動(dòng)可以通過(guò)下面的方法進(jìn)行自動(dòng)探測(cè)它使用的IRQ:

unsigned long irqs;
int irq;

irqs = probe_irq_on();--------啟動(dòng)IRQ自動(dòng)探測(cè)
驅(qū)動(dòng)那個(gè)打算自動(dòng)探測(cè)IRQ的硬件產(chǎn)生中斷
irq = probe_irq_off(irqs);-------結(jié)束IRQ自動(dòng)探測(cè)

如果能夠自動(dòng)探測(cè)到IRQ,上面程序中的irq(probe_irq_off的返回值)就是自動(dòng)探測(cè)的結(jié)果。后續(xù)程序可以通過(guò)request_threaded_irq申請(qǐng)?jiān)揑RQ。probe_irq_on函數(shù)主要的目的是返回一個(gè)32 bit的掩碼,通過(guò)該掩碼可以知道可能使用的IRQ number有哪些,具體代碼如下:

unsigned long probe_irq_on(void)
{

……
    for_each_irq_desc_reverse(i, desc) { ----scan 從nr_irqs-1 到0 的中斷描述符
        raw_spin_lock_irq(&desc->lock);
        if (!desc->action && irq_settings_can_probe(desc)) {--------(1)
            desc->istate |= IRQS_AUTODETECT | IRQS_WAITING;-----(2)
            if (irq_startup(desc, false))
                desc->istate |= IRQS_PENDING;
        }
        raw_spin_unlock_irq(&desc->lock);
    } 
    msleep(100); --------------------------(3)


    for_each_irq_desc(i, desc) {
        raw_spin_lock_irq(&desc->lock);

        if (desc->istate & IRQS_AUTODETECT) {------------(4)
            if (!(desc->istate & IRQS_WAITING)) {
                desc->istate &= ~IRQS_AUTODETECT;
                irq_shutdown(desc);
            } else
                if (i < 32)------------------------(5)
                    mask |= 1 << i;
        }
        raw_spin_unlock_irq(&desc->lock);
    }

    return mask;
}

(1)那些能自動(dòng)探測(cè)IRQ的中斷描述符需要具體兩個(gè)條件:

a、該中斷描述符還沒(méi)有通過(guò)request_threaded_irq或者其他方式申請(qǐng)?jiān)揑RQ的specific handler(也就是irqaction數(shù)據(jù)結(jié)構(gòu))

b、該中斷描述符允許自動(dòng)探測(cè)(不能設(shè)定IRQ_NOPROBE)

(2)如果滿(mǎn)足上面的條件,那么該中斷描述符屬于備選描述符。設(shè)定其internal state為IRQS_AUTODETECT | IRQS_WAITING。IRQS_AUTODETECT表示本IRQ正處于自動(dòng)探測(cè)中。

(3)在等待過(guò)程中,系統(tǒng)仍然允許,各種中斷依然會(huì)觸發(fā)。在各種high level irq event handler中,總會(huì)有如下的代碼:

desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);

這里會(huì)清除IRQS_WAITING狀態(tài)。

(4)這時(shí)候,我們還沒(méi)有控制那個(gè)想要自動(dòng)探測(cè)IRQ的硬件產(chǎn)生中斷,因此處于自動(dòng)探測(cè)中,并且IRQS_WAITING并清除的一定不是我們期待的IRQ(可能是spurious interrupts導(dǎo)致的),這時(shí)候,clear IRQS_AUTODETECT,shutdown該IRQ。

(5)最大探測(cè)的IRQ是31(mask是一個(gè)32 bit的value),mask返回的是可能的irq掩碼。

我們?cè)賮?lái)看看probe_irq_off的代碼:

int probe_irq_off(unsigned long val)
{
    int i, irq_found = 0, nr_of_irqs = 0;
    struct irq_desc *desc;

    for_each_irq_desc(i, desc) {
        raw_spin_lock_irq(&desc->lock);

        if (desc->istate & IRQS_AUTODETECT) {----只有處于IRQ自動(dòng)探測(cè)中的描述符才會(huì)被處理
            if (!(desc->istate & IRQS_WAITING)) {----找到一個(gè)潛在的中斷描述符
                if (!nr_of_irqs)
                    irq_found = i;
                nr_of_irqs++;
            }
            desc->istate &= ~IRQS_AUTODETECT; ----IRQS_WAITING沒(méi)有被清除,說(shuō)明該描述符
            irq_shutdown(desc);                                     不是自動(dòng)探測(cè)的那個(gè),shutdown之
        }
        raw_spin_unlock_irq(&desc->lock);
    }
    mutex_unlock(&probing_active);

    if (nr_of_irqs > 1) ------如果找到多于1個(gè)的IRQ,說(shuō)明探測(cè)失敗,返回負(fù)的IRQ個(gè)數(shù)信息
        irq_found = -irq_found;

    return irq_found;
}

因?yàn)樵谡{(diào)用probe_irq_off已經(jīng)觸發(fā)了自動(dòng)探測(cè)IRQ的那個(gè)硬件中斷,因此在該中斷的high level handler的執(zhí)行過(guò)程中,該硬件對(duì)應(yīng)的中斷描述符的IRQS_WAITING標(biāo)致應(yīng)該已經(jīng)被清除,因此probe_irq_off函數(shù)scan中斷描述符DB,找到處于auto probe中,而且IRQS_WAITING標(biāo)致被清除的那個(gè)IRQ。如果找到一個(gè),那么探測(cè)OK,返回該IRQ number,如果找到多個(gè),說(shuō)明探測(cè)失敗,返回負(fù)的IRQ個(gè)數(shù)信息,沒(méi)有找到的話(huà),返回0。


2、resend一個(gè)中斷

一個(gè)ARM SOC總是有很多的GPIO,有些GPIO可以提供中斷功能,這些GPIO的中斷可以配置成level trigger或者edge trigger。一般而言,大家都更喜歡用level trigger的中斷。有的SOC只能是有限個(gè)數(shù)的GPIO可以配置成電平中斷,因此,在項(xiàng)目初期進(jìn)行pin define的時(shí)候,大家都在爭(zhēng)搶電平觸發(fā)的GPIO。

電平觸發(fā)的中斷有什么好處呢?電平觸發(fā)的中斷很簡(jiǎn)單、直接,只要硬件檢測(cè)到硬件事件(例如有數(shù)據(jù)到來(lái)),其assert指定的電平信號(hào),CPU ack該中斷后,電平信號(hào)消失。但是對(duì)于邊緣觸發(fā)的中斷,它是用一個(gè)上升沿或者下降沿告知硬件的狀態(tài),這個(gè)狀態(tài)不是一個(gè)持續(xù)的狀態(tài),如果軟件處理不好,容易丟失中斷。

什么時(shí)候會(huì)resend一個(gè)中斷呢?我們考慮一個(gè)簡(jiǎn)單的例子:

(1)CPU A上正在處理x外設(shè)的中斷

(2)x外設(shè)的中斷再次到來(lái)(CPU A已經(jīng)ack該IRQ,因此x外設(shè)的中斷可以再次觸發(fā)),這時(shí)候其他CPU會(huì)處理它(mask and ack),并設(shè)置該中斷描述符是pending狀態(tài),并委托CPU A處理該pending狀態(tài)的中斷。需要注意的是CPU已經(jīng)ack了該中斷,因此該中斷的硬件狀態(tài)已經(jīng)不是pending狀態(tài),無(wú)法觸發(fā)中斷了,這里的pending狀態(tài)是指中斷描述符的軟件狀態(tài)。

(3)CPU B上由于同步的需求,disable了x外設(shè)的IRQ,這時(shí)候,CPU A沒(méi)有處理pending狀態(tài)的x外設(shè)中斷就離開(kāi)了中斷處理過(guò)程。

(4)當(dāng)enable x外設(shè)的IRQ的時(shí)候,需要檢測(cè)pending狀態(tài)以便resend該中斷,否則,該中斷會(huì)丟失的

具體代碼如下:

void check_irq_resend(struct irq_desc *desc, unsigned int irq)
{

    if (irq_settings_is_level(desc)) {-------電平中斷不存在resend的問(wèn)題
        desc->istate &= ~IRQS_PENDING;
        return;
    }
    if (desc->istate & IRQS_REPLAY)----如果已經(jīng)設(shè)定resend的flag,退出就OK了,這個(gè)應(yīng)該
        return;                                                和irq的enable disable能多層嵌套相關(guān)
    if (desc->istate & IRQS_PENDING) {-------如果有pending的flag則進(jìn)行處理
        desc->istate &= ~IRQS_PENDING;
        desc->istate |= IRQS_REPLAY; ------設(shè)置retrigger標(biāo)志

        if (!desc->irq_data.chip->irq_retrigger ||
            !desc->irq_data.chip->irq_retrigger(&desc->irq_data)) {----調(diào)用底層irq chip的callback
#ifdef CONFIG_HARDIRQS_SW_RESEND
也可以使用軟件手段來(lái)完成resend一個(gè)中斷,具體代碼省略,有興趣大家可以自己看看
#endif
        }
    }
}

在各種high level irq event handler中,總會(huì)有如下的代碼:

desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);

這里會(huì)清除IRQS_REPLAY狀態(tài),表示該中斷已經(jīng)被retrigger,一次resend interrupt的過(guò)程結(jié)束。

 

3、unhandled interrupt和spurious interrupt

在中斷處理的最后,總會(huì)有一段代碼如下:

irqreturn_t
handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action)
{

……

    if (!noirqdebug)
        note_interrupt(irq, desc, retval);
    return retval;
}

note_interrupt就是進(jìn)行unhandled interrupt和spurious interrupt處理的。對(duì)于這類(lèi)中斷,linux kernel有一套復(fù)雜的機(jī)制來(lái)處理,你可以通過(guò)command line參數(shù)(noirqdebug)來(lái)控制開(kāi)關(guān)該功能。

當(dāng)發(fā)生了一個(gè)中斷,但是沒(méi)有被處理(有兩種可能,一種是根本沒(méi)有注冊(cè)的specific handler,第二種是有handler,但是handler否認(rèn)是自己對(duì)應(yīng)的設(shè)備觸發(fā)的中斷),怎么辦?毫無(wú)疑問(wèn)這是一個(gè)異常狀況,那么kernel是否要立刻采取措施將該IRQ disable呢?也不太合適,畢竟interrupt request信號(hào)線(xiàn)是允許共享的,直接disable該IRQ有可能會(huì)下手太狠,kernel采取了這樣的策略:如果該IRQ觸發(fā)了100,000次,但是99,900次沒(méi)有處理,在這種條件下,我們就是disable這個(gè)interrupt request line。多么有情有義的策略?。∠嚓P(guān)的控制數(shù)據(jù)在中斷描述符中,如下:

struct irq_desc {
……
    unsigned int        irq_count;--------記錄發(fā)生的中斷的次數(shù),每100,000則回滾
    unsigned long        last_unhandled;-----上一次沒(méi)有處理的IRQ的時(shí)間點(diǎn)
    unsigned int        irqs_unhandled;------沒(méi)有處理的次數(shù)
……
}

irq_count和irqs_unhandled都是比較直觀的,為何要記錄unhandled interrupt發(fā)生的時(shí)間呢?我們來(lái)看具體的代碼。具體的相關(guān)代碼位于note_interrupt中,如下:

void note_interrupt(unsigned int irq, struct irq_desc *desc,  irqreturn_t action_ret)
{
    if (desc->istate & IRQS_POLL_INPROGRESS ||  irq_settings_is_polled(desc))
        return;


    if (action_ret == IRQ_WAKE_THREAD)----h(huán)andler返回IRQ_WAKE_THREAD是正常情況
        return;

    if (bad_action_ret(action_ret)) {-----報(bào)告錯(cuò)誤,這些是由于specific handler的返回錯(cuò)誤導(dǎo)致的
        report_bad_irq(irq, desc, action_ret);
        return;
    }

    if (unlikely(action_ret == IRQ_NONE)) {-------是unhandled interrupt
        if (time_after(jiffies, desc->last_unhandled + HZ/10))---(1)
            desc->irqs_unhandled = 1;---重新開(kāi)始計(jì)數(shù)
        else
            desc->irqs_unhandled++;---判定為unhandled interrupt,計(jì)數(shù)加一
        desc->last_unhandled = jiffies;-------保存本次unhandled interrupt對(duì)應(yīng)的jiffies時(shí)間
    }

if (unlikely(try_misrouted_irq(irq, desc, action_ret))) {---是否啟動(dòng)Misrouted IRQ fixup
    int ok = misrouted_irq(irq);
    if (action_ret == IRQ_NONE)
        desc->irqs_unhandled -= ok;
}

    desc->irq_count++;
    if (likely(desc->irq_count < 100000))-----------(2)
        return;

    desc->irq_count = 0;
    if (unlikely(desc->irqs_unhandled > 99900)) {--------(3)

        __report_bad_irq(irq, desc, action_ret);---報(bào)告錯(cuò)誤

        desc->istate |= IRQS_SPURIOUS_DISABLED;
        desc->depth++;
        irq_disable(desc);

        mod_timer(&poll_spurious_irq_timer,----------(4)
              jiffies + POLL_SPURIOUS_IRQ_INTERVAL);
    }
    desc->irqs_unhandled = 0;
}

(1)是否是一次有效的unhandled interrupt還要根據(jù)時(shí)間來(lái)判斷。一般而言,當(dāng)硬件處于異常狀態(tài)的時(shí)候往往是非常短的時(shí)間觸發(fā)非常多次的中斷,如果距離上次unhandled interrupt的時(shí)間超過(guò)了10秒(HZ=100),那么我們要把irqs_unhandled重新計(jì)數(shù)。如果不這么處理的話(huà),隨著時(shí)間的累計(jì),最終irqs_unhandled可能會(huì)達(dá)到99900次的,從而把這個(gè)IRQ錯(cuò)誤的推上了審判臺(tái)。

(2)irq_count每次都會(huì)加一,記錄IRQ被觸發(fā)的次數(shù)。但只要大于100000才啟動(dòng) step (3)中的檢查。一旦啟動(dòng)檢查,irq_count會(huì)清零,irqs_unhandled也會(huì)清零,進(jìn)入下一個(gè)檢查周期。

(3)如果滿(mǎn)足條件(IRQ觸發(fā)了100,000次,但是99,900次沒(méi)有處理),disable該IRQ。

(4)啟動(dòng)timer,輪詢(xún)整個(gè)系統(tǒng)中的handler來(lái)處理這個(gè)中斷(輪詢(xún)啊,絕對(duì)是真愛(ài)啊)。這個(gè)timer的callback函數(shù)定義如下:

static void poll_spurious_irqs(unsigned long dummy)
{
    struct irq_desc *desc;
    int i;

    if (atomic_inc_return(&irq_poll_active) != 1)----確保系統(tǒng)中只有一個(gè)excuting thread進(jìn)入臨界區(qū)
        goto out;
    irq_poll_cpu = smp_processor_id(); ----記錄當(dāng)前正在polling的CPU

    for_each_irq_desc(i, desc) {------遍歷所有的中斷描述符
        unsigned int state;

        if (!i)-------------越過(guò)0號(hào)中斷描述符。對(duì)于X86,這是timer的中斷
             continue;

        /* Racy but it doesn't matter */
        state = desc->istate;
        barrier();
        if (!(state & IRQS_SPURIOUS_DISABLED))----名花有主的那些就不必考慮了
            continue;

        local_irq_disable();
        try_one_irq(i, desc, true);---------OK,嘗試一下是不是可以處理
        local_irq_enable();
    }
out:
    atomic_dec(&irq_poll_active);
    mod_timer(&poll_spurious_irq_timer,--------一旦觸發(fā)了該timer,就停不下來(lái)
          jiffies + POLL_SPURIOUS_IRQ_INTERVAL);
}

 

三、和high level irq event handler相關(guān)的硬件描述

1、CPU layer和Interrupt controller之間的接口

從邏輯層面上看,CPU和interrupt controller之間的接口包括:

(1)觸發(fā)中斷的signal。一般而言,這個(gè)(些)信號(hào)是電平觸發(fā)的。對(duì)于ARM CPU,它是nIRQ和nFIQ信號(hào)線(xiàn),對(duì)于X86,它是INT和NMI信號(hào)線(xiàn),對(duì)于PowerPC,這些信號(hào)線(xiàn)包括MC(machine check)、CRIT(critical interrupt)和NON-CRIT(Non critical interrupt)。對(duì)于linux kernel的中斷子系統(tǒng),我們只使用其中一個(gè)信號(hào)線(xiàn)(例如對(duì)于ARM而言,我們只使用nIRQ這個(gè)信號(hào)線(xiàn))。這樣,從CPU層面看,其邏輯動(dòng)作非常的簡(jiǎn)單,不區(qū)分優(yōu)先級(jí),觸發(fā)中斷的那個(gè)信號(hào)線(xiàn)一旦assert,并且CPU沒(méi)有mask中斷,那么軟件就會(huì)轉(zhuǎn)到一個(gè)異常向量執(zhí)行,完畢后返回現(xiàn)場(chǎng)。

(2)Ack中斷的signal。這個(gè)signal可能是物理上的一個(gè)連接CPU和interrupt controller的銅線(xiàn),也可能不是。對(duì)于X86+8259這樣的結(jié)構(gòu),Ack中斷的signal就是nINTA信號(hào)線(xiàn),對(duì)于ARM+GIC而言,這個(gè)信號(hào)就是總線(xiàn)上的一次訪(fǎng)問(wèn)(讀Interrupt Acknowledge Register寄存器)。CPU ack中斷標(biāo)識(shí)cpu開(kāi)啟啟動(dòng)中斷服務(wù)程序(specific handler)去處理該中斷。對(duì)于X86而言,ack中斷可以讓8259將interrupt vector數(shù)據(jù)送到數(shù)據(jù)總線(xiàn)上,從而讓CPU獲取了足夠的處理該中斷的信息。對(duì)于ARM而言,ack中斷的同時(shí)也就是獲取了發(fā)生中斷的HW interrupt ID,總而言之,ack中斷后,CPU獲取了足夠開(kāi)啟執(zhí)行中斷處理的信息。

(3)結(jié)束中斷(EOI,end of interrupt)的signal。這個(gè)signal用來(lái)標(biāo)識(shí)CPU已經(jīng)完成了對(duì)該中斷的處理(specific handler或者ISR,interrupt serivce routine執(zhí)行完畢)。實(shí)際的物理形態(tài)這里就不描述了,和ack中斷signal是類(lèi)似的。

(4)控制總線(xiàn)和數(shù)據(jù)總線(xiàn)接口。通過(guò)這些接口,CPU可以訪(fǎng)問(wèn)(讀寫(xiě))interrupt controller的寄存器。

 

2、Interrupt controller和Peripheral device之間的接口

所有的系統(tǒng)中,Interrupt controller和Peripheral device之間的接口都是一個(gè)Interrupt Request信號(hào)線(xiàn)。外設(shè)通過(guò)這個(gè)信號(hào)線(xiàn)上的電平或者邊緣向CPU(實(shí)際上是通過(guò)interrupt controller)申請(qǐng)中斷服務(wù)。

 

四、幾種典型的high level irq event handler

本章主要介紹幾種典型的high level irq event handler,在進(jìn)行high level irq event handler的設(shè)定的時(shí)候需要注意,不是外設(shè)使用電平觸發(fā)就選用handle_level_irq,選用什么樣的high level irq event handler是和Interrupt controller的行為以及外設(shè)電平觸發(fā)方式?jīng)Q定的。介紹每個(gè)典型的handler之前,我會(huì)簡(jiǎn)單的描述該handler要求的硬件行為,如果該外設(shè)的中斷系統(tǒng)符合這個(gè)硬件行為,那么可以選擇該handler為該中斷的high level irq event handler。

1、邊緣觸發(fā)的handler。

使用handle_edge_irq這個(gè)handler的硬件中斷系統(tǒng)行為如下:

xyz

我們以上升沿為例描述邊緣中斷的處理過(guò)程(下降沿的觸發(fā)是類(lèi)似的)。當(dāng)interrupt controller檢測(cè)到了上升沿信號(hào),會(huì)將該上升沿狀態(tài)(pending)鎖存在寄存器中,并通過(guò)中斷的signal向CPU觸發(fā)中斷。需要注意:這時(shí)候,外設(shè)和interrupt controller之間的interrupt request信號(hào)線(xiàn)會(huì)保持高電平,這也就意味著interrupt controller不可能檢測(cè)到新的中斷信號(hào)(本身是高電平,無(wú)法形成上升沿)。這個(gè)高電平信號(hào)會(huì)一直保持到軟件ack該中斷(調(diào)用irq chip的irq_ack callback函數(shù))。ack之后,中斷控制器才有可能繼續(xù)探測(cè)上升沿,觸發(fā)下一次中斷。

ARM+GIC組成的系統(tǒng)不符合這個(gè)類(lèi)型。雖然GIC提供了IAR(Interrupt Acknowledge Register)寄存器來(lái)讓ARM來(lái)ack中斷,但是,在調(diào)用high level handler之前,中斷處理程序需要通過(guò)讀取IAR寄存器獲得HW interrpt ID并轉(zhuǎn)換成IRQ number,因此實(shí)際上,對(duì)于GIC的irq chip,它是無(wú)法提供本場(chǎng)景中的irq_ack函數(shù)的。很多GPIO type的interrupt controller符合上面的條件,它們會(huì)提供pending狀態(tài)寄存器,讀可以獲取pending狀態(tài),而向pending狀態(tài)寄存器寫(xiě)1可以ack該中斷,讓interrupt controller可以繼續(xù)觸發(fā)下一次中斷。

handle_edge_irq代碼如下:

void handle_edge_irq(unsigned int irq, struct irq_desc *desc)
{
    raw_spin_lock(&desc->lock); -----------------(0)

    desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);----參考上一章的描述

    if (unlikely(irqd_irq_disabled(&desc->irq_data) ||-----------(1)
             irqd_irq_inprogress(&desc->irq_data) || !desc->action)) {
        if (!irq_check_poll(desc)) {
            desc->istate |= IRQS_PENDING;
            mask_ack_irq(desc);
            goto out_unlock;
        }
    }
    kstat_incr_irqs_this_cpu(irq, desc); ---更新該IRQ統(tǒng)計(jì)信息


    desc->irq_data.chip->irq_ack(&desc->irq_data); ---------(2)

    do {
        if (unlikely(!desc->action)) { -----------------(3)
            mask_irq(desc);
            goto out_unlock;
        }


        if (unlikely(desc->istate & IRQS_PENDING)) { ---------(4)
            if (!irqd_irq_disabled(&desc->irq_data) &&
                irqd_irq_masked(&desc->irq_data))
                unmask_irq(desc);
        }

        handle_irq_event(desc); -------------------(5)

    } while ((desc->istate & IRQS_PENDING) &&
         !irqd_irq_disabled(&desc->irq_data)); -------------(6)

out_unlock:
    raw_spin_unlock(&desc->lock); -----------------(7)
}

(0) 這時(shí)候,中斷仍然是關(guān)閉的,因此不會(huì)有來(lái)自本CPU的并發(fā),使用raw spin lock就防止其他CPU上對(duì)該IRQ的中斷描述符的訪(fǎng)問(wèn)。針對(duì)該spin lock,我們直觀的感覺(jué)是raw_spin_lock和(7)中的raw_spin_unlock是成對(duì)的,實(shí)際上并不是,handle_irq_event中的代碼是這樣的:

irqreturn_t handle_irq_event(struct irq_desc *desc)
{

    raw_spin_unlock(&desc->lock); -------和上面的(0)對(duì)應(yīng)

    處理具體的action list

    raw_spin_lock(&desc->lock);--------和上面的(7)對(duì)應(yīng)

}

實(shí)際上,由于在handle_irq_event中處理action list的耗時(shí)還是比較長(zhǎng)的,因此處理具體的action list的時(shí)候并沒(méi)有持有中斷描述符的spin lock。在如果那樣的話(huà),其他CPU在對(duì)中斷描述符進(jìn)行操作的時(shí)候需要spin的時(shí)間會(huì)很長(zhǎng)的。

(1)判斷是否需要執(zhí)行下面的action list的處理。這里分成幾種情況:

a、該中斷事件已經(jīng)被其他的CPU處理了

b、該中斷被其他的CPU disable了

c、該中斷描述符沒(méi)有注冊(cè)specific handler。這個(gè)比較簡(jiǎn)單,如果沒(méi)有irqaction,根本沒(méi)有必要調(diào)用action list的處理

如果該中斷事件已經(jīng)被其他的CPU處理了,那么我們僅僅是設(shè)定pending狀態(tài)(為了委托正在處理的該中斷的那個(gè)CPU進(jìn)行處理),mask_ack_irq該中斷并退出就OK了,并不做具體的處理。另外正在處理該中斷的CPU會(huì)檢查pending狀態(tài),并進(jìn)行處理的。同樣的,如果該中斷被其他的CPU disable了,本就不應(yīng)該繼續(xù)執(zhí)行該中斷的specific handler,我們也是設(shè)定pending狀態(tài),mask and ack中斷就退出了。當(dāng)其他CPU的代碼離開(kāi)臨界區(qū),enable 該中斷的時(shí)候,軟件會(huì)檢測(cè)pending狀態(tài)并resend該中斷。

這里的irq_check_poll代碼如下:

static bool irq_check_poll(struct irq_desc *desc)
{
    if (!(desc->istate & IRQS_POLL_INPROGRESS))
        return false;
    return irq_wait_for_poll(desc);
}

IRQS_POLL_INPROGRESS標(biāo)識(shí)了該IRQ正在被polling(上一章有描述),如果沒(méi)有被輪詢(xún),那么返回false,進(jìn)行正常的設(shè)定pending標(biāo)記、mask and ack中斷。如果正在被輪詢(xún),那么需要等待poll結(jié)束。

(2)ack該中斷。對(duì)于中斷控制器,一旦被ack,表示該外設(shè)的中斷被enable,硬件上已經(jīng)準(zhǔn)備好觸發(fā)下一次中斷了。再次觸發(fā)的中斷會(huì)被調(diào)度到其他的CPU上?,F(xiàn)在,我們可以再次回到步驟(1)中,為什么這里用mask and ack而不是單純的ack呢?如果單純的ack則意味著后續(xù)中斷還是會(huì)觸發(fā),這時(shí)候怎么處理?在pending+in progress的情況下,我們要怎么處理?記錄pending的次數(shù),有意義嗎?由于中斷是完全異步的,也有可能pending的標(biāo)記可能在另外的CPU上已經(jīng)修改為replay的標(biāo)記,這時(shí)候怎么辦?當(dāng)事情變得復(fù)雜的時(shí)候,那一定是本來(lái)方向就錯(cuò)了,因此,mask and ack就是最好的策略,我已經(jīng)記錄了pending狀態(tài),不再考慮pending嵌套的情況。

(3)在調(diào)用specific handler處理具體的中斷的時(shí)候,由于不持有中斷描述符的spin lock,因此其他CPU上有可能會(huì)注銷(xiāo)其specific handler,因此do while循環(huán)之后,desc->action有可能是NULL,如果是這樣,那么mask irq,然后退出就OK了

(4)如果中斷描述符處于pending狀態(tài),那么一定是其他CPU上又觸發(fā)了該interrupt source的中斷,并設(shè)定了pending狀態(tài),“委托”本CPU進(jìn)行處理,這時(shí)候,需要把之前mask住的中斷進(jìn)行unmask的操作。一旦unmask了該interrupt source,后續(xù)的中斷可以繼續(xù)觸發(fā),由其他的CPU處理(仍然是設(shè)定中斷描述符的pending狀態(tài),委托當(dāng)前正在處理該中斷請(qǐng)求的那個(gè)CPU進(jìn)行處理)。

(5)處理該中斷請(qǐng)求事件

irqreturn_t handle_irq_event(struct irq_desc *desc)
{
    struct irqaction *action = desc->action;
    irqreturn_t ret;

    desc->istate &= ~IRQS_PENDING;----CPU已經(jīng)準(zhǔn)備處理該中斷了,因此,清除pending狀態(tài)
    irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);--設(shè)定INPROGRESS的flag
    raw_spin_unlock(&desc->lock);

    ret = handle_irq_event_percpu(desc, action); ---遍歷action list,調(diào)用specific handler

    raw_spin_lock(&desc->lock);
    irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);---處理完成,清除INPROGRESS標(biāo)記
    return ret;
}

(6)只要有pending標(biāo)記,就說(shuō)明該中斷還在pending狀態(tài),需要繼續(xù)處理。當(dāng)然,如果有其他的CPU disable了該interrupt source,那么本次中斷結(jié)束處理。

 

2、電平觸發(fā)的handler

使用handle_level_irq這個(gè)handler的硬件中斷系統(tǒng)行為如下:

level

我們以高電平觸發(fā)為例。當(dāng)interrupt controller檢測(cè)到了高電平信號(hào),并通過(guò)中斷的signal向CPU觸發(fā)中斷。這時(shí)候,對(duì)中斷控制器進(jìn)行ack并不能改變interrupt request signal上的電平狀態(tài),一直要等到執(zhí)行具體的中斷服務(wù)程序(specific handler),對(duì)外設(shè)進(jìn)行ack的時(shí)候,電平信號(hào)才會(huì)恢復(fù)成低電平。在對(duì)外設(shè)ack之前,中斷狀態(tài)一直是pending的,如果沒(méi)有mask中斷,那么中斷控制器就會(huì)assert CPU。

handle_level_irq的代碼如下:

void handle_level_irq(unsigned int irq, struct irq_desc *desc)
{
    raw_spin_lock(&desc->lock);
    mask_ack_irq(desc); ---------------------(1)

    if (unlikely(irqd_irq_inprogress(&desc->irq_data)))---------(2)
        if (!irq_check_poll(desc))
            goto out_unlock;

    desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);--和retrigger中斷以及自動(dòng)探測(cè)IRQ相關(guān)
    kstat_incr_irqs_this_cpu(irq, desc);


    if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {-----(3)
        desc->istate |= IRQS_PENDING;
        goto out_unlock;
    }

    handle_irq_event(desc);

    cond_unmask_irq(desc); --------------(4)

out_unlock:
    raw_spin_unlock(&desc->lock);
}

(1)考慮CPU<------>interrupt controller<------>device這樣的連接方式中,我們認(rèn)為high level handler主要是和interrupt controller交互,而specific handler(request_irq注冊(cè)的那個(gè))是和device進(jìn)行交互。Level類(lèi)型的中斷的特點(diǎn)就是只要外設(shè)interrupt request line的電平狀態(tài)是有效狀態(tài),對(duì)于interrupt controller,該外設(shè)的interrupt總是active的。由于外設(shè)檢測(cè)到了事件(比如數(shù)據(jù)到來(lái)了),因此assert了指定的電平信號(hào),這個(gè)電平信號(hào)會(huì)一直保持,直到軟件清除了外設(shè)的狀態(tài)寄存器。但是,high level irq event handler這個(gè)層面只能操作Interrupt controller,不能操作具體外設(shè)的寄存器(那應(yīng)該屬于具體外設(shè)的specific interrupt handler處理內(nèi)容,該handler會(huì)掛入中斷描述符中的IRQ action list)。直到在具體的中斷服務(wù)程序(specific handler中)操作具體外設(shè)的寄存器,才能讓這個(gè)asserted電平信號(hào)消息。

正是因?yàn)閘evel trigger的這個(gè)特點(diǎn),因此,在high level handler中首先mask并ack該IRQ。這一點(diǎn)和邊緣觸發(fā)的high level handler有顯著的不同,在handle_edge_irq中,我們僅僅是ack了中斷,并沒(méi)有mask,因?yàn)檫吘売|發(fā)的中斷稍縱即逝,一旦mask了該中斷,容易造成中斷丟失。而對(duì)于電平中斷,我們不得不mask住該中斷,如果不mask住,只要CPU ack中斷,中斷控制器將持續(xù)的assert CPU中斷(因?yàn)橛行щ娖綘顟B(tài)一直保持)。如果我們mask住該中斷,中斷控制器將不再轉(zhuǎn)發(fā)該interrupt source來(lái)的中斷,因此,所有的CPU都不會(huì)感知到該中斷,直到軟件unmask。這里的ack是針對(duì)interrupt controller的ack,本身ack就是為了clear interrupt controller對(duì)該IRQ的狀態(tài)寄存器,不過(guò)由于外部的電平仍然是有效信號(hào),其實(shí)未必能清除interrupt controller的中斷狀態(tài),不過(guò)這是和中斷控制器硬件實(shí)現(xiàn)相關(guān)的。

(2)對(duì)于電平觸發(fā)的high level handler,我們一開(kāi)始就mask并ack了中斷,因此后續(xù)specific handler因該是串行化執(zhí)行的,為何要判斷in progress標(biāo)記呢?不要忘記spurious interrupt,那里會(huì)直接調(diào)用handler來(lái)處理spurious interrupt。

(3)這里有兩個(gè)場(chǎng)景

a、沒(méi)有注冊(cè)specific handler。如果沒(méi)有注冊(cè)handler,那么保持mask并設(shè)定pending標(biāo)記(這個(gè)pending標(biāo)記有什么作用還沒(méi)有想明白)。

b、該中斷被其他的CPU disable了。如果該中斷被其他的CPU disable了,本就不應(yīng)該繼續(xù)執(zhí)行該中斷的specific handler,我們也是設(shè)定pending狀態(tài),mask and ack中斷就退出了。當(dāng)其他CPU的代碼離開(kāi)臨界區(qū),enable 該中斷的時(shí)候,軟件會(huì)檢測(cè)pending狀態(tài)并resend該中斷。

(4)為何是有條件的unmask該IRQ?正常的話(huà)當(dāng)然是umask就OK了,不過(guò)有些threaded interrupt(這個(gè)概念在下一份文檔中描述)要求是one shot的(首次中斷,specific handler中開(kāi)了一槍?zhuān)瑆akeup了irq handler thread,如果允許中斷嵌套,那么在specific handler會(huì)多次開(kāi)槍?zhuān)@也就不是one shot了,有些IRQ的handler thread要求是one shot,也就是不能嵌套specific handler)。

 

3、支持EOI的handler

TODO

原創(chuàng)文章,轉(zhuǎn)發(fā)請(qǐng)注明出處。蝸窩科技。http://www./linux_kenrel/High_level_irq_event_handler.html

標(biāo)簽: 中斷處理

評(píng)論:

heziq
2015-03-23 16:33
1.有沒(méi)有同時(shí)支持上升沿和下降沿的中斷控制器?當(dāng)然我覺(jué)得不可能有同時(shí)支持高電平和低電平出發(fā)的中斷控制器。 ---------- 這句話(huà)有問(wèn)題。是某條中斷線(xiàn)是否可以同時(shí)支持上升沿和下降沿, 我見(jiàn)過(guò)的中斷控制器控制某條中斷線(xiàn)的時(shí)候,只能四種方式觸發(fā)方式里面選一種。中斷控制器當(dāng)然支持4種觸發(fā)方式。
heziq
2015-03-23 10:19
@linuxer:
請(qǐng)教幾個(gè)問(wèn)題:
1.有沒(méi)有同時(shí)支持上升沿和下降沿的中斷控制器?當(dāng)然我覺(jué)得不可能有同時(shí)支持高電平和低電平出發(fā)的中斷控制器。
2.gpio type 中斷,如果是上升沿中斷,是不是要使用下拉電阻或者根本不使用上下拉?如果使用了上拉電阻會(huì)怎么樣?
3.gpio type 中斷,如果是高電平,我覺(jué)得肯定要使用下拉電阻,將電平鉗制在低電平。
3.gpio type 中斷,如果是下降沿,是不是要使用上拉電阻,或者不使用。如果使用了下拉電阻會(huì)怎么樣?
4.gpio type  中斷,如果是低電平,我覺(jué)得肯定要使用上拉電阻,將電平鉗制在高電平。
5.如果gpio中斷內(nèi)部使用了上拉電阻,如果外部在使用上拉電阻,會(huì)出現(xiàn)什么副作用嗎。我覺(jué)得有,兩個(gè)上拉電源之間會(huì)互相影響。
5.如果gpio中斷內(nèi)部使用了下拉電阻,如果外部在使用下拉電阻,會(huì)使pin腳的輸入電阻變低,有可能高電平信號(hào)拉不起來(lái)。
6.如果外設(shè)使用邊沿觸發(fā)。比如上升沿,那么它的高電平維持時(shí)間有要求嗎,由高電平變低電平,是外設(shè)自己拉低的?還是interrupt controler控制?

這都是我在工作中碰到的疑問(wèn)。有些設(shè)置不好,會(huì)造成中斷觸發(fā)失敗,有些會(huì)造成功耗過(guò)大。在這里想和大家交流一下。
RobinHsiang
2015-03-23 11:21
@heziq:同樣想了解上面的問(wèn)題,希望Linuxer能分享寶貴經(jīng)驗(yàn)..!!
linuxer
2015-03-23 12:44
@heziq:非常好的問(wèn)題,不過(guò)問(wèn)題有些多,呵呵,我晚上再回答你吧
linuxer
2015-03-23 23:15
@heziq:我單獨(dú)開(kāi)了一篇文章討論這些問(wèn)題,有興趣的讀者可以到http://www./bbs/pull-up-resistor.html去討論。
RobinHsiang
2015-03-26 17:29
@linuxer:一般所說(shuō)GPIO導(dǎo)致漏電,是什么導(dǎo)致的?是比如GPIO設(shè)置,或者類(lèi)似中斷類(lèi)型GPIO錯(cuò)誤的上下拉導(dǎo)致的嗎?
而漏電的方向是從CPU漏到device還是反過(guò)來(lái)漏?這個(gè)主要看電源域嗎?
一般怎么樣去查證項(xiàng)目中有沒(méi)有GPIO上面的漏電呢?
linuxer
2015-03-28 22:22
@RobinHsiang:多謝你的問(wèn)題,讓我仔細(xì)思考了一番(好的問(wèn)題總是有這樣的特性),因此延遲到今天才回復(fù)。
一般而言,漏電(leakage current)是一個(gè)芯片的DC參數(shù)之一,例如Iozl和Iozh這兩個(gè)參數(shù),分別表示引腳處于高阻(High-Impedance)狀態(tài)時(shí),外加高電平和低電平的電流值。由于是高阻狀態(tài),因此這時(shí)候的leakage current應(yīng)該是很小的(例如1uA)。當(dāng)然,我想你這里說(shuō)的不是這種漏電。
我們調(diào)試嵌入式設(shè)備總是從功能開(kāi)始,然后性能,特別是功耗,例如待機(jī)或者關(guān)機(jī)電流,我們都是希望能夠滿(mǎn)足功能的情況下越小越好。當(dāng)大刀闊斧的針對(duì)各個(gè)HW block的的檢查過(guò)后,最后往往會(huì)糾纏在GPIO狀態(tài)的調(diào)整上,正確的GPIO設(shè)定往往能節(jié)省幾個(gè)毫安的電流(例如如果一個(gè)GPIO的狀態(tài)的錯(cuò)誤設(shè)定可能導(dǎo)致0.1mA的電流,那么精細(xì)的調(diào)整10個(gè)GPIO可以節(jié)省1個(gè)mA)
具體電流是source(用你的描述就是從CPU漏到device)還是sink(從device漏到CPU)是和實(shí)際的電路連接、cpu pin的特性以及外設(shè)芯片引腳的特性相關(guān)。例如CPU的一個(gè)GPIO是tri state,假設(shè)在CPU處于suspend的時(shí)候,將該pin設(shè)定為high-impedance狀態(tài),如果對(duì)端連接的是外設(shè)芯片的enable引腳(低電平有效),為了穩(wěn)定的電路狀態(tài),可能需要連接一個(gè)上拉電阻,確保外設(shè)芯片處于disable狀態(tài),以便節(jié)省功耗。如果CPU處于suspend的時(shí)候,將該pin設(shè)定為低電平,那么上拉電阻上將有一個(gè)不小的電流消耗(假設(shè)上拉到3V,上拉電阻是10k,那么CPU在該GPIO上的sink current大約要0.3mA)。如果調(diào)整輸出成高電平,也不會(huì)有這個(gè)sink current。
具體的電路形形色色,這里無(wú)法每一個(gè)都描述了。
RobinHsiang
2015-04-01 20:44
@linuxer:多謝。大致上明白了,有些概念再消化消化~~~
Rocky
2015-03-18 17:23
a、沒(méi)有注冊(cè)specific handler。如果沒(méi)有注冊(cè)handler,那么保持mask并設(shè)定pending標(biāo)記(這個(gè)pending標(biāo)記有什么作用還沒(méi)有想明白)。

@linuxer
應(yīng)該沒(méi)有特別的作用,就是代碼簡(jiǎn)潔吧?!要不是為了mark一下IRQS_PENDING,表明此中斷曾經(jīng)來(lái)過(guò)??

if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {-----(3)
        if(desc->action)//啰嗦吧?!
            desc->istate |= IRQS_PENDING;
        goto out_unlock;
    }

ps:寫(xiě)的真的挺好的,看第二遍就理解很清楚了!
linuxer
2015-03-23 23:21
@Rocky:多謝!有機(jī)會(huì)的話(huà)可以考慮出中斷子系統(tǒng)version 2,這次寫(xiě)的很多文檔有些自己也沒(méi)有搞明白,因此很難讓讀者明白。有些自己明白,寫(xiě)出來(lái)又不是那么明白??傊?,這次的中斷系統(tǒng)的文檔就當(dāng)是愛(ài)因斯坦的第一個(gè)小板凳吧,雖然粗糙,但是有些板凳的形狀,需要后續(xù)不斷的打磨。
Rocky
2015-03-24 23:59
@linuxer:‘看第二遍就理解很清楚了?!?

哦,我沒(méi)有說(shuō)清楚。之前沒(méi)有接觸過(guò)gic,所以看第一遍的時(shí)候,我概要的看了關(guān)于中斷的所有文章,不懂也看完了一遍。

等回頭再看第二遍的時(shí)候,發(fā)現(xiàn)總體都看明白了。

謝謝wowotech的付出。
forion
2014-11-04 00:10
現(xiàn)在回頭再看這篇文章,我能體會(huì)到你想表達(dá)的啦。不得不說(shuō),沒(méi)有一些積累,比較難體會(huì)你要描述的這個(gè)high level irq event handler 處理的過(guò)程。還有您的苦心啊。想盡全力的要把一個(gè)抽象的過(guò)程描寫(xiě)清楚。
linuxer
2014-08-29 19:33
補(bǔ)充兩句,實(shí)際上這份文檔我是寫(xiě)不下去了,我準(zhǔn)備從具體的中斷控制器驅(qū)動(dòng)開(kāi)始,把各種中斷控制器的代碼理清楚的話(huà),在回頭review這份文檔,也許這份文檔也是注定要打上“廢棄”的標(biāo)簽
forion
2014-09-01 10:41
@linuxer:這份文檔,看起來(lái)也很累啊,哈哈。
linuxer
2014-09-01 12:18
@forion:我寫(xiě)的也很累啊,哈哈
這種抽象層的東西就是理解起來(lái)比較困難,因此實(shí)際的系統(tǒng)是千奇百怪的,因此,要要足夠的底層知識(shí)的累積才可以悟到上層邏輯的精妙。
forion
2014-09-01 15:32
@linuxer:辛苦了,前輩~~
linuxer
2014-09-01 18:21
@linuxer:因此實(shí)際的系統(tǒng)是千奇百怪的--->因?yàn)閷?shí)際的系統(tǒng)是千奇百怪的
-------------------------------
這里沒(méi)有精確的表達(dá)我的意思,本來(lái)我想說(shuō)的是:目前我還不掌握足夠的底層知識(shí),需要積累到一定程度才能領(lǐng)會(huì)上層邏輯的精妙。因此,我準(zhǔn)備先潛心研究一下具體的中斷控制器再回頭看看怎么修改這份文檔。目前考慮先看看ARM+GIC系統(tǒng),X86+Multi APIC系統(tǒng)和PowerPC+MPIC系統(tǒng)

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多