linux 将外设抽象为 /dev 下的文件,通过统一的文件读写接口访问外设

linux与外设通信:

  • I/O 端口:通过 I/O 读写访问设备
  • I/O 内存映射:对 I/O 端口进行内存映射,将外设地址映射到内存地址,PCI 总线寻址通过内存映射完成
  • 中断

- 阅读剩余部分 -

memory

managed memory : 使用 linux heterogeneous memory management (HMM), device 和 host 端可以以相同指针访问同一块内存

coherent memory : 可以在 kernel 运行时执行对 host 和其他 peer 可见的原子操作,通过不 cache 内存实现
non-coherent memory : device 端 cache 的内存,修改不实时可见

调度

direct dispatch : runtime 直接将操作发送至 AQL 队列
device side malloc

编译与链接

HIP 支持两种 static lib:

  • 只包含 host 函数,可以使用 gcc 等非 hipcc 编译器链接
  • 包含 device 函数,只能使用 hipcc 链接

driver api v.s. runtime api

driver api 更为细粒度,例如 runtime 中所有 kernel 初始化时自动 load 且程序运行时保持 load ,driver api 可以只保持当前需要 load 的 module

从用户接口上:
driver api 通常返回 CUresult
runtime api 通常返回 cudaError_t

- 阅读剩余部分 -

代码版本:linux-4.9.327
(最老的一个 long term 版)
多年来第一次鼓起勇气开 linux 源码
感觉也没有那么吓人(?)
不得不感慨 open source 以及 free software 真的是好文明

- 阅读剩余部分 -

关于

论文: Linux Block IO: Introducing Multi-queue SSD Access on
Multi-core Systems

IO 块设备的吞吐发展迅速,2012 年 SSD 4K 随机读写速度达到 785K IOPS (是不是有点高?)
SSD 随机读写速度与顺序读写持平 (不太清楚)

传统 块设备 队列优点:

  • 统一接口

    缺点:

  • 效率低,导致一些应用跳过块数据管理层
  • 单队列,单锁,造成核间竞争
  • 只有一个核处理与块设备通信,其他核需要软中断与该核通信,并且对于不同 NUMA 下的内存不友好

解决方案:

两层队列
软件队列:每个 NUMA 上 或者每个核上 一个队列
硬件队列:每个块设备上 若干队列

tag : 指示 block IO 在 driver 提交队列中的位置,在 IO 完成时指示位置

代码

hctx : hardware context

每个 request_queue 包含若干 request,包含一个 percpu 的 blk_mq_ctx (queue_ctx) ,包含若干 blk_mq_hw_ctx

每个 request 有一个 blk_mq_ctx

每个 blk_mq_hw_ctx 中包含若干 blk_mq_ctx

目前维护了 hardcoding 的 per-cpu 软件队列

使用 sbitmap 维护 pending work

struct blk_mq_hw_ctx { // include/linux/blk-mq.h
    struct {
        spinlock_t        lock;
        struct list_head    dispatch;
        unsigned long        state;        /* BLK_MQ_S_* flags */
    }
    struct blk_mq_ctx    **ctxs;
    struct blk_mq_tags    *tags; // bitmap tag
}

struct blk_mq_ctx  { // block/blk-mq.h
    unsigned int        cpu; // ctx 对应的 cpu ?
}

struct blk_mq_tags { // block/blk-mq-tag.h
    struct request **rqs; // rqs[tag] 为下标 tag 对应的 request 
}

struct request_queue { //include/linux/blkdev.h
    struct percpu_ref q_usage_counter;
    struct blk_mq_ctx __percpu    *queue_ctx; // per cpu 的本队列 context
    struct blk_mq_hw_ctx    **queue_hw_ctx; // 所有 hardware dispatch queue context ? 
    unsigned int        *mq_map; // 每个 cpu 对应的 hw_ctx 编号 ?
}
struct request { //include/linux/blkdev.h
    struct request_queue *q;
}