以下是write系统调用内核态处理函数的路径:
经过一系列处理,write系统调用处理结束后,若需要写磁盘数据最终会经过以下路径:
scsi_scan_target(scsi扫面函数)——》__scsi_scan_target ——》scsi_sequential_lun_scan ——》scsi_probe_and_add_lun ——>scsi_alloc_sdev ——》scsi_alloc_queue(scsi分配队列),从这里分开,一条路径是设置DMA并发送命令到DMA控制器(路径一),另一条是初始化函数路径(路径二)。
路径一:scsi_request_fn——>scsi_dispatch_cmd——》scsi_log_send——》(.queuecommand =ata_scsi_queuecmd,)ata_scsi_queuecmd——》__ata_scsi_queuecmd——》ata_scsi_translate——》ata_qc_issue——》ata_bmdma_qc_issue——》(bfin_bmdma_setup:设置DMA寄存器/ bfin_bmdma_start:开始DMA)
路径二:scsi_prep_fn——>scsi_setup_blk_pc_cmnd ——》scsi_init_io ——》scsi_init_sgtable ——》blk_rq_map_sg(该函数的参数request这个结构体封装了bio结构体).
以下主要分析bfin_bmdma_setup和bfin_bmdma_start函数,即DMA操作过程:
(1) 软件准备好一个PRD Table放在内存中,每个8字节,对齐到4字节边界。
(2) 软件把PRD table的起始地址设置好,同时通过设置读/写控制位设置数据和传输方向,清除状态寄存器中的中断位和错误位。
(3) 软件发出DMA传送指令到disk设备。
(4) 向总线控制器IDE命令寄存器的对应通道中写入1,使能总线控制器。
(5) DMA从IDE设备中请求控制器传送数据到/从内存中
(6) 传送结束,IDE设备发出中断
(7) 接收到中断后,软件设置命令寄存器的开始/结束位,然后先后读控制器状态、驱动状态,进而确定是否传送成功。
代码如下:
/**
* bfin_bmdma_setup- Set up IDE DMA transaction
* @qc:Info associated with this ATA transaction.
*
* Note:Original code is ata_bmdma_setup().
*/
static void bfin_bmdma_setup(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
/*下面结构体就是scatterlist 结构体的封装,其指向所有scatterlist 在内存中的位置,对应以上步骤(2)*/
struct dma_desc_array *dma_desc_cpu =(struct dma_desc_array *)ap->bmdma_prd;
void __iomem *base = (void __iomem*)ap->ioaddr.ctl_addr;
/*DMA配置*/
unsigned short config = DMAFLOW_ARRAY |NDSIZE_5 | RESTART | WDSIZE_16 | DMAEN;
struct scatterlist *sg;
unsigned int si;
unsigned int channel;
unsigned int dir;
unsigned int size = 0;
dev_dbg(qc->ap->dev, "inatapi dma setup\n");
/* Program the ATA_CTRL register withdir */
/*设置ATA控制寄存器,与DMA控制寄存器无关*/
if (qc->tf.flags &ATA_TFLAG_WRITE) {
channel = CH_ATAPI_TX;
dir = DMA_TO_DEVICE;
} else {
channel = CH_ATAPI_RX;
dir = DMA_FROM_DEVICE;
config |= WNR;
}
dma_map_sg(ap->dev,qc->sg, qc->n_elem, dir);
/* fill the ATAPI DMA controller *//挨个填写sg结构体,sg结构体用来指向每个要传输的内存块,对应以上步骤(1)
for_each_sg(qc->sg, sg,qc->n_elem, si) {
dma_desc_cpu[si].start_addr =sg_dma_address(sg);
dma_desc_cpu[si].cfg = config;
dma_desc_cpu[si].x_count =sg_dma_len(sg) >> 1;
dma_desc_cpu[si].x_modify = 2;
size += sg_dma_len(sg);
}
/* Send ATA DMA command */
/*这里要注意,虽然是发送DMA命令,但是真正的DMA操作还没开始;
*该函数中有设置各种ATA设备寄存器,并等待设置结束后返回
*/
bfin_exec_command(ap, &qc->tf);
//根据初始化时设置的IO操作方向,确定DMA方向,对应步骤(2)
if (qc->tf.flags &ATA_TFLAG_WRITE) {
/*set ATA DMA write direction */
ATAPI_SET_CONTROL(base,(ATAPI_GET_CONTROL(base)
| XFER_DIR));
} else {
/* set ATA DMA read direction*/
ATAPI_SET_CONTROL(base,(ATAPI_GET_CONTROL(base)
& ~XFER_DIR));
}
/* Reset all transfer count */
ATAPI_SET_CONTROL(base,ATAPI_GET_CONTROL(base) | TFRCNT_RST);
/* Set ATAPI state machine contorl interminate sequence */
ATAPI_SET_CONTROL(base,ATAPI_GET_CONTROL(base) | END_ON_TERM);
/* Set transfer length to the totalsize of sg buffers */
ATAPI_SET_XFER_LEN(base, size >>1);
/**
* bfin_bmdma_start- Start an IDE DMA transaction
* @qc:Info associated with this ATA transaction.
*
* Note:Original code is ata_bmdma_start().
*/ static void bfin_bmdma_start(structata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
void __iomem *base = (void __iomem*)ap->ioaddr.ctl_addr;
dev_dbg(qc->ap->dev, "inatapi dma start\n");
if (!(ap->udma_mask ||ap->mwdma_mask))
return;
/* start ATAPI transfer*/
if (ap->udma_mask)
ATAPI_SET_CONTROL(base, ATAPI_GET_CONTROL(base)
| ULTRA_START);
else
ATAPI_SET_CONTROL(base,ATAPI_GET_CONTROL(base)
| MULTI_START);
} |