qed-table.c (5653B)
1/* 2 * QEMU Enhanced Disk Format Table I/O 3 * 4 * Copyright IBM, Corp. 2010 5 * 6 * Authors: 7 * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> 8 * Anthony Liguori <aliguori@us.ibm.com> 9 * 10 * This work is licensed under the terms of the GNU LGPL, version 2 or later. 11 * See the COPYING.LIB file in the top-level directory. 12 * 13 */ 14 15#include "qemu/osdep.h" 16#include "trace.h" 17#include "qemu/sockets.h" /* for EINPROGRESS on Windows */ 18#include "qed.h" 19#include "qemu/bswap.h" 20 21/* Called with table_lock held. */ 22static int coroutine_fn qed_read_table(BDRVQEDState *s, uint64_t offset, 23 QEDTable *table) 24{ 25 unsigned int bytes = s->header.cluster_size * s->header.table_size; 26 27 int noffsets; 28 int i, ret; 29 30 trace_qed_read_table(s, offset, table); 31 32 qemu_co_mutex_unlock(&s->table_lock); 33 ret = bdrv_co_pread(s->bs->file, offset, bytes, table->offsets, 0); 34 qemu_co_mutex_lock(&s->table_lock); 35 if (ret < 0) { 36 goto out; 37 } 38 39 /* Byteswap offsets */ 40 noffsets = bytes / sizeof(uint64_t); 41 for (i = 0; i < noffsets; i++) { 42 table->offsets[i] = le64_to_cpu(table->offsets[i]); 43 } 44 45 ret = 0; 46out: 47 /* Completion */ 48 trace_qed_read_table_cb(s, table, ret); 49 return ret; 50} 51 52/** 53 * Write out an updated part or all of a table 54 * 55 * @s: QED state 56 * @offset: Offset of table in image file, in bytes 57 * @table: Table 58 * @index: Index of first element 59 * @n: Number of elements 60 * @flush: Whether or not to sync to disk 61 * 62 * Called with table_lock held. 63 */ 64static int coroutine_fn qed_write_table(BDRVQEDState *s, uint64_t offset, 65 QEDTable *table, unsigned int index, 66 unsigned int n, bool flush) 67{ 68 unsigned int sector_mask = BDRV_SECTOR_SIZE / sizeof(uint64_t) - 1; 69 unsigned int start, end, i; 70 QEDTable *new_table; 71 size_t len_bytes; 72 int ret; 73 74 trace_qed_write_table(s, offset, table, index, n); 75 76 /* Calculate indices of the first and one after last elements */ 77 start = index & ~sector_mask; 78 end = (index + n + sector_mask) & ~sector_mask; 79 80 len_bytes = (end - start) * sizeof(uint64_t); 81 82 new_table = qemu_blockalign(s->bs, len_bytes); 83 84 /* Byteswap table */ 85 for (i = start; i < end; i++) { 86 uint64_t le_offset = cpu_to_le64(table->offsets[i]); 87 new_table->offsets[i - start] = le_offset; 88 } 89 90 /* Adjust for offset into table */ 91 offset += start * sizeof(uint64_t); 92 93 qemu_co_mutex_unlock(&s->table_lock); 94 ret = bdrv_co_pwrite(s->bs->file, offset, len_bytes, new_table->offsets, 0); 95 qemu_co_mutex_lock(&s->table_lock); 96 trace_qed_write_table_cb(s, table, flush, ret); 97 if (ret < 0) { 98 goto out; 99 } 100 101 if (flush) { 102 ret = bdrv_flush(s->bs); 103 if (ret < 0) { 104 goto out; 105 } 106 } 107 108 ret = 0; 109out: 110 qemu_vfree(new_table); 111 return ret; 112} 113 114int coroutine_fn qed_read_l1_table_sync(BDRVQEDState *s) 115{ 116 return qed_read_table(s, s->header.l1_table_offset, s->l1_table); 117} 118 119/* Called with table_lock held. */ 120int coroutine_fn qed_write_l1_table(BDRVQEDState *s, unsigned int index, 121 unsigned int n) 122{ 123 BLKDBG_EVENT(s->bs->file, BLKDBG_L1_UPDATE); 124 return qed_write_table(s, s->header.l1_table_offset, 125 s->l1_table, index, n, false); 126} 127 128int coroutine_fn qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index, 129 unsigned int n) 130{ 131 return qed_write_l1_table(s, index, n); 132} 133 134/* Called with table_lock held. */ 135int coroutine_fn qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, 136 uint64_t offset) 137{ 138 int ret; 139 140 qed_unref_l2_cache_entry(request->l2_table); 141 142 /* Check for cached L2 entry */ 143 request->l2_table = qed_find_l2_cache_entry(&s->l2_cache, offset); 144 if (request->l2_table) { 145 return 0; 146 } 147 148 request->l2_table = qed_alloc_l2_cache_entry(&s->l2_cache); 149 request->l2_table->table = qed_alloc_table(s); 150 151 BLKDBG_EVENT(s->bs->file, BLKDBG_L2_LOAD); 152 ret = qed_read_table(s, offset, request->l2_table->table); 153 154 if (ret) { 155 /* can't trust loaded L2 table anymore */ 156 qed_unref_l2_cache_entry(request->l2_table); 157 request->l2_table = NULL; 158 } else { 159 request->l2_table->offset = offset; 160 161 qed_commit_l2_cache_entry(&s->l2_cache, request->l2_table); 162 163 /* This is guaranteed to succeed because we just committed the entry 164 * to the cache. 165 */ 166 request->l2_table = qed_find_l2_cache_entry(&s->l2_cache, offset); 167 assert(request->l2_table != NULL); 168 } 169 170 return ret; 171} 172 173int coroutine_fn qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request, 174 uint64_t offset) 175{ 176 return qed_read_l2_table(s, request, offset); 177} 178 179/* Called with table_lock held. */ 180int coroutine_fn qed_write_l2_table(BDRVQEDState *s, QEDRequest *request, 181 unsigned int index, unsigned int n, 182 bool flush) 183{ 184 BLKDBG_EVENT(s->bs->file, BLKDBG_L2_UPDATE); 185 return qed_write_table(s, request->l2_table->offset, 186 request->l2_table->table, index, n, flush); 187} 188 189int coroutine_fn qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request, 190 unsigned int index, unsigned int n, 191 bool flush) 192{ 193 return qed_write_l2_table(s, request, index, n, flush); 194}