Commit 7aea4412b677749afa237940aa49f364cb19a2a8
1 parent
3b69e4b9
Convert IDE to directly access guest memory (Avi Kivity)
Instead of copying to a temporary buffer, map guest memory for IDE DMA transactions. Signed-off-by: Avi Kivity <avi@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6398 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
117 additions
and
16 deletions
hw/ide.c
| ... | ... | @@ -422,6 +422,7 @@ typedef struct IDEState { |
| 422 | 422 | int atapi_dma; /* true if dma is requested for the packet cmd */ |
| 423 | 423 | /* ATA DMA state */ |
| 424 | 424 | int io_buffer_size; |
| 425 | + QEMUIOVector iovec; | |
| 425 | 426 | /* PIO transfer handling */ |
| 426 | 427 | int req_nb_sectors; /* number of sectors per interrupt */ |
| 427 | 428 | EndTransferFunc *end_transfer_func; |
| ... | ... | @@ -862,6 +863,66 @@ static void ide_sector_read(IDEState *s) |
| 862 | 863 | } |
| 863 | 864 | } |
| 864 | 865 | |
| 866 | + | |
| 867 | +/* return 0 if buffer completed */ | |
| 868 | +static int dma_buf_prepare(BMDMAState *bm, int is_write) | |
| 869 | +{ | |
| 870 | + IDEState *s = bm->ide_if; | |
| 871 | + struct { | |
| 872 | + uint32_t addr; | |
| 873 | + uint32_t size; | |
| 874 | + } prd; | |
| 875 | + int l, len; | |
| 876 | + void *mem; | |
| 877 | + target_phys_addr_t l1; | |
| 878 | + | |
| 879 | + qemu_iovec_init(&s->iovec, s->nsector / (TARGET_PAGE_SIZE/512) + 1); | |
| 880 | + s->io_buffer_size = 0; | |
| 881 | + for(;;) { | |
| 882 | + if (bm->cur_prd_len == 0) { | |
| 883 | + /* end of table (with a fail safe of one page) */ | |
| 884 | + if (bm->cur_prd_last || | |
| 885 | + (bm->cur_addr - bm->addr) >= 4096) | |
| 886 | + return s->io_buffer_size != 0; | |
| 887 | + cpu_physical_memory_read(bm->cur_addr, (uint8_t *)&prd, 8); | |
| 888 | + bm->cur_addr += 8; | |
| 889 | + prd.addr = le32_to_cpu(prd.addr); | |
| 890 | + prd.size = le32_to_cpu(prd.size); | |
| 891 | + len = prd.size & 0xfffe; | |
| 892 | + if (len == 0) | |
| 893 | + len = 0x10000; | |
| 894 | + bm->cur_prd_len = len; | |
| 895 | + bm->cur_prd_addr = prd.addr; | |
| 896 | + bm->cur_prd_last = (prd.size & 0x80000000); | |
| 897 | + } | |
| 898 | + l = bm->cur_prd_len; | |
| 899 | + if (l > 0) { | |
| 900 | + l1 = l; | |
| 901 | + mem = cpu_physical_memory_map(bm->cur_prd_addr, &l1, is_write); | |
| 902 | + if (!mem) { | |
| 903 | + break; | |
| 904 | + } | |
| 905 | + qemu_iovec_add(&s->iovec, mem, l1); | |
| 906 | + bm->cur_prd_addr += l1; | |
| 907 | + bm->cur_prd_len -= l1; | |
| 908 | + s->io_buffer_size += l1; | |
| 909 | + } | |
| 910 | + } | |
| 911 | + return 1; | |
| 912 | +} | |
| 913 | + | |
| 914 | +static void dma_buf_commit(IDEState *s, int is_write) | |
| 915 | +{ | |
| 916 | + int i; | |
| 917 | + | |
| 918 | + for (i = 0; i < s->iovec.niov; ++i) { | |
| 919 | + cpu_physical_memory_unmap(s->iovec.iov[i].iov_base, | |
| 920 | + s->iovec.iov[i].iov_len, is_write, | |
| 921 | + s->iovec.iov[i].iov_len); | |
| 922 | + } | |
| 923 | + qemu_iovec_destroy(&s->iovec); | |
| 924 | +} | |
| 925 | + | |
| 865 | 926 | static void ide_dma_error(IDEState *s) |
| 866 | 927 | { |
| 867 | 928 | ide_transfer_stop(s); |
| ... | ... | @@ -883,10 +944,12 @@ static int ide_handle_write_error(IDEState *s, int error, int op) |
| 883 | 944 | s->bmdma->status |= op; |
| 884 | 945 | vm_stop(0); |
| 885 | 946 | } else { |
| 886 | - if (op == BM_STATUS_DMA_RETRY) | |
| 947 | + if (op == BM_STATUS_DMA_RETRY) { | |
| 948 | + dma_buf_commit(s, 0); | |
| 887 | 949 | ide_dma_error(s); |
| 888 | - else | |
| 950 | + } else { | |
| 889 | 951 | ide_rw_error(s); |
| 952 | + } | |
| 890 | 953 | } |
| 891 | 954 | |
| 892 | 955 | return 1; |
| ... | ... | @@ -940,6 +1003,39 @@ static int dma_buf_rw(BMDMAState *bm, int is_write) |
| 940 | 1003 | return 1; |
| 941 | 1004 | } |
| 942 | 1005 | |
| 1006 | +typedef struct { | |
| 1007 | + BMDMAState *bm; | |
| 1008 | + void (*cb)(void *opaque, int ret); | |
| 1009 | + QEMUBH *bh; | |
| 1010 | +} MapFailureContinuation; | |
| 1011 | + | |
| 1012 | +static void reschedule_dma(void *opaque) | |
| 1013 | +{ | |
| 1014 | + MapFailureContinuation *cont = opaque; | |
| 1015 | + | |
| 1016 | + cont->cb(cont->bm, 0); | |
| 1017 | + qemu_bh_delete(cont->bh); | |
| 1018 | + qemu_free(cont); | |
| 1019 | +} | |
| 1020 | + | |
| 1021 | +static void continue_after_map_failure(void *opaque) | |
| 1022 | +{ | |
| 1023 | + MapFailureContinuation *cont = opaque; | |
| 1024 | + | |
| 1025 | + cont->bh = qemu_bh_new(reschedule_dma, opaque); | |
| 1026 | + qemu_bh_schedule(cont->bh); | |
| 1027 | +} | |
| 1028 | + | |
| 1029 | +static void wait_for_bounce_buffer(BMDMAState *bmdma, | |
| 1030 | + void (*cb)(void *opaque, int ret)) | |
| 1031 | +{ | |
| 1032 | + MapFailureContinuation *cont = qemu_malloc(sizeof(*cont)); | |
| 1033 | + | |
| 1034 | + cont->bm = bmdma; | |
| 1035 | + cont->cb = cb; | |
| 1036 | + cpu_register_map_client(cont, continue_after_map_failure); | |
| 1037 | +} | |
| 1038 | + | |
| 943 | 1039 | static void ide_read_dma_cb(void *opaque, int ret) |
| 944 | 1040 | { |
| 945 | 1041 | BMDMAState *bm = opaque; |
| ... | ... | @@ -948,6 +1044,7 @@ static void ide_read_dma_cb(void *opaque, int ret) |
| 948 | 1044 | int64_t sector_num; |
| 949 | 1045 | |
| 950 | 1046 | if (ret < 0) { |
| 1047 | + dma_buf_commit(s, 1); | |
| 951 | 1048 | ide_dma_error(s); |
| 952 | 1049 | return; |
| 953 | 1050 | } |
| ... | ... | @@ -955,11 +1052,10 @@ static void ide_read_dma_cb(void *opaque, int ret) |
| 955 | 1052 | n = s->io_buffer_size >> 9; |
| 956 | 1053 | sector_num = ide_get_sector(s); |
| 957 | 1054 | if (n > 0) { |
| 1055 | + dma_buf_commit(s, 1); | |
| 958 | 1056 | sector_num += n; |
| 959 | 1057 | ide_set_sector(s, sector_num); |
| 960 | 1058 | s->nsector -= n; |
| 961 | - if (dma_buf_rw(bm, 1) == 0) | |
| 962 | - goto eot; | |
| 963 | 1059 | } |
| 964 | 1060 | |
| 965 | 1061 | /* end of transfer ? */ |
| ... | ... | @@ -977,15 +1073,19 @@ static void ide_read_dma_cb(void *opaque, int ret) |
| 977 | 1073 | |
| 978 | 1074 | /* launch next transfer */ |
| 979 | 1075 | n = s->nsector; |
| 980 | - if (n > IDE_DMA_BUF_SECTORS) | |
| 981 | - n = IDE_DMA_BUF_SECTORS; | |
| 982 | 1076 | s->io_buffer_index = 0; |
| 983 | 1077 | s->io_buffer_size = n * 512; |
| 1078 | + if (dma_buf_prepare(bm, 1) == 0) | |
| 1079 | + goto eot; | |
| 1080 | + if (!s->iovec.niov) { | |
| 1081 | + wait_for_bounce_buffer(bm, ide_read_dma_cb); | |
| 1082 | + return; | |
| 1083 | + } | |
| 984 | 1084 | #ifdef DEBUG_AIO |
| 985 | 1085 | printf("aio_read: sector_num=%" PRId64 " n=%d\n", sector_num, n); |
| 986 | 1086 | #endif |
| 987 | - bm->aiocb = bdrv_aio_read(s->bs, sector_num, s->io_buffer, n, | |
| 988 | - ide_read_dma_cb, bm); | |
| 1087 | + bm->aiocb = bdrv_aio_readv(s->bs, sector_num, &s->iovec, n, | |
| 1088 | + ide_read_dma_cb, bm); | |
| 989 | 1089 | ide_dma_submit_check(s, ide_read_dma_cb, bm); |
| 990 | 1090 | } |
| 991 | 1091 | |
| ... | ... | @@ -1081,6 +1181,7 @@ static void ide_write_dma_cb(void *opaque, int ret) |
| 1081 | 1181 | n = s->io_buffer_size >> 9; |
| 1082 | 1182 | sector_num = ide_get_sector(s); |
| 1083 | 1183 | if (n > 0) { |
| 1184 | + dma_buf_commit(s, 0); | |
| 1084 | 1185 | sector_num += n; |
| 1085 | 1186 | ide_set_sector(s, sector_num); |
| 1086 | 1187 | s->nsector -= n; |
| ... | ... | @@ -1099,20 +1200,20 @@ static void ide_write_dma_cb(void *opaque, int ret) |
| 1099 | 1200 | return; |
| 1100 | 1201 | } |
| 1101 | 1202 | |
| 1102 | - /* launch next transfer */ | |
| 1103 | 1203 | n = s->nsector; |
| 1104 | - if (n > IDE_DMA_BUF_SECTORS) | |
| 1105 | - n = IDE_DMA_BUF_SECTORS; | |
| 1106 | - s->io_buffer_index = 0; | |
| 1107 | 1204 | s->io_buffer_size = n * 512; |
| 1108 | - | |
| 1109 | - if (dma_buf_rw(bm, 0) == 0) | |
| 1205 | + /* launch next transfer */ | |
| 1206 | + if (dma_buf_prepare(bm, 0) == 0) | |
| 1110 | 1207 | goto eot; |
| 1208 | + if (!s->iovec.niov) { | |
| 1209 | + wait_for_bounce_buffer(bm, ide_write_dma_cb); | |
| 1210 | + return; | |
| 1211 | + } | |
| 1111 | 1212 | #ifdef DEBUG_AIO |
| 1112 | 1213 | printf("aio_write: sector_num=%" PRId64 " n=%d\n", sector_num, n); |
| 1113 | 1214 | #endif |
| 1114 | - bm->aiocb = bdrv_aio_write(s->bs, sector_num, s->io_buffer, n, | |
| 1115 | - ide_write_dma_cb, bm); | |
| 1215 | + bm->aiocb = bdrv_aio_writev(s->bs, sector_num, &s->iovec, n, | |
| 1216 | + ide_write_dma_cb, bm); | |
| 1116 | 1217 | ide_dma_submit_check(s, ide_write_dma_cb, bm); |
| 1117 | 1218 | } |
| 1118 | 1219 | ... | ... |