dma-helpers.c revision bcde1092aca184dbd7860078af020de7d1e4e22f
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/*
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * DMA helper functions
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Copyright (c) 2009 Red Hat
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * This work is licensed under the terms of the GNU General Public License
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * (GNU GPL), version 2 or later.
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sysemu/dma.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "block/block_int.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void qemu_sglist_init(QEMUSGList *qsg, int alloc_hint)
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    qsg->sg = qemu_malloc(alloc_hint * sizeof(ScatterGatherEntry));
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    qsg->nsg = 0;
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    qsg->nalloc = alloc_hint;
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    qsg->size = 0;
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void qemu_sglist_add(QEMUSGList *qsg, hwaddr base,
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     hwaddr len)
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (qsg->nsg == qsg->nalloc) {
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        qsg->nalloc = 2 * qsg->nalloc + 1;
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        qsg->sg = qemu_realloc(qsg->sg, qsg->nalloc * sizeof(ScatterGatherEntry));
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    qsg->sg[qsg->nsg].base = base;
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    qsg->sg[qsg->nsg].len = len;
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    qsg->size += len;
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++qsg->nsg;
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void qemu_sglist_destroy(QEMUSGList *qsg)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    qemu_free(qsg->sg);
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)typedef struct {
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BlockDriverAIOCB common;
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BlockDriverState *bs;
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BlockDriverAIOCB *acb;
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    QEMUSGList *sg;
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    uint64_t sector_num;
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int is_write;
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int sg_cur_index;
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    hwaddr sg_cur_byte;
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    QEMUIOVector iov;
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    QEMUBH *bh;
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} DMAAIOCB;
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void dma_bdrv_cb(void *opaque, int ret);
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void reschedule_dma(void *opaque)
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DMAAIOCB *dbs = (DMAAIOCB *)opaque;
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    qemu_bh_delete(dbs->bh);
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    dbs->bh = NULL;
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    dma_bdrv_cb(opaque, 0);
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void continue_after_map_failure(void *opaque)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DMAAIOCB *dbs = (DMAAIOCB *)opaque;
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    dbs->bh = qemu_bh_new(reschedule_dma, dbs);
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    qemu_bh_schedule(dbs->bh);
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void dma_bdrv_unmap(DMAAIOCB *dbs)
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int i;
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (i = 0; i < dbs->iov.niov; ++i) {
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        cpu_physical_memory_unmap(dbs->iov.iov[i].iov_base,
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  dbs->iov.iov[i].iov_len, !dbs->is_write,
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  dbs->iov.iov[i].iov_len);
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void dma_bdrv_cb(void *opaque, int ret)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DMAAIOCB *dbs = (DMAAIOCB *)opaque;
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    hwaddr cur_addr, cur_len;
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    void *mem;
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    dbs->acb = NULL;
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    dbs->sector_num += dbs->iov.size / 512;
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    dma_bdrv_unmap(dbs);
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    qemu_iovec_reset(&dbs->iov);
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (dbs->sg_cur_index == dbs->sg->nsg || ret < 0) {
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        dbs->common.cb(dbs->common.opaque, ret);
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        qemu_iovec_destroy(&dbs->iov);
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        qemu_aio_release(dbs);
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while (dbs->sg_cur_index < dbs->sg->nsg) {
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        cur_addr = dbs->sg->sg[dbs->sg_cur_index].base + dbs->sg_cur_byte;
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        cur_len = dbs->sg->sg[dbs->sg_cur_index].len - dbs->sg_cur_byte;
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        mem = cpu_physical_memory_map(cur_addr, &cur_len, !dbs->is_write);
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (!mem)
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            break;
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        qemu_iovec_add(&dbs->iov, mem, cur_len);
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        dbs->sg_cur_byte += cur_len;
108        if (dbs->sg_cur_byte == dbs->sg->sg[dbs->sg_cur_index].len) {
109            dbs->sg_cur_byte = 0;
110            ++dbs->sg_cur_index;
111        }
112    }
113
114    if (dbs->iov.size == 0) {
115        cpu_register_map_client(dbs, continue_after_map_failure);
116        return;
117    }
118
119    if (dbs->is_write) {
120        dbs->acb = bdrv_aio_writev(dbs->bs, dbs->sector_num, &dbs->iov,
121                                   dbs->iov.size / 512, dma_bdrv_cb, dbs);
122    } else {
123        dbs->acb = bdrv_aio_readv(dbs->bs, dbs->sector_num, &dbs->iov,
124                                  dbs->iov.size / 512, dma_bdrv_cb, dbs);
125    }
126    if (!dbs->acb) {
127        dma_bdrv_unmap(dbs);
128        qemu_iovec_destroy(&dbs->iov);
129        return;
130    }
131}
132
133static void dma_aio_cancel(BlockDriverAIOCB *acb)
134{
135    DMAAIOCB *dbs = container_of(acb, DMAAIOCB, common);
136
137    if (dbs->acb) {
138        bdrv_aio_cancel(dbs->acb);
139    }
140}
141
142static AIOPool dma_aio_pool = {
143    .aiocb_size         = sizeof(DMAAIOCB),
144    .cancel             = dma_aio_cancel,
145};
146
147static BlockDriverAIOCB *dma_bdrv_io(
148    BlockDriverState *bs, QEMUSGList *sg, uint64_t sector_num,
149    BlockDriverCompletionFunc *cb, void *opaque,
150    int is_write)
151{
152    DMAAIOCB *dbs =  qemu_aio_get(&dma_aio_pool, bs, cb, opaque);
153
154    dbs->acb = NULL;
155    dbs->bs = bs;
156    dbs->sg = sg;
157    dbs->sector_num = sector_num;
158    dbs->sg_cur_index = 0;
159    dbs->sg_cur_byte = 0;
160    dbs->is_write = is_write;
161    dbs->bh = NULL;
162    qemu_iovec_init(&dbs->iov, sg->nsg);
163    dma_bdrv_cb(dbs, 0);
164    if (!dbs->acb) {
165        qemu_aio_release(dbs);
166        return NULL;
167    }
168    return &dbs->common;
169}
170
171
172BlockDriverAIOCB *dma_bdrv_read(BlockDriverState *bs,
173                                QEMUSGList *sg, uint64_t sector,
174                                void (*cb)(void *opaque, int ret), void *opaque)
175{
176    return dma_bdrv_io(bs, sg, sector, cb, opaque, 0);
177}
178
179BlockDriverAIOCB *dma_bdrv_write(BlockDriverState *bs,
180                                 QEMUSGList *sg, uint64_t sector,
181                                 void (*cb)(void *opaque, int ret), void *opaque)
182{
183    return dma_bdrv_io(bs, sg, sector, cb, opaque, 1);
184}
185