Edit online

命令队列模式

命令队列模式,即 queue 模式。在 queue 模式下,GE 驱动内部是以 queue 的方式执行的, GE 驱动只负责从用户态接收命令队列,然后执行命令队列。GE 的完整功能需要依赖 MPP 中间件, 在 MPP 中间件中会把用户设置的参数信息转换成硬件可以识别的命令队列信息。

Command Queue 相关的几个概念:

  • Task: GE(Graphics Engine) 可以执行的最小任务单元,比如说一次 blit 操作、一次矩形填充
  • Batch:硬件以 batch 为单位执行,是一系列命令的集合,可以包含一个或者多个 task,软件也必须以 batch 为单位向驱动写入命令
  • Command Queue:软件可以向 Queue 写入多个 batch,硬件以 batch 为单位,按顺序执行。GE Command Queue 是以 buffer 的方式实现的, 关于 buffer 的说明,请参考 GE 规格书

CMD queue 和 normal 模式驱动相比增加了hal_ge_write 接口,命令队列通过hal_ge_write 接口,以 batch 为单位发送给驱动,batch 中可以包含多个 task 的命令。hal_ge_write 操作是异步的,相应的命令只要写入驱动中的 buffer 即返回,不用等待硬件执行完当前 batch 中的所有命令, 当用户需要等待发送的命令执行完成时可以调用 SYNC 接口。在 queue 模式下,通过hal_ge_write 接口写入以 batch 为单位的命令, 硬件可以连续执行多个 task。而在 normal 模式下,通过 BITBLT 等接口,硬件一次只能执行一个任务。

CMD Queue 模式用户态可用 ioctl:

  • IOC_GE_VERSION
  • IOC_GE_MODE
  • IOC_GE_CMD_BUF_SIZE
  • IOC_GE_SYNC

初始化流程

GE 驱动的初始化过程见hal_ge_init()函数,除了申请 regs、clk、reset 等资源外, 还申请了存储 Queue 需要的 buffer,以及存储 batch 信息的结构体。 batch 结构体不存储实际的命令,batch 结构体中保存指向 buffer 的一段空间, 包括相对于 buffer 起始地址的 offset,以及当前 batch 中的命令 length。batch 结构体定义如下:
struct aic_ge_batch {
    struct list_head list;
    int offset;
    int length;
    int client_id;
};

下图 buffer 中不同的颜色代表不同的 batch 对应的命令:

../../images/ge_function_cmdq_0.png
1. ring buffer 与 batch 关系图
目前驱动中定义了 8 个存储 batch 信息的结构体,ring buffer 的 size 定义为 32K, 32K 的空间可以缓存超过 256 个 task(假如都是 RGB 格式的 task)。
#define MAX_BATCH_NUM 8
#define CMD_BUF_SIZE  (32 * 1024)

AIC_GE_CMDQ_BUF_LENGTH 在 Kconfig 中定义,可以在 menuconfig 去分配 CMD_BUF_SIZE 大小

batch 管理

  1. 每一个 batch 总共可能存在三种状态:
    • free 状态,batch 中没有可用信息,在 list 中
    • ready 状态,batch 中有等待执行的 cmd 信息,在 batch 中
    • 运行状态,当前硬件正在运行的 batch,即不在 list 中也不在 ready list 中
  2. 分配给应用的缓冲 buffer 中,应用组织好命令队列,以 batch 为单位,通过标准的 write 接口把命令 copy 到内核中的 buffer。

  3. 在内核中维护一个包含每个 batch 起始 offset 和 length 信息的链表,硬件以 batch 为单位执行命令队列。

  4. batch 状态变化流程图所示,以下两种情况,需要启动硬件执行 batch 命令
    • 当用户调用了 write 命令写入当前 batch 信息,并且硬件处于空闲状态

    • 在中断服务判断当前的 batch 不为空,则从列表中 dequeue 一个 batch,送给硬件执行

    ../../images/ge_function_cmdq_41.png
    2. batch 状态变化流程图

多进程支持

支持多进程调用,不同进程添加的 batch,按照先进先出原则运行。

../../images/ge_function_cmdq_31.png
3. 多进程支持

在 lite 中,应用是在线程去调用 GE 的。当某个进程需要等待当前进程任务是否完成的时候,需要调用 SYNC 命令,等待当前进程所有的任务完成即可,如上图所示。 当进程 2 调用 SYNC 命令时,只需要等待 Batch5 完成即可,不用管后边加入的其他进程的 Batch6、Batch7

IOC_GE_SYNC 的实现:

  1. 在 open 中的 file 上下文中创建当前 client 的上下文信息,并添加到 client 链表中,在 client 中有唯一识别 id
    struct aic_ge_client {
        struct list_head list;
        struct list_head buf_list;
        struct mutex buf_lock; /* dma buf list lock */
        int id;
        int batch_num;
    };
  2. 当前应用调用hal_write 接口,写入一个 batch 命令的时候, 当前 batch 中的 client_id 会写入对应的 client 识别 id, 并且对应的 client 上下文中的 batch_num 引用计数会加 1。
  3. 硬件每执行完成一个 batch 产生一次中断,在中断服务程序中查询当前 batch 中的 id,并通过 id 从 client 链表中找到当前 client, 对应的 client 上下文中的 batch_num 引用计数减 1。在中断服务程序中每来一次中断会通知所有进程
    wake_up_all(&data->wait);
  4. 应用通过接口 SYNC 等待任务完成,只需要等待当前 client 中的 batch_num 为 0 即可。
    static int ge_client_sync(struct aic_ge_data *data,
                struct aic_ge_client *client)
        while (client->batch_num) {
            ret = wait_event_interruptible_timeout(data->wait,
                                !client->batch_num,
                                GE_TIMEOUT(4));
            if (ret < 0)
                break;
        }
        return ret;
    }