xref: /openbmc/qemu/block/qed-table.c (revision 2a53cff4)
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.  */
22 static int qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table)
23 {
24     QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(
25         qiov, table->offsets, s->header.cluster_size * s->header.table_size);
26     int noffsets;
27     int i, ret;
28 
29     trace_qed_read_table(s, offset, table);
30 
31     qemu_co_mutex_unlock(&s->table_lock);
32     ret = bdrv_preadv(s->bs->file, offset, &qiov);
33     qemu_co_mutex_lock(&s->table_lock);
34     if (ret < 0) {
35         goto out;
36     }
37 
38     /* Byteswap offsets */
39     noffsets = qiov.size / sizeof(uint64_t);
40     for (i = 0; i < noffsets; i++) {
41         table->offsets[i] = le64_to_cpu(table->offsets[i]);
42     }
43 
44     ret = 0;
45 out:
46     /* Completion */
47     trace_qed_read_table_cb(s, table, ret);
48     return ret;
49 }
50 
51 /**
52  * Write out an updated part or all of a table
53  *
54  * @s:          QED state
55  * @offset:     Offset of table in image file, in bytes
56  * @table:      Table
57  * @index:      Index of first element
58  * @n:          Number of elements
59  * @flush:      Whether or not to sync to disk
60  *
61  * Called with table_lock held.
62  */
63 static int qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
64                            unsigned int index, unsigned int n, bool flush)
65 {
66     unsigned int sector_mask = BDRV_SECTOR_SIZE / sizeof(uint64_t) - 1;
67     unsigned int start, end, i;
68     QEDTable *new_table;
69     QEMUIOVector qiov;
70     size_t len_bytes;
71     int ret;
72 
73     trace_qed_write_table(s, offset, table, index, n);
74 
75     /* Calculate indices of the first and one after last elements */
76     start = index & ~sector_mask;
77     end = (index + n + sector_mask) & ~sector_mask;
78 
79     len_bytes = (end - start) * sizeof(uint64_t);
80 
81     new_table = qemu_blockalign(s->bs, len_bytes);
82     qemu_iovec_init_buf(&qiov, new_table->offsets, 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_pwritev(s->bs->file, offset, &qiov);
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;
109 out:
110     qemu_vfree(new_table);
111     return ret;
112 }
113 
114 int 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.  */
120 int qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n)
121 {
122     BLKDBG_EVENT(s->bs->file, BLKDBG_L1_UPDATE);
123     return qed_write_table(s, s->header.l1_table_offset,
124                            s->l1_table, index, n, false);
125 }
126 
127 int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index,
128                             unsigned int n)
129 {
130     return qed_write_l1_table(s, index, n);
131 }
132 
133 /* Called with table_lock held.  */
134 int qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset)
135 {
136     int ret;
137 
138     qed_unref_l2_cache_entry(request->l2_table);
139 
140     /* Check for cached L2 entry */
141     request->l2_table = qed_find_l2_cache_entry(&s->l2_cache, offset);
142     if (request->l2_table) {
143         return 0;
144     }
145 
146     request->l2_table = qed_alloc_l2_cache_entry(&s->l2_cache);
147     request->l2_table->table = qed_alloc_table(s);
148 
149     BLKDBG_EVENT(s->bs->file, BLKDBG_L2_LOAD);
150     ret = qed_read_table(s, offset, request->l2_table->table);
151 
152     if (ret) {
153         /* can't trust loaded L2 table anymore */
154         qed_unref_l2_cache_entry(request->l2_table);
155         request->l2_table = NULL;
156     } else {
157         request->l2_table->offset = offset;
158 
159         qed_commit_l2_cache_entry(&s->l2_cache, request->l2_table);
160 
161         /* This is guaranteed to succeed because we just committed the entry
162          * to the cache.
163          */
164         request->l2_table = qed_find_l2_cache_entry(&s->l2_cache, offset);
165         assert(request->l2_table != NULL);
166     }
167 
168     return ret;
169 }
170 
171 int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request, uint64_t offset)
172 {
173     return qed_read_l2_table(s, request, offset);
174 }
175 
176 /* Called with table_lock held.  */
177 int qed_write_l2_table(BDRVQEDState *s, QEDRequest *request,
178                        unsigned int index, unsigned int n, bool flush)
179 {
180     BLKDBG_EVENT(s->bs->file, BLKDBG_L2_UPDATE);
181     return qed_write_table(s, request->l2_table->offset,
182                            request->l2_table->table, index, n, flush);
183 }
184 
185 int qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request,
186                             unsigned int index, unsigned int n, bool flush)
187 {
188     return qed_write_l2_table(s, request, index, n, flush);
189 }
190