|
@@ -3095,6 +3095,20 @@ void cpu_flush_icache_range(hwaddr start, hwaddr len)
|
|
NULL, len, FLUSH_CACHE);
|
|
NULL, len, FLUSH_CACHE);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * A magic value stored in the first 8 bytes of the bounce buffer struct. Used
|
|
|
|
+ * to detect illegal pointers passed to address_space_unmap.
|
|
|
|
+ */
|
|
|
|
+#define BOUNCE_BUFFER_MAGIC 0xb4017ceb4ffe12ed
|
|
|
|
+
|
|
|
|
+typedef struct {
|
|
|
|
+ uint64_t magic;
|
|
|
|
+ MemoryRegion *mr;
|
|
|
|
+ hwaddr addr;
|
|
|
|
+ size_t len;
|
|
|
|
+ uint8_t buffer[];
|
|
|
|
+} BounceBuffer;
|
|
|
|
+
|
|
static void
|
|
static void
|
|
address_space_unregister_map_client_do(AddressSpaceMapClient *client)
|
|
address_space_unregister_map_client_do(AddressSpaceMapClient *client)
|
|
{
|
|
{
|
|
@@ -3120,9 +3134,9 @@ void address_space_register_map_client(AddressSpace *as, QEMUBH *bh)
|
|
QEMU_LOCK_GUARD(&as->map_client_list_lock);
|
|
QEMU_LOCK_GUARD(&as->map_client_list_lock);
|
|
client->bh = bh;
|
|
client->bh = bh;
|
|
QLIST_INSERT_HEAD(&as->map_client_list, client, link);
|
|
QLIST_INSERT_HEAD(&as->map_client_list, client, link);
|
|
- /* Write map_client_list before reading in_use. */
|
|
|
|
|
|
+ /* Write map_client_list before reading bounce_buffer_size. */
|
|
smp_mb();
|
|
smp_mb();
|
|
- if (!qatomic_read(&as->bounce.in_use)) {
|
|
|
|
|
|
+ if (qatomic_read(&as->bounce_buffer_size) < as->max_bounce_buffer_size) {
|
|
address_space_notify_map_clients_locked(as);
|
|
address_space_notify_map_clients_locked(as);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -3251,28 +3265,40 @@ void *address_space_map(AddressSpace *as,
|
|
mr = flatview_translate(fv, addr, &xlat, &l, is_write, attrs);
|
|
mr = flatview_translate(fv, addr, &xlat, &l, is_write, attrs);
|
|
|
|
|
|
if (!memory_access_is_direct(mr, is_write)) {
|
|
if (!memory_access_is_direct(mr, is_write)) {
|
|
- if (qatomic_xchg(&as->bounce.in_use, true)) {
|
|
|
|
|
|
+ size_t used = qatomic_read(&as->bounce_buffer_size);
|
|
|
|
+ for (;;) {
|
|
|
|
+ hwaddr alloc = MIN(as->max_bounce_buffer_size - used, l);
|
|
|
|
+ size_t new_size = used + alloc;
|
|
|
|
+ size_t actual =
|
|
|
|
+ qatomic_cmpxchg(&as->bounce_buffer_size, used, new_size);
|
|
|
|
+ if (actual == used) {
|
|
|
|
+ l = alloc;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ used = actual;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (l == 0) {
|
|
*plen = 0;
|
|
*plen = 0;
|
|
return NULL;
|
|
return NULL;
|
|
}
|
|
}
|
|
- /* Avoid unbounded allocations */
|
|
|
|
- l = MIN(l, TARGET_PAGE_SIZE);
|
|
|
|
- as->bounce.buffer = qemu_memalign(TARGET_PAGE_SIZE, l);
|
|
|
|
- as->bounce.addr = addr;
|
|
|
|
- as->bounce.len = l;
|
|
|
|
|
|
|
|
|
|
+ BounceBuffer *bounce = g_malloc0(l + sizeof(BounceBuffer));
|
|
|
|
+ bounce->magic = BOUNCE_BUFFER_MAGIC;
|
|
memory_region_ref(mr);
|
|
memory_region_ref(mr);
|
|
- as->bounce.mr = mr;
|
|
|
|
|
|
+ bounce->mr = mr;
|
|
|
|
+ bounce->addr = addr;
|
|
|
|
+ bounce->len = l;
|
|
|
|
+
|
|
if (!is_write) {
|
|
if (!is_write) {
|
|
flatview_read(fv, addr, MEMTXATTRS_UNSPECIFIED,
|
|
flatview_read(fv, addr, MEMTXATTRS_UNSPECIFIED,
|
|
- as->bounce.buffer, l);
|
|
|
|
|
|
+ bounce->buffer, l);
|
|
}
|
|
}
|
|
|
|
|
|
*plen = l;
|
|
*plen = l;
|
|
- return as->bounce.buffer;
|
|
|
|
|
|
+ return bounce->buffer;
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
memory_region_ref(mr);
|
|
memory_region_ref(mr);
|
|
*plen = flatview_extend_translation(fv, addr, len, mr, xlat,
|
|
*plen = flatview_extend_translation(fv, addr, len, mr, xlat,
|
|
l, is_write, attrs);
|
|
l, is_write, attrs);
|
|
@@ -3287,12 +3313,11 @@ void *address_space_map(AddressSpace *as,
|
|
void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len,
|
|
void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len,
|
|
bool is_write, hwaddr access_len)
|
|
bool is_write, hwaddr access_len)
|
|
{
|
|
{
|
|
- if (buffer != as->bounce.buffer) {
|
|
|
|
- MemoryRegion *mr;
|
|
|
|
- ram_addr_t addr1;
|
|
|
|
|
|
+ MemoryRegion *mr;
|
|
|
|
+ ram_addr_t addr1;
|
|
|
|
|
|
- mr = memory_region_from_host(buffer, &addr1);
|
|
|
|
- assert(mr != NULL);
|
|
|
|
|
|
+ mr = memory_region_from_host(buffer, &addr1);
|
|
|
|
+ if (mr != NULL) {
|
|
if (is_write) {
|
|
if (is_write) {
|
|
invalidate_and_set_dirty(mr, addr1, access_len);
|
|
invalidate_and_set_dirty(mr, addr1, access_len);
|
|
}
|
|
}
|
|
@@ -3302,15 +3327,22 @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len,
|
|
memory_region_unref(mr);
|
|
memory_region_unref(mr);
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ BounceBuffer *bounce = container_of(buffer, BounceBuffer, buffer);
|
|
|
|
+ assert(bounce->magic == BOUNCE_BUFFER_MAGIC);
|
|
|
|
+
|
|
if (is_write) {
|
|
if (is_write) {
|
|
- address_space_write(as, as->bounce.addr, MEMTXATTRS_UNSPECIFIED,
|
|
|
|
- as->bounce.buffer, access_len);
|
|
|
|
- }
|
|
|
|
- qemu_vfree(as->bounce.buffer);
|
|
|
|
- as->bounce.buffer = NULL;
|
|
|
|
- memory_region_unref(as->bounce.mr);
|
|
|
|
- /* Clear in_use before reading map_client_list. */
|
|
|
|
- qatomic_set_mb(&as->bounce.in_use, false);
|
|
|
|
|
|
+ address_space_write(as, bounce->addr, MEMTXATTRS_UNSPECIFIED,
|
|
|
|
+ bounce->buffer, access_len);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ qatomic_sub(&as->bounce_buffer_size, bounce->len);
|
|
|
|
+ bounce->magic = ~BOUNCE_BUFFER_MAGIC;
|
|
|
|
+ memory_region_unref(bounce->mr);
|
|
|
|
+ g_free(bounce);
|
|
|
|
+ /* Write bounce_buffer_size before reading map_client_list. */
|
|
|
|
+ smp_mb();
|
|
address_space_notify_map_clients(as);
|
|
address_space_notify_map_clients(as);
|
|
}
|
|
}
|
|
|
|
|