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" 11e1e03df288d5a44bfbffbd86588395c7cbbc27dfDavid 'Digit' Turner#include "block/block_int.h" 125d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 135d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnervoid qemu_sglist_init(QEMUSGList *qsg, int alloc_hint) 145d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{ 15aa8236dc1b1ea300ab18716db5b8fab42aca3ca7David 'Digit' Turner qsg->sg = g_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 21bcde1092aca184dbd7860078af020de7d1e4e22fDavid 'Digit' Turnervoid qemu_sglist_add(QEMUSGList *qsg, hwaddr base, 22bcde1092aca184dbd7860078af020de7d1e4e22fDavid 'Digit' Turner hwaddr len) 235d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{ 245d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (qsg->nsg == qsg->nalloc) { 255d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner qsg->nalloc = 2 * qsg->nalloc + 1; 26aa8236dc1b1ea300ab18716db5b8fab42aca3ca7David 'Digit' Turner qsg->sg = g_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{ 36aa8236dc1b1ea300ab18716db5b8fab42aca3ca7David 'Digit' Turner g_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; 47bcde1092aca184dbd7860078af020de7d1e4e22fDavid 'Digit' Turner hwaddr 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; 85bcde1092aca184dbd7860078af020de7d1e4e22fDavid 'Digit' Turner hwaddr 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