weixin_44532477 2019-01-15 14:53
浏览 1162

Linux内核SATA驱动部分代码疑问

我是一名ssd firmware开发人员,在查看linux SATA驱动代码时,下面的代码中,第152行到155行的代码看起来很容易死循环,由于我对linux内核代码不熟悉,而且linux很稳定,我不是很确定这部分代码是否有问题,还请熟悉linux内核的同学帮我看一下这个代码有没有问题,贴出的代码来自linux kernel 4.20.2,文件路径是\driver\ata\sata_dwc_460ex.c, 507行

static irqreturn_t sata_dwc_isr(int irq, void *dev_instance)
{
    struct ata_host *host = (struct ata_host *)dev_instance;
    struct sata_dwc_device *hsdev = HSDEV_FROM_HOST(host);
    struct ata_port *ap;
    struct ata_queued_cmd *qc;
    unsigned long flags;
    u8 status, tag;
    int handled, num_processed, port = 0;
    uint intpr, sactive, sactive2, tag_mask;
    struct sata_dwc_device_port *hsdevp;
    hsdev->sactive_issued = 0;

    spin_lock_irqsave(&host->lock, flags);

    /* Read the interrupt register */
    intpr = sata_dwc_readl(&hsdev->sata_dwc_regs->intpr);

    ap = host->ports[port];
    hsdevp = HSDEVP_FROM_AP(ap);

    dev_dbg(ap->dev, "%s intpr=0x%08x active_tag=%d\n", __func__, intpr,
        ap->link.active_tag);

    /* Check for error interrupt */
    if (intpr & SATA_DWC_INTPR_ERR) {
        sata_dwc_error_intr(ap, hsdev, intpr);
        handled = 1;
        goto DONE;
    }

    /* Check for DMA SETUP FIS (FP DMA) interrupt */
    if (intpr & SATA_DWC_INTPR_NEWFP) {
        clear_interrupt_bit(hsdev, SATA_DWC_INTPR_NEWFP);

        tag = (u8)(sata_dwc_readl(&hsdev->sata_dwc_regs->fptagr));
        dev_dbg(ap->dev, "%s: NEWFP tag=%d\n", __func__, tag);
        if (hsdevp->cmd_issued[tag] != SATA_DWC_CMD_ISSUED_PEND)
            dev_warn(ap->dev, "CMD tag=%d not pending?\n", tag);

        hsdev->sactive_issued |= qcmd_tag_to_mask(tag);

        qc = ata_qc_from_tag(ap, tag);
        /*
         * Start FP DMA for NCQ command.  At this point the tag is the
         * active tag.  It is the tag that matches the command about to
         * be completed.
         */
        qc->ap->link.active_tag = tag;
        sata_dwc_bmdma_start_by_tag(qc, tag);

        handled = 1;
        goto DONE;
    }
    sata_dwc_scr_read(&ap->link, SCR_ACTIVE, &sactive);
    tag_mask = (hsdev->sactive_issued | sactive) ^ sactive;

    /* If no sactive issued and tag_mask is zero then this is not NCQ */
    if (hsdev->sactive_issued == 0 && tag_mask == 0) {
        if (ap->link.active_tag == ATA_TAG_POISON)
            tag = 0;
        else
            tag = ap->link.active_tag;
        qc = ata_qc_from_tag(ap, tag);

        /* DEV interrupt w/ no active qc? */
        if (unlikely(!qc || (qc->tf.flags & ATA_TFLAG_POLLING))) {
            dev_err(ap->dev,
                "%s interrupt with no active qc qc=%p\n",
                __func__, qc);
            ap->ops->sff_check_status(ap);
            handled = 1;
            goto DONE;
        }
        status = ap->ops->sff_check_status(ap);

        qc->ap->link.active_tag = tag;
        hsdevp->cmd_issued[tag] = SATA_DWC_CMD_ISSUED_NOT;

        if (status & ATA_ERR) {
            dev_dbg(ap->dev, "interrupt ATA_ERR (0x%x)\n", status);
            sata_dwc_qc_complete(ap, qc, 1);
            handled = 1;
            goto DONE;
        }

        dev_dbg(ap->dev, "%s non-NCQ cmd interrupt, protocol: %s\n",
            __func__, get_prot_descript(qc->tf.protocol));
DRVSTILLBUSY:
        if (ata_is_dma(qc->tf.protocol)) {
            /*
             * Each DMA transaction produces 2 interrupts. The DMAC
             * transfer complete interrupt and the SATA controller
             * operation done interrupt. The command should be
             * completed only after both interrupts are seen.
             */
            hsdevp->dma_interrupt_count++;
            if (hsdevp->dma_pending[tag] == \
                    SATA_DWC_DMA_PENDING_NONE) {
                dev_err(ap->dev,
                    "%s: DMA not pending intpr=0x%08x status=0x%08x pending=%d\n",
                    __func__, intpr, status,
                    hsdevp->dma_pending[tag]);
            }

            if ((hsdevp->dma_interrupt_count % 2) == 0)
                sata_dwc_dma_xfer_complete(ap, 1);
        } else if (ata_is_pio(qc->tf.protocol)) {
            ata_sff_hsm_move(ap, qc, status, 0);
            handled = 1;
            goto DONE;
        } else {
            if (unlikely(sata_dwc_qc_complete(ap, qc, 1)))
                goto DRVSTILLBUSY;
        }

        handled = 1;
        goto DONE;
    }

    /*
     * This is a NCQ command. At this point we need to figure out for which
     * tags we have gotten a completion interrupt.  One interrupt may serve
     * as completion for more than one operation when commands are queued
     * (NCQ).  We need to process each completed command.
     */

     /* process completed commands */
    sata_dwc_scr_read(&ap->link, SCR_ACTIVE, &sactive);
    tag_mask = (hsdev->sactive_issued | sactive) ^ sactive;

    if (sactive != 0 || hsdev->sactive_issued > 1 || tag_mask > 1) {
        dev_dbg(ap->dev,
            "%s NCQ:sactive=0x%08x  sactive_issued=0x%08x tag_mask=0x%08x\n",
            __func__, sactive, hsdev->sactive_issued, tag_mask);
    }

    if ((tag_mask | hsdev->sactive_issued) != hsdev->sactive_issued) {
        dev_warn(ap->dev,
             "Bad tag mask?  sactive=0x%08x sactive_issued=0x%08x  tag_mask=0x%08x\n",
             sactive, hsdev->sactive_issued, tag_mask);
    }

    /* read just to clear ... not bad if currently still busy */
    status = ap->ops->sff_check_status(ap);
    dev_dbg(ap->dev, "%s ATA status register=0x%x\n", __func__, status);

    tag = 0;
    num_processed = 0;
    while (tag_mask) {
        num_processed++;
        while (!(tag_mask & 0x00000001)) {
            tag++;
            tag_mask <<= 1;
        }

        tag_mask &= (~0x00000001);
        qc = ata_qc_from_tag(ap, tag);

        /* To be picked up by completion functions */
        qc->ap->link.active_tag = tag;
        hsdevp->cmd_issued[tag] = SATA_DWC_CMD_ISSUED_NOT;

        /* Let libata/scsi layers handle error */
        if (status & ATA_ERR) {
            dev_dbg(ap->dev, "%s ATA_ERR (0x%x)\n", __func__,
                status);
            sata_dwc_qc_complete(ap, qc, 1);
            handled = 1;
            goto DONE;
        }

        /* Process completed command */
        dev_dbg(ap->dev, "%s NCQ command, protocol: %s\n", __func__,
            get_prot_descript(qc->tf.protocol));
        if (ata_is_dma(qc->tf.protocol)) {
            hsdevp->dma_interrupt_count++;
            if (hsdevp->dma_pending[tag] == \
                    SATA_DWC_DMA_PENDING_NONE)
                dev_warn(ap->dev, "%s: DMA not pending?\n",
                    __func__);
            if ((hsdevp->dma_interrupt_count % 2) == 0)
                sata_dwc_dma_xfer_complete(ap, 1);
        } else {
            if (unlikely(sata_dwc_qc_complete(ap, qc, 1)))
                goto STILLBUSY;
        }
        continue;

STILLBUSY:
        ap->stats.idle_irq++;
        dev_warn(ap->dev, "STILL BUSY IRQ ata%d: irq trap\n",
            ap->print_id);
    } /* while tag_mask */

    /*
     * Check to see if any commands completed while we were processing our
     * initial set of completed commands (read status clears interrupts,
     * so we might miss a completed command interrupt if one came in while
     * we were processing --we read status as part of processing a completed
     * command).
     */
    sata_dwc_scr_read(&ap->link, SCR_ACTIVE, &sactive2);
    if (sactive2 != sactive) {
        dev_dbg(ap->dev,
            "More completed - sactive=0x%x sactive2=0x%x\n",
            sactive, sactive2);
    }
    handled = 1;

DONE:
    spin_unlock_irqrestore(&host->lock, flags);
    return IRQ_RETVAL(handled);
}

@天麓

  • 写回答

0条回答 默认 最新

    报告相同问题?

    悬赏问题

    • ¥50 易语言把MYSQL数据库中的数据添加至组合框
    • ¥20 求数据集和代码#有偿答复
    • ¥15 关于下拉菜单选项关联的问题
    • ¥20 java-OJ-健康体检
    • ¥15 rs485的上拉下拉,不会对a-b<-200mv有影响吗,就是接受时,对判断逻辑0有影响吗
    • ¥15 使用phpstudy在云服务器上搭建个人网站
    • ¥15 应该如何判断含间隙的曲柄摇杆机构,轴与轴承是否发生了碰撞?
    • ¥15 vue3+express部署到nginx
    • ¥20 搭建pt1000三线制高精度测温电路
    • ¥15 使用Jdk8自带的算法,和Jdk11自带的加密结果会一样吗,不一样的话有什么解决方案,Jdk不能升级的情况