如果符合以下条件,则 tran_init_pkt(9E) 入口点必须分配用于数据传送的 DMA 资源:
bp 不为 null。
bp->b_bcount 不为零。
尚未为此 scsi_pkt(9S) 分配 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。
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);
}