zoukankan      html  css  js  c++  java
  • Linux3.10.0块IO子系统流程(5)-- 为SCSI命令准备聚散列表

    SCSI数据缓冲区组织成聚散列表的形式。Linux内核中表示聚散列表的基本数据结构是scatterlist,虽然名字中有list,但它只对应一个内存缓冲区,聚散列表就是多个scatterlist的组合。这种组合是链表+数组的结合。这是因为他使用的内存以页面为基本单位分配,每个页面相当于一个scatterlist。每个scatterlist以链表方式组织起来。

     1 /*
     2 * Function:    scsi_init_io()
     3 *
     4 * Purpose:     SCSI I/O initialize function.
     5 *
     6 * Arguments:   cmd   - Command descriptor we wish to initialize
     7 *
     8 * Returns:     0 on success
     9 *        BLKPREP_DEFER if the failure is retryable
    10 *        BLKPREP_KILL if the failure is fatal
    11 */
    12 int scsi_init_io(struct scsi_cmnd *cmd, gfp_t gfp_mask)
    13 {
    14     struct request *rq = cmd->request;
    15 
    16     // 初始化sg列表
    17     int error = scsi_init_sgtable(rq, &cmd->sdb, gfp_mask);
    18     if (error)
    19         goto err_exit;
    20 
    21     // 如果是双向请求,则为关联的request分配SCSI数据缓冲区,用于另一方向的数据传输,然后调用scsi_init_sgtable分配聚散列表,最后进行映射
    22     if (blk_bidi_rq(rq)) {
    23         struct scsi_data_buffer *bidi_sdb = kmem_cache_zalloc(
    24             scsi_sdb_cache, GFP_ATOMIC);
    25         if (!bidi_sdb) {
    26             error = BLKPREP_DEFER;
    27             goto err_exit;
    28         }
    29 
    30         rq->next_rq->special = bidi_sdb;
    31         error = scsi_init_sgtable(rq->next_rq, bidi_sdb, GFP_ATOMIC);
    32         if (error)
    33             goto err_exit;
    34     }
    35 
    36     /*
    37      * 如果是完整性请求,即原始bio中带有完整性载荷,则调用blk_rq_count_integrity_sg计算完整性数据段的数目
    38      * 然后调用scsi_alloc_sgtable分配聚散列表,再调用blk_rq_map_integrity_sg将完整性数据映射到这个聚散列表,最后更新聚散列表已映射的项数
    39      * 实际上,完整性请求的处理过程概括了scsi_init_sgtable的操作流程,它实际上是这个过程的一个封装
    40      * 即调用scsi_alloc_sgtable分配指定数据数据段的聚散列表,然后调用blk_rq_map_sg进行映射,最后更新列表已映射的项数
    41      */
    42     if (blk_integrity_rq(rq)) {
    43         struct scsi_data_buffer *prot_sdb = cmd->prot_sdb;
    44         int ivecs, count;
    45 
    46         BUG_ON(prot_sdb == NULL);
    47         ivecs = blk_rq_count_integrity_sg(rq->q, rq->bio);
    48 
    49         if (scsi_alloc_sgtable(prot_sdb, ivecs, gfp_mask)) {
    50             error = BLKPREP_DEFER;
    51             goto err_exit;
    52         }
    53 
    54         count = blk_rq_map_integrity_sg(rq->q, rq->bio,
    55                         prot_sdb->table.sgl);
    56         BUG_ON(unlikely(count > ivecs));
    57         BUG_ON(unlikely(count > queue_max_integrity_segments(rq->q)));
    58 
    59         cmd->prot_sdb = prot_sdb;
    60         cmd->prot_sdb->table.nents = count;
    61     }
    62 
    63     return BLKPREP_OK ;
    64 
    65 err_exit:
    66     scsi_release_buffers(cmd);
    67     cmd->request->special = NULL;
    68     scsi_put_command(cmd);
    69     return error;
    70 }

    blk_rq_map_sg函数如下:

     1 /*
     2  * map a request to scatterlist, return number of sg entries setup. Caller
     3  * must make sure sg can hold rq->nr_phys_segments entries
     4  */
     5 int blk_rq_map_sg(struct request_queue *q, struct request *rq,
     6           struct scatterlist *sglist)
     7 {
     8     struct bio_vec *bvec, *bvprv;
     9     struct req_iterator iter;
    10     struct scatterlist *sg;
    11     int nsegs, cluster;
    12 
    13     nsegs = 0;
    14     cluster = blk_queue_cluster(q);
    15 
    16     /*
    17      * for each bio in rq
    18      */
    19     bvprv = NULL;
    20     sg = NULL;
    21     rq_for_each_segment(bvec, rq, iter) {
    22         __blk_segment_map_sg(q, bvec, sglist, &bvprv, &sg,
    23                      &nsegs, &cluster);
    24     } /* segments in rq */
    25 
    26 
    27     if (unlikely(rq->cmd_flags & REQ_COPY_USER) &&
    28         (blk_rq_bytes(rq) & q->dma_pad_mask)) {
    29         unsigned int pad_len =
    30             (q->dma_pad_mask & ~blk_rq_bytes(rq)) + 1;
    31 
    32         sg->length += pad_len;
    33         rq->extra_len += pad_len;
    34     }
    35 
    36     if (q->dma_drain_size && q->dma_drain_needed(rq)) {
    37         if (rq->cmd_flags & REQ_WRITE)
    38             memset(q->dma_drain_buffer, 0, q->dma_drain_size);
    39 
    40         sg->page_link &= ~0x02;
    41         sg = sg_next(sg);
    42         sg_set_page(sg, virt_to_page(q->dma_drain_buffer),
    43                 q->dma_drain_size,
    44                 ((unsigned long)q->dma_drain_buffer) &
    45                 (PAGE_SIZE - 1));
    46         nsegs++;
    47         rq->extra_len += q->dma_drain_size;
    48     }
    49 
    50     if (sg)
    51         sg_mark_end(sg);
    52 
    53     return nsegs;
    54 }

    在bio处理过程中的两次合并,第一个合并由IO调度算法负责,它将在磁盘扇区上连续的请求合并到一个request中。第二次合并出现在SCSI策略例程,如果低层驱动支持,则进而将内存中连续的段合并为聚散列表中的一项,如下图,两个bio(每个bio有两段请求)在经过两个合并之后,聚散列表最终有三个项目。

  • 相关阅读:
    VBA通配符(*, ?, ~)
    pycharm更换包下载镜像
    python在使用pip安装模块的时候下载速度很慢的问题
    java标识符
    C语言||作业01 结构:通讯录
    C语言寒假大作战04
    C语言寒假大作战03
    C语言寒假大作战02
    c语言寒假大作战
    C语言I作业12—学期总结
  • 原文地址:https://www.cnblogs.com/luxiaodai/p/9266317.html
Copyright © 2011-2022 走看看