IPMI 通过中断和timer 两种机制处理发送给bmc的命令

在acpi_ipmi_probe 中可以发现ipmi支持两种形式的中断
    /* If _GPE exists, use it; otherwise use standard interrupts */
    status = acpi_evaluate_integer(handle, "_GPE", NULL, &tmp);
    if (ACPI_SUCCESS(status)) {
        info->irq = tmp;
        info->irq_setup = acpi_gpe_irq_setup;
    } else {
        int irq = platform_get_irq(dev, 0);

        if (irq > 0) {
            info->irq = irq;
            info->irq_setup = std_irq_setup;
        }
    }
一种是acpi的GPE中断,这个中断是需要bios支持的,一种是正常的中断
首先看看GPE中断
static int acpi_gpe_irq_setup(struct smi_info *info)
{
    acpi_status status;

    if (!info->irq)
        return 0;

    status = acpi_install_gpe_handler(NULL,
                      info->irq,
                      ACPI_GPE_LEVEL_TRIGGERED,
                      &ipmi_acpi_gpe,
                      info);
    if (status != AE_OK) {
        dev_warn(info->dev, "%s unable to claim ACPI GPE %d,"
             " running polled\n", DEVICE_NAME, info->irq);
        info->irq = 0;
        return -EINVAL;
    } else {
        info->irq_cleanup = acpi_gpe_irq_cleanup;
        dev_info(info->dev, "Using ACPI GPE %d\n", info->irq);
        return 0;
    }
}
基本就是调用acpi_install_gpe_handler 来注册handler,对应的type是ACPI_GPE_LEVEL_TRIGGERED,如果发生中断的话,就调用ipmi_acpi_gpe
static u32 ipmi_acpi_gpe(acpi_handle gpe_device,
    u32 gpe_number, void *context)
{
    struct smi_info *smi_info = context;
    unsigned long   flags;

    spin_lock_irqsave(&(smi_info->si_lock), flags);

    smi_inc_stat(smi_info, interrupts);

    debug_timestamp("ACPI_GPE");

    smi_event_handler(smi_info, 0);
    spin_unlock_irqrestore(&(smi_info->si_lock), flags);

    return ACPI_INTERRUPT_HANDLED;
}
在ipmi_acpi_gpe 中就直接调用smi_event_handler
回到acpi_ipmi_probe 中标准的中断注册是调用std_irq_setup
static int std_irq_setup(struct smi_info *info)
{
    int rv;

    if (!info->irq)
        return 0;

    if (info->si_type == SI_BT) {
        rv = request_irq(info->irq,
                 si_bt_irq_handler,
                 IRQF_SHARED,
                 DEVICE_NAME,
                 info);
        if (!rv)
            /* Enable the interrupt in the BT interface. */
            info->io.outputb(&info->io, IPMI_BT_INTMASK_REG,
                     IPMI_BT_INTMASK_ENABLE_IRQ_BIT);
    } else
        rv = request_irq(info->irq,
                 si_irq_handler,
                 IRQF_SHARED,
                 DEVICE_NAME,
                 info);
    if (rv) {
        dev_warn(info->dev, "%s unable to claim interrupt %d,"
             " running polled\n",
             DEVICE_NAME, info->irq);
        info->irq = 0;
    } else {
        info->irq_cleanup = std_irq_cleanup;
        dev_info(info->dev, "Using irq %d\n", info->irq);
    }

    return rv;
}
可见bt和其他的中断处理函数有所不同si_bt_irq_handler->
static irqreturn_t si_bt_irq_handler(int irq, void *data)
{
    struct smi_info *smi_info = data;
    /* We need to clear the IRQ flag for the BT interface. */
    smi_info->io.outputb(&smi_info->io, IPMI_BT_INTMASK_REG,
                 IPMI_BT_INTMASK_CLEAR_IRQ_BIT
                 | IPMI_BT_INTMASK_ENABLE_IRQ_BIT);
    return si_irq_handler(irq, data);
}
i_bt_irq_handler->si_irq_handler—>smi_event_handler.可见最终都是调用smi_event_handler。只是bt比其的case要多写一个flag
static enum si_sm_result smi_event_handler(struct smi_info *smi_info,
                       int time)
{
    enum si_sm_result si_sm_result;

restart:
    /*
     * There used to be a loop here that waited a little while
     * (around 25us) before giving up.  That turned out to be
     * pointless, the minimum delays I was seeing were in the 300us
     * range, which is far too long to wait in an interrupt.  So
     * we just run until the state machine tells us something
     * happened or it needs a delay.
     */
//通过event想bmc发送命令后面si_sm_result就是bmc返回的结果
    si_sm_result = smi_info->handlers->event(smi_info->si_sm, time);
    time = 0;
    while (si_sm_result == SI_SM_CALL_WITHOUT_DELAY)
        si_sm_result = smi_info->handlers->event(smi_info->si_sm, 0);

    if (si_sm_result == SI_SM_TRANSACTION_COMPLETE) {
        smi_inc_stat(smi_info, complete_transactions);

        handle_transaction_done(smi_info);
        goto restart;
    } else if (si_sm_result == SI_SM_HOSED) {
        smi_inc_stat(smi_info, hosed_count);

        /*
         * Do the before return_hosed_msg, because that
         * releases the lock.
         */
        smi_info->si_state = SI_NORMAL;
        if (smi_info->curr_msg != NULL) {
            /*
             * If we were handling a user message, format
             * a response to send to the upper layer to
             * tell it about the error.
             */
            return_hosed_msg(smi_info, IPMI_ERR_UNSPECIFIED);
        }
        goto restart;
    }

    if (si_sm_result == SI_SM_IDLE && smi_info->timer_running) {
        /* Ok it if fails, the timer will just go off. */
        if (del_timer(&smi_info->si_timer))
            smi_info->timer_running = false;
    }

out:
    return si_sm_result;
}
这里需要注意的是如果si_sm_result == SI_SM_IDLE,则说明当前已经没有要处理的命令了,就把smi_info->si_timer 停掉
这个timer是在smi_start_processing 中赋值的。
static int smi_start_processing(void       *send_info,
                ipmi_smi_t intf)
{
    struct smi_info *new_smi = send_info;
    int             enable = 0;

    new_smi->intf = intf;

    /* Set up the timer that drives the interface. */
    setup_timer(&new_smi->si_timer, smi_timeout, (long)new_smi);
    smi_mod_timer(new_smi, jiffies + SI_TIMEOUT_JIFFIES);
}
每隔SI_TIMEOUT_JIFFIES 就会调用smi_timeout
static void smi_timeout(unsigned long data)
{
    struct smi_info   *smi_info = (struct smi_info *) data;
    enum si_sm_result smi_result;
    unsigned long     flags;
    unsigned long     jiffies_now;
    long              time_diff;
    long          timeout;

    spin_lock_irqsave(&(smi_info->si_lock), flags);
    debug_timestamp("Timer");

    jiffies_now = jiffies;
    time_diff = (((long)jiffies_now - (long)smi_info->last_timeout_jiffies)
             * SI_USEC_PER_JIFFY);
    smi_result = smi_event_handler(smi_info, time_diff);

    if ((smi_info->irq) && (!smi_info->interrupt_disabled)) {
        /* Running with interrupts, only do long timeouts. */
        timeout = jiffies + SI_TIMEOUT_JIFFIES;
        smi_inc_stat(smi_info, long_timeouts);
        goto do_mod_timer;
    }

    /*
     * If the state machine asks for a short delay, then shorten
     * the timer timeout.
     */
    if (smi_result == SI_SM_CALL_WITH_DELAY) {
        smi_inc_stat(smi_info, short_timeouts);
        timeout = jiffies + 1;
    } else {
        smi_inc_stat(smi_info, long_timeouts);
        timeout = jiffies + SI_TIMEOUT_JIFFIES;
    }

do_mod_timer:
    if (smi_result != SI_SM_IDLE)
        smi_mod_timer(smi_info, timeout);
    else
        smi_info->timer_running = false;
    spin_unlock_irqrestore(&(smi_info->si_lock), flags);
}
可见在smi_timeout 中依然是调用smi_event_handler 来处理IPMI发给bmc的命令。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值