dma-helpers.c revision 34c48ff1e3ad5cd2084ca40188754d45f423750b
15d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner/*
25d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * DMA helper functions
35d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner *
45d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * Copyright (c) 2009 Red Hat
55d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner *
65d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * This work is licensed under the terms of the GNU General Public License
75d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * (GNU GPL), version 2 or later.
85d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner */
95d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1034c48ff1e3ad5cd2084ca40188754d45f423750bDavid 'Digit' Turner#include "sysemu/dma.h"
115d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#include "block_int.h"
125d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
135d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnervoid qemu_sglist_init(QEMUSGList *qsg, int alloc_hint)
145d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
155d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    qsg->sg = qemu_malloc(alloc_hint * sizeof(ScatterGatherEntry));
165d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    qsg->nsg = 0;
175d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    qsg->nalloc = alloc_hint;
185d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    qsg->size = 0;
195d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
205d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
215d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnervoid qemu_sglist_add(QEMUSGList *qsg, target_phys_addr_t base,
225d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                     target_phys_addr_t len)
235d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
245d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (qsg->nsg == qsg->nalloc) {
255d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        qsg->nalloc = 2 * qsg->nalloc + 1;
265d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        qsg->sg = qemu_realloc(qsg->sg, qsg->nalloc * sizeof(ScatterGatherEntry));
275d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    }
285d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    qsg->sg[qsg->nsg].base = base;
295d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    qsg->sg[qsg->nsg].len = len;
305d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    qsg->size += len;
315d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    ++qsg->nsg;
325d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
335d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
345d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnervoid qemu_sglist_destroy(QEMUSGList *qsg)
355d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
365d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    qemu_free(qsg->sg);
375d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
385d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
395d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnertypedef struct {
405d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BlockDriverAIOCB common;
415d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BlockDriverState *bs;
425d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BlockDriverAIOCB *acb;
435d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    QEMUSGList *sg;
445d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint64_t sector_num;
455d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    int is_write;
465d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    int sg_cur_index;
475d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    target_phys_addr_t sg_cur_byte;
485d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    QEMUIOVector iov;
495d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    QEMUBH *bh;
505d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} DMAAIOCB;
515d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
525d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic void dma_bdrv_cb(void *opaque, int ret);
535d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
545d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic void reschedule_dma(void *opaque)
555d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
565d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    DMAAIOCB *dbs = (DMAAIOCB *)opaque;
575d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
585d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    qemu_bh_delete(dbs->bh);
595d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    dbs->bh = NULL;
605d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    dma_bdrv_cb(opaque, 0);
615d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
625d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
635d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic void continue_after_map_failure(void *opaque)
645d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
655d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    DMAAIOCB *dbs = (DMAAIOCB *)opaque;
665d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
675d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    dbs->bh = qemu_bh_new(reschedule_dma, dbs);
685d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    qemu_bh_schedule(dbs->bh);
695d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
705d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
715d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic void dma_bdrv_unmap(DMAAIOCB *dbs)
725d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
735d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    int i;
745d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
755d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    for (i = 0; i < dbs->iov.niov; ++i) {
765d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        cpu_physical_memory_unmap(dbs->iov.iov[i].iov_base,
775d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                                  dbs->iov.iov[i].iov_len, !dbs->is_write,
785d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                                  dbs->iov.iov[i].iov_len);
795d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    }
805d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
815d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
825d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic void dma_bdrv_cb(void *opaque, int ret)
835d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
845d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    DMAAIOCB *dbs = (DMAAIOCB *)opaque;
855d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    target_phys_addr_t cur_addr, cur_len;
865d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    void *mem;
875d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
885d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    dbs->acb = NULL;
895d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    dbs->sector_num += dbs->iov.size / 512;
905d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    dma_bdrv_unmap(dbs);
915d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    qemu_iovec_reset(&dbs->iov);
925d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
935d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (dbs->sg_cur_index == dbs->sg->nsg || ret < 0) {
945d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        dbs->common.cb(dbs->common.opaque, ret);
955d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        qemu_iovec_destroy(&dbs->iov);
965d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        qemu_aio_release(dbs);
975d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        return;
985d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    }
995d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1005d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    while (dbs->sg_cur_index < dbs->sg->nsg) {
1015d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        cur_addr = dbs->sg->sg[dbs->sg_cur_index].base + dbs->sg_cur_byte;
1025d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        cur_len = dbs->sg->sg[dbs->sg_cur_index].len - dbs->sg_cur_byte;
1035d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        mem = cpu_physical_memory_map(cur_addr, &cur_len, !dbs->is_write);
1045d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        if (!mem)
1055d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            break;
1065d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        qemu_iovec_add(&dbs->iov, mem, cur_len);
1075d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        dbs->sg_cur_byte += cur_len;
1085d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        if (dbs->sg_cur_byte == dbs->sg->sg[dbs->sg_cur_index].len) {
1095d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            dbs->sg_cur_byte = 0;
1105d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            ++dbs->sg_cur_index;
1115d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        }
1125d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    }
1135d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1145d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (dbs->iov.size == 0) {
1155d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        cpu_register_map_client(dbs, continue_after_map_failure);
1165d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        return;
1175d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    }
1185d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1195d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (dbs->is_write) {
1205d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        dbs->acb = bdrv_aio_writev(dbs->bs, dbs->sector_num, &dbs->iov,
1215d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                                   dbs->iov.size / 512, dma_bdrv_cb, dbs);
1225d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    } else {
1235d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        dbs->acb = bdrv_aio_readv(dbs->bs, dbs->sector_num, &dbs->iov,
1245d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                                  dbs->iov.size / 512, dma_bdrv_cb, dbs);
1255d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    }
1265d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (!dbs->acb) {
1275d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        dma_bdrv_unmap(dbs);
1285d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        qemu_iovec_destroy(&dbs->iov);
1295d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        return;
1305d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    }
1315d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
1325d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1335d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic void dma_aio_cancel(BlockDriverAIOCB *acb)
1345d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
1355d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    DMAAIOCB *dbs = container_of(acb, DMAAIOCB, common);
1365d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1375d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (dbs->acb) {
1385d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        bdrv_aio_cancel(dbs->acb);
1395d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    }
1405d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
1415d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1425d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic AIOPool dma_aio_pool = {
1435d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    .aiocb_size         = sizeof(DMAAIOCB),
1445d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    .cancel             = dma_aio_cancel,
1455d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner};
1465d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1475d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic BlockDriverAIOCB *dma_bdrv_io(
1485d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BlockDriverState *bs, QEMUSGList *sg, uint64_t sector_num,
1495d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BlockDriverCompletionFunc *cb, void *opaque,
1505d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    int is_write)
1515d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
1525d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    DMAAIOCB *dbs =  qemu_aio_get(&dma_aio_pool, bs, cb, opaque);
1535d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1545d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    dbs->acb = NULL;
1555d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    dbs->bs = bs;
1565d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    dbs->sg = sg;
1575d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    dbs->sector_num = sector_num;
1585d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    dbs->sg_cur_index = 0;
1595d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    dbs->sg_cur_byte = 0;
1605d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    dbs->is_write = is_write;
1615d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    dbs->bh = NULL;
1625d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    qemu_iovec_init(&dbs->iov, sg->nsg);
1635d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    dma_bdrv_cb(dbs, 0);
1645d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (!dbs->acb) {
1655d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        qemu_aio_release(dbs);
1665d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        return NULL;
1675d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    }
1685d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    return &dbs->common;
1695d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
1705d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1715d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1725d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' TurnerBlockDriverAIOCB *dma_bdrv_read(BlockDriverState *bs,
1735d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                                QEMUSGList *sg, uint64_t sector,
1745d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                                void (*cb)(void *opaque, int ret), void *opaque)
1755d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
1765d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    return dma_bdrv_io(bs, sg, sector, cb, opaque, 0);
1775d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
1785d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1795d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' TurnerBlockDriverAIOCB *dma_bdrv_write(BlockDriverState *bs,
1805d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                                 QEMUSGList *sg, uint64_t sector,
1815d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                                 void (*cb)(void *opaque, int ret), void *opaque)
1825d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
1835d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    return dma_bdrv_io(bs, sg, sector, cb, opaque, 1);
1845d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
185