编写设备驱动程序

分配 DMA 资源

如果符合以下条件,则 tran_init_pkt(9E) 入口点必须分配用于数据传送的 DMA 资源:

HBA 驱动程序需要跟踪如何为特定命令分配 DMA 资源。按包的 HBA 驱动程序专用数据的标志位或 DMA 句柄可能会进行此分配。

使用 pkt 中的 PKT_DMA_PARTIAL 标志,目标驱动程序可以将数据传送按多个 SCSI 命令分类以适应整个请求。如果 HBA 硬件的分散/集中功能或系统 DMA 资源无法完成单个 SCSI 命令的请求,则此方法会非常有用。

使用 PKT_DMA_PARTIAL 标志,HBA 驱动程序可以设置 DDI_DMA_PARTIAL 标志。DDI_DMA_PARTIAL 标志有助于分配此 SCSI 命令的 DMA 资源。例如,ddi_dma_buf_bind_handle(9F) 命令可用于分配 DMA 资源。分配 DMA 资源时使用的 DMA 属性应准确说明针对 HBA 硬件执行 DMA 的能力设定的约束。如果系统只能为部分请求分配 DMA 资源,则 ddi_dma_buf_bind_handle(9F) 将返回 DDI_DMA_PARTIAL_MAP

tran_init_pkt(9E) 入口点必须在字段 pkt_resid 中返回未为此传送分配的 DMA 资源量。

目标驱动程序可以请求 tran_init_pkt(9E) 同时为该 pkt 分配 scsi_pkt(9S) 结构和 DMA 资源。在这种情况下,如果 HBA 驱动程序无法分配 DMA 资源,则该驱动程序必须在返回前释放已分配的 scsi_pkt(9S)scsi_pkt(9S) 必须通过调用 scsi_hba_pkt_free(9F) 进行释放。

目标驱动程序可能会首先分配 scsi_pkt(9S),随后再为此 pkt 分配 DMA 资源。在这种情况下,如果 HBA 驱动程序无法分配 DMA 资源,则该驱动程序决不能释放 pkt。在这种情况下,目标驱动程序负责释放 pkt


示例 18–3 HBA 驱动程序的 DMA 资源分配

static int
isp_i_dma_alloc(
    struct isp         *isp,
    struct scsi_pkt    *pkt,
    struct buf         *bp,
    int                flags,
    int                (*callback)())
{
    struct isp_cmd     *sp  = (struct isp_cmd *)pkt->pkt_ha_private;
    int                dma_flags;
    ddi_dma_attr_t     tmp_dma_attr;
    int                (*cb)(caddr_t);
    int                i;

    ASSERT(callback == NULL_FUNC || callback == SLEEP_FUNC);

    if (bp->b_flags & B_READ) {
        sp->cmd_flags &= ~CFLAG_DMASEND;
        dma_flags = DDI_DMA_READ;
    } else {
        sp->cmd_flags |= CFLAG_DMASEND;
        dma_flags = DDI_DMA_WRITE;
    }
    if (flags & PKT_CONSISTENT) {
        sp->cmd_flags |= CFLAG_CMDIOPB;
        dma_flags |= DDI_DMA_CONSISTENT;
    }
    if (flags & PKT_DMA_PARTIAL) {
        dma_flags |= DDI_DMA_PARTIAL;
    }

    tmp_dma_attr = isp_dma_attr;
    tmp_dma_attr.dma_attr_burstsizes = isp->isp_burst_size;

    cb = (callback == NULL_FUNC) ? DDI_DMA_DONTWAIT :
DDI_DMA_SLEEP;

    if ((i = ddi_dma_alloc_handle(isp->isp_dip, &tmp_dma_attr,
      cb, 0, &sp->cmd_dmahandle)) != DDI_SUCCESS) {
        switch (i) {
        case DDI_DMA_BADATTR:
            bioerror(bp, EFAULT);
            return (0);
        case DDI_DMA_NORESOURCES:
            bioerror(bp, 0);
            return (0);
        }
    }

    i = ddi_dma_buf_bind_handle(sp->cmd_dmahandle, bp, dma_flags,
    cb, 0, &sp->cmd_dmacookies[0], &sp->cmd_ncookies);

    switch (i) {
    case DDI_DMA_PARTIAL_MAP:
    if (ddi_dma_numwin(sp->cmd_dmahandle, &sp->cmd_nwin) ==
            DDI_FAILURE) {
        cmn_err(CE_PANIC, "ddi_dma_numwin() failed\n");
    }

    if (ddi_dma_getwin(sp->cmd_dmahandle, sp->cmd_curwin,
        &sp->cmd_dma_offset, &sp->cmd_dma_len,
        &sp->cmd_dmacookies[0], &sp->cmd_ncookies) ==
           DDI_FAILURE) {
        cmn_err(CE_PANIC, "ddi_dma_getwin() failed\n");
    }
    goto get_dma_cookies;

    case DDI_DMA_MAPPED:
    sp->cmd_nwin = 1;
    sp->cmd_dma_len = 0;
    sp->cmd_dma_offset = 0;

get_dma_cookies:
    i = 0;
    sp->cmd_dmacount = 0;
    for (;;) {
        sp->cmd_dmacount += sp->cmd_dmacookies[i++].dmac_size;

        if (i == ISP_NDATASEGS || i == sp->cmd_ncookies)
        break;
        ddi_dma_nextcookie(sp->cmd_dmahandle,
        &sp->cmd_dmacookies[i]);
    }
    sp->cmd_cookie = i;
    sp->cmd_cookiecnt = i;

    sp->cmd_flags |= CFLAG_DMAVALID;
    pkt->pkt_resid = bp->b_bcount - sp->cmd_dmacount;
    return (1);

    case DDI_DMA_NORESOURCES:
    bioerror(bp, 0);
    break;

    case DDI_DMA_NOMAPPING:
    bioerror(bp, EFAULT);
    break;

    case DDI_DMA_TOOBIG:
    bioerror(bp, EINVAL);
    break;

    case DDI_DMA_INUSE:
    cmn_err(CE_PANIC, "ddi_dma_buf_bind_handle:"
        " DDI_DMA_INUSE impossible\n");

    default:
    cmn_err(CE_PANIC, "ddi_dma_buf_bind_handle:"
        " 0x%x impossible\n", i);
    }

    ddi_dma_free_handle(&sp->cmd_dmahandle);
    sp->cmd_dmahandle = NULL;
    sp->cmd_flags &= ~CFLAG_DMAVALID;
    return (0);
}