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