Home | History | Annotate | Download | only in qemu
      1 /*
      2  * DMA helper functions
      3  *
      4  * Copyright (c) 2009 Red Hat
      5  *
      6  * This work is licensed under the terms of the GNU General Public License
      7  * (GNU GPL), version 2 or later.
      8  */
      9 
     10 #include "dma.h"
     11 #include "block_int.h"
     12 
     13 void qemu_sglist_init(QEMUSGList *qsg, int alloc_hint)
     14 {
     15     qsg->sg = qemu_malloc(alloc_hint * sizeof(ScatterGatherEntry));
     16     qsg->nsg = 0;
     17     qsg->nalloc = alloc_hint;
     18     qsg->size = 0;
     19 }
     20 
     21 void qemu_sglist_add(QEMUSGList *qsg, target_phys_addr_t base,
     22                      target_phys_addr_t len)
     23 {
     24     if (qsg->nsg == qsg->nalloc) {
     25         qsg->nalloc = 2 * qsg->nalloc + 1;
     26         qsg->sg = qemu_realloc(qsg->sg, qsg->nalloc * sizeof(ScatterGatherEntry));
     27     }
     28     qsg->sg[qsg->nsg].base = base;
     29     qsg->sg[qsg->nsg].len = len;
     30     qsg->size += len;
     31     ++qsg->nsg;
     32 }
     33 
     34 void qemu_sglist_destroy(QEMUSGList *qsg)
     35 {
     36     qemu_free(qsg->sg);
     37 }
     38 
     39 typedef struct {
     40     BlockDriverAIOCB common;
     41     BlockDriverState *bs;
     42     BlockDriverAIOCB *acb;
     43     QEMUSGList *sg;
     44     uint64_t sector_num;
     45     int is_write;
     46     int sg_cur_index;
     47     target_phys_addr_t sg_cur_byte;
     48     QEMUIOVector iov;
     49     QEMUBH *bh;
     50 } DMAAIOCB;
     51 
     52 static void dma_bdrv_cb(void *opaque, int ret);
     53 
     54 static void reschedule_dma(void *opaque)
     55 {
     56     DMAAIOCB *dbs = (DMAAIOCB *)opaque;
     57 
     58     qemu_bh_delete(dbs->bh);
     59     dbs->bh = NULL;
     60     dma_bdrv_cb(opaque, 0);
     61 }
     62 
     63 static void continue_after_map_failure(void *opaque)
     64 {
     65     DMAAIOCB *dbs = (DMAAIOCB *)opaque;
     66 
     67     dbs->bh = qemu_bh_new(reschedule_dma, dbs);
     68     qemu_bh_schedule(dbs->bh);
     69 }
     70 
     71 static void dma_bdrv_unmap(DMAAIOCB *dbs)
     72 {
     73     int i;
     74 
     75     for (i = 0; i < dbs->iov.niov; ++i) {
     76         cpu_physical_memory_unmap(dbs->iov.iov[i].iov_base,
     77                                   dbs->iov.iov[i].iov_len, !dbs->is_write,
     78                                   dbs->iov.iov[i].iov_len);
     79     }
     80 }
     81 
     82 static void dma_bdrv_cb(void *opaque, int ret)
     83 {
     84     DMAAIOCB *dbs = (DMAAIOCB *)opaque;
     85     target_phys_addr_t cur_addr, cur_len;
     86     void *mem;
     87 
     88     dbs->acb = NULL;
     89     dbs->sector_num += dbs->iov.size / 512;
     90     dma_bdrv_unmap(dbs);
     91     qemu_iovec_reset(&dbs->iov);
     92 
     93     if (dbs->sg_cur_index == dbs->sg->nsg || ret < 0) {
     94         dbs->common.cb(dbs->common.opaque, ret);
     95         qemu_iovec_destroy(&dbs->iov);
     96         qemu_aio_release(dbs);
     97         return;
     98     }
     99 
    100     while (dbs->sg_cur_index < dbs->sg->nsg) {
    101         cur_addr = dbs->sg->sg[dbs->sg_cur_index].base + dbs->sg_cur_byte;
    102         cur_len = dbs->sg->sg[dbs->sg_cur_index].len - dbs->sg_cur_byte;
    103         mem = cpu_physical_memory_map(cur_addr, &cur_len, !dbs->is_write);
    104         if (!mem)
    105             break;
    106         qemu_iovec_add(&dbs->iov, mem, cur_len);
    107         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 
    133 static 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 
    142 static AIOPool dma_aio_pool = {
    143     .aiocb_size         = sizeof(DMAAIOCB),
    144     .cancel             = dma_aio_cancel,
    145 };
    146 
    147 static 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 
    172 BlockDriverAIOCB *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 
    179 BlockDriverAIOCB *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