master
c 223 lines 7.46 KB
Raw
1 /*
2 * QEMU ram block attributes
3 *
4 * Copyright Intel
5 *
6 * Author:
7 * Chenyi Qiang <chenyi.qiang@intel.com>
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
12 #include "qemu/osdep.h"
13 #include "qemu/error-report.h"
14 #include "system/ramblock.h"
15 #include "trace.h"
16
17 OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RamBlockAttributes,
18 ram_block_attributes,
19 RAM_BLOCK_ATTRIBUTES,
20 OBJECT,
21 { TYPE_RAM_DISCARD_SOURCE },
22 { })
23
24 static size_t
25 ram_block_attributes_get_block_size(void)
26 {
27 /*
28 * Because page conversion could be manipulated in the size of at least 4K
29 * or 4K aligned, Use the host page size as the granularity to track the
30 * memory attribute.
31 */
32 return qemu_real_host_page_size();
33 }
34
35 /* RamDiscardSource interface implementation */
36 static uint64_t
37 ram_block_attributes_rds_get_min_granularity(const RamDiscardSource *rds,
38 const MemoryRegion *mr)
39 {
40 const RamBlockAttributes *attr = RAM_BLOCK_ATTRIBUTES(rds);
41
42 g_assert(mr == attr->ram_block->mr);
43 return ram_block_attributes_get_block_size();
44 }
45
46 static bool
47 ram_block_attributes_rds_is_populated(const RamDiscardSource *rds,
48 const MemoryRegionSection *section)
49 {
50 const RamBlockAttributes *attr = RAM_BLOCK_ATTRIBUTES(rds);
51 const size_t block_size = ram_block_attributes_get_block_size();
52 const uint64_t first_bit = section->offset_within_region / block_size;
53 const uint64_t last_bit =
54 first_bit + int128_get64(section->size) / block_size - 1;
55 unsigned long first_discarded_bit;
56
57 first_discarded_bit = find_next_zero_bit(attr->bitmap, last_bit + 1,
58 first_bit);
59 return first_discarded_bit > last_bit;
60 }
61
62 static bool
63 ram_block_attributes_is_valid_range(RamBlockAttributes *attr, uint64_t offset,
64 uint64_t size)
65 {
66 MemoryRegion *mr = attr->ram_block->mr;
67
68 g_assert(mr);
69
70 uint64_t region_size = memory_region_size(mr);
71 const size_t block_size = ram_block_attributes_get_block_size();
72
73 if (!QEMU_IS_ALIGNED(offset, block_size) ||
74 !QEMU_IS_ALIGNED(size, block_size)) {
75 return false;
76 }
77 if (offset + size <= offset) {
78 return false;
79 }
80 if (offset + size > region_size) {
81 return false;
82 }
83 return true;
84 }
85
86 static void
87 ram_block_attributes_notify_discard(RamBlockAttributes *attr,
88 uint64_t offset,
89 uint64_t size)
90 {
91 RamDiscardManager *rdm = memory_region_get_ram_discard_manager(attr->ram_block->mr);
92
93 ram_discard_manager_notify_discard(rdm, RAM_DISCARD_SOURCE(attr),
94 offset, size);
95 }
96
97 static int
98 ram_block_attributes_notify_populate(RamBlockAttributes *attr,
99 uint64_t offset, uint64_t size)
100 {
101 RamDiscardManager *rdm = memory_region_get_ram_discard_manager(attr->ram_block->mr);
102
103 return ram_discard_manager_notify_populate(rdm, RAM_DISCARD_SOURCE(attr),
104 offset, size);
105 }
106
107 int ram_block_attributes_state_change(RamBlockAttributes *attr,
108 uint64_t offset, uint64_t size,
109 bool to_discard)
110 {
111 const size_t block_size = ram_block_attributes_get_block_size();
112 const unsigned long first_bit = offset / block_size;
113 const unsigned long nbits = size / block_size;
114 const unsigned long last_bit = first_bit + nbits - 1;
115 const bool is_discarded = find_next_bit(attr->bitmap, attr->bitmap_size,
116 first_bit) > last_bit;
117 const bool is_populated = find_next_zero_bit(attr->bitmap,
118 attr->bitmap_size, first_bit) > last_bit;
119 unsigned long bit;
120 int ret = 0;
121
122 if (!ram_block_attributes_is_valid_range(attr, offset, size)) {
123 error_report("%s, invalid range: offset 0x%" PRIx64 ", size "
124 "0x%" PRIx64, __func__, offset, size);
125 return -EINVAL;
126 }
127
128 trace_ram_block_attributes_state_change(offset, size,
129 is_discarded ? "discarded" :
130 is_populated ? "populated" :
131 "mixture",
132 to_discard ? "discarded" :
133 "populated");
134 if (to_discard) {
135 if (is_discarded) {
136 /* Already private */
137 } else if (is_populated) {
138 /* Completely shared */
139 bitmap_clear(attr->bitmap, first_bit, nbits);
140 ram_block_attributes_notify_discard(attr, offset, size);
141 } else {
142 /* Unexpected mixture: process individual blocks */
143 for (bit = first_bit; bit < first_bit + nbits; bit++) {
144 if (!test_bit(bit, attr->bitmap)) {
145 continue;
146 }
147 clear_bit(bit, attr->bitmap);
148 ram_block_attributes_notify_discard(attr, bit * block_size,
149 block_size);
150 }
151 }
152 } else {
153 if (is_populated) {
154 /* Already shared */
155 } else if (is_discarded) {
156 /* Completely private */
157 bitmap_set(attr->bitmap, first_bit, nbits);
158 ret = ram_block_attributes_notify_populate(attr, offset, size);
159 } else {
160 /* Unexpected mixture: process individual blocks */
161 for (bit = first_bit; bit < first_bit + nbits; bit++) {
162 if (test_bit(bit, attr->bitmap)) {
163 continue;
164 }
165 set_bit(bit, attr->bitmap);
166 ret = ram_block_attributes_notify_populate(attr,
167 bit * block_size,
168 block_size);
169 if (ret) {
170 break;
171 }
172 }
173 }
174 }
175
176 return ret;
177 }
178
179 RamBlockAttributes *ram_block_attributes_create(RAMBlock *ram_block)
180 {
181 const int block_size = ram_block_attributes_get_block_size();
182 RamBlockAttributes *attr;
183 MemoryRegion *mr = ram_block->mr;
184
185 attr = RAM_BLOCK_ATTRIBUTES(object_new(TYPE_RAM_BLOCK_ATTRIBUTES));
186
187 attr->ram_block = ram_block;
188
189 if (memory_region_add_ram_discard_source(mr, RAM_DISCARD_SOURCE(attr))) {
190 object_unref(OBJECT(attr));
191 return NULL;
192 }
193 attr->bitmap_size = DIV_ROUND_UP(int128_get64(mr->size), block_size);
194 attr->bitmap = bitmap_new(attr->bitmap_size);
195
196 return attr;
197 }
198
199 void ram_block_attributes_destroy(RamBlockAttributes *attr)
200 {
201 g_assert(attr);
202
203 g_free(attr->bitmap);
204 memory_region_del_ram_discard_source(attr->ram_block->mr, RAM_DISCARD_SOURCE(attr));
205 object_unref(OBJECT(attr));
206 }
207
208 static void ram_block_attributes_init(Object *obj)
209 {
210 }
211
212 static void ram_block_attributes_finalize(Object *obj)
213 {
214 }
215
216 static void ram_block_attributes_class_init(ObjectClass *klass,
217 const void *data)
218 {
219 RamDiscardSourceClass *rdsc = RAM_DISCARD_SOURCE_CLASS(klass);
220
221 rdsc->get_min_granularity = ram_block_attributes_rds_get_min_granularity;
222 rdsc->is_populated = ram_block_attributes_rds_is_populated;
223 }