Vengineerの妄想(準備期間)

人生は短いけど、長いです。人生を楽しみましょう!

AMD XDNA Driver for Linux を覗いてみる (その3)

はじめに

AMD XDNA Driver for Linux を覗いてみる (その3)

今回は、mailbox 関連を見ていきます。

amdxdna_mailbox.c

amdxdna_mailbox.c で、Host と XDAN 間の mailbox を実装しています。

 memcpy(&record->re_x2i, x2i, sizeof(*x2i));
    memcpy(&record->re_i2x, i2x, sizeof(*i2x));

re_x2i と re_i2x で双方向のメッセージ通信をやっていますね。

 INIT_WORK(&mb_chann->rx_work, mailbox_rx_worker);
    mb_chann->work_q = create_singlethread_workqueue(mb->name);

受信側は、mailbox_rx_worker にて行っています。

static void mailbox_rx_worker(struct work_struct *rx_work)
{
    struct mailbox_channel *mb_chann;
    int ret;

    mb_chann = container_of(rx_work, struct mailbox_channel, rx_work);

    while (1) {
        /*
        * If return is 0, keep consuming next message, until there is
        * no messages or an error happened.
        */
        ret = mailbox_get_msg(mb_chann);
        if (ret)
            break;
    }
}

下記のmailbox_get_msgを呼び出しているだけですね。

static inline int mailbox_get_msg(struct mailbox_channel *mb_chann) { struct xdna_msg_header header; u32 ringbuf_size; u32 head, tail; u32 start_addr; u64 read_addr; u32 msg_size; u32 val;

tail = mailbox_get_tailptr(mb_chann, CHAN_RES_I2X);
head = mb_chann->i2x_head;
ringbuf_size = mailbox_get_ringbuf_size(mb_chann, CHAN_RES_I2X);
start_addr = mb_chann->res[CHAN_RES_I2X].rb_start_addr;

/* ringbuf empty */
if ((head & (ringbuf_size - 1)) == (tail & (ringbuf_size - 1)))
    return -ENOENT;

if (head == ringbuf_size)
    head = 0;

/* Peek size of the message or TOMBSTONE */
read_addr = mb_chann->mb->res.ringbuf_base + start_addr + head;
val = ioread32((void *)read_addr);

/* The first word could be total size or TOMBSTONE */
if (val == TOMBSTONE) {
    mailbox_set_headptr(mb_chann, 0);
    return 0;
}

msg_size = val;
memcpy_fromio(&header, (void *)read_addr, sizeof(header));
if (msg_size + sizeof(header) > tail - head) {
    WARN_ONCE(1, "Invalid message size %d, tail %d, head %d\n",
          msg_size, tail, head);
    return -EINVAL;
}

read_addr += sizeof(header);
if (header.id < ASYNC_MSG_START_ID)
    mailbox_get_resp(mb_chann, &header, (u32 *)read_addr);
else
    mailbox_get_async_msg(mb_chann, &header, (u32 *)read_addr);

mailbox_set_headptr(mb_chann, head + sizeof(header) + msg_size);
/* After update head, it can equal to ringbuf_size. This is expected. */
trace_mbox_set_head(mb_chann->mb->name, mb_chann->msix_irq,
            header.opcode, header.id);

return 0;

}

割り込みハンドラ[mailbox_irq_handler](https://github.com/amd/xdna-driver/blob/main/src/driver/amdxdna/amdxdna_mailbox.c#L378-L388)を登録しています。
/* Everything look good. Time to enable irq handler */
ret = request_irq(mb_irq, mailbox_irq_handler, 0, mb->name, mb_chann);
mailbox_riq_hanlder では、色々とやらずに、上記の create_singlethread_workqueue で生成した、mb_chann->work_q をスケジュールしていますね。 work_q (mailbox_rx_worker)の中で行います。

static irqreturn_t mailbox_irq_handler(int irq, void p) { struct mailbox_channel mb_chann = p;

trace_mbox_irq_handle(mb_chann->mb->name, irq);
/* Schedule a rx_work to call the callback functions */
queue_work(mb_chann->work_q, &mb_chann->rx_work);
mailbox_clear_msix_intr(mb_chann);

return IRQ_HANDLED;

}

# おわりに

今回は、mailbox 関連をみてみました。