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 | ... | ... |