root / block / qed-l2-cache.c @ 73f5e313
History | View | Annotate | Download (5.4 kB)
1 | 298800ca | Stefan Hajnoczi | /*
|
---|---|---|---|
2 | 298800ca | Stefan Hajnoczi | * QEMU Enhanced Disk Format L2 Cache
|
3 | 298800ca | Stefan Hajnoczi | *
|
4 | 298800ca | Stefan Hajnoczi | * Copyright IBM, Corp. 2010
|
5 | 298800ca | Stefan Hajnoczi | *
|
6 | 298800ca | Stefan Hajnoczi | * Authors:
|
7 | 298800ca | Stefan Hajnoczi | * Anthony Liguori <aliguori@us.ibm.com>
|
8 | 298800ca | Stefan Hajnoczi | *
|
9 | 298800ca | Stefan Hajnoczi | * This work is licensed under the terms of the GNU LGPL, version 2 or later.
|
10 | 298800ca | Stefan Hajnoczi | * See the COPYING.LIB file in the top-level directory.
|
11 | 298800ca | Stefan Hajnoczi | *
|
12 | 298800ca | Stefan Hajnoczi | */
|
13 | 298800ca | Stefan Hajnoczi | |
14 | 298800ca | Stefan Hajnoczi | /*
|
15 | 298800ca | Stefan Hajnoczi | * L2 table cache usage is as follows:
|
16 | 298800ca | Stefan Hajnoczi | *
|
17 | 298800ca | Stefan Hajnoczi | * An open image has one L2 table cache that is used to avoid accessing the
|
18 | 298800ca | Stefan Hajnoczi | * image file for recently referenced L2 tables.
|
19 | 298800ca | Stefan Hajnoczi | *
|
20 | 298800ca | Stefan Hajnoczi | * Cluster offset lookup translates the logical offset within the block device
|
21 | 298800ca | Stefan Hajnoczi | * to a cluster offset within the image file. This is done by indexing into
|
22 | 298800ca | Stefan Hajnoczi | * the L1 and L2 tables which store cluster offsets. It is here where the L2
|
23 | 298800ca | Stefan Hajnoczi | * table cache serves up recently referenced L2 tables.
|
24 | 298800ca | Stefan Hajnoczi | *
|
25 | 298800ca | Stefan Hajnoczi | * If there is a cache miss, that L2 table is read from the image file and
|
26 | 298800ca | Stefan Hajnoczi | * committed to the cache. Subsequent accesses to that L2 table will be served
|
27 | 298800ca | Stefan Hajnoczi | * from the cache until the table is evicted from the cache.
|
28 | 298800ca | Stefan Hajnoczi | *
|
29 | 298800ca | Stefan Hajnoczi | * L2 tables are also committed to the cache when new L2 tables are allocated
|
30 | 298800ca | Stefan Hajnoczi | * in the image file. Since the L2 table cache is write-through, the new L2
|
31 | 298800ca | Stefan Hajnoczi | * table is first written out to the image file and then committed to the
|
32 | 298800ca | Stefan Hajnoczi | * cache.
|
33 | 298800ca | Stefan Hajnoczi | *
|
34 | 298800ca | Stefan Hajnoczi | * Multiple I/O requests may be using an L2 table cache entry at any given
|
35 | 298800ca | Stefan Hajnoczi | * time. That means an entry may be in use across several requests and
|
36 | 298800ca | Stefan Hajnoczi | * reference counting is needed to free the entry at the correct time. In
|
37 | 298800ca | Stefan Hajnoczi | * particular, an entry evicted from the cache will only be freed once all
|
38 | 298800ca | Stefan Hajnoczi | * references are dropped.
|
39 | 298800ca | Stefan Hajnoczi | *
|
40 | 298800ca | Stefan Hajnoczi | * An in-flight I/O request will hold a reference to a L2 table cache entry for
|
41 | 298800ca | Stefan Hajnoczi | * the period during which it needs to access the L2 table. This includes
|
42 | 298800ca | Stefan Hajnoczi | * cluster offset lookup, L2 table allocation, and L2 table update when a new
|
43 | 298800ca | Stefan Hajnoczi | * data cluster has been allocated.
|
44 | 298800ca | Stefan Hajnoczi | *
|
45 | 298800ca | Stefan Hajnoczi | * An interesting case occurs when two requests need to access an L2 table that
|
46 | 298800ca | Stefan Hajnoczi | * is not in the cache. Since the operation to read the table from the image
|
47 | 298800ca | Stefan Hajnoczi | * file takes some time to complete, both requests may see a cache miss and
|
48 | 298800ca | Stefan Hajnoczi | * start reading the L2 table from the image file. The first to finish will
|
49 | 298800ca | Stefan Hajnoczi | * commit its L2 table into the cache. When the second tries to commit its
|
50 | 298800ca | Stefan Hajnoczi | * table will be deleted in favor of the existing cache entry.
|
51 | 298800ca | Stefan Hajnoczi | */
|
52 | 298800ca | Stefan Hajnoczi | |
53 | 298800ca | Stefan Hajnoczi | #include "trace.h" |
54 | 298800ca | Stefan Hajnoczi | #include "qed.h" |
55 | 298800ca | Stefan Hajnoczi | |
56 | 298800ca | Stefan Hajnoczi | /* Each L2 holds 2GB so this let's us fully cache a 100GB disk */
|
57 | 298800ca | Stefan Hajnoczi | #define MAX_L2_CACHE_SIZE 50 |
58 | 298800ca | Stefan Hajnoczi | |
59 | 298800ca | Stefan Hajnoczi | /**
|
60 | 298800ca | Stefan Hajnoczi | * Initialize the L2 cache
|
61 | 298800ca | Stefan Hajnoczi | */
|
62 | 298800ca | Stefan Hajnoczi | void qed_init_l2_cache(L2TableCache *l2_cache)
|
63 | 298800ca | Stefan Hajnoczi | { |
64 | 298800ca | Stefan Hajnoczi | QTAILQ_INIT(&l2_cache->entries); |
65 | 298800ca | Stefan Hajnoczi | l2_cache->n_entries = 0;
|
66 | 298800ca | Stefan Hajnoczi | } |
67 | 298800ca | Stefan Hajnoczi | |
68 | 298800ca | Stefan Hajnoczi | /**
|
69 | 298800ca | Stefan Hajnoczi | * Free the L2 cache
|
70 | 298800ca | Stefan Hajnoczi | */
|
71 | 298800ca | Stefan Hajnoczi | void qed_free_l2_cache(L2TableCache *l2_cache)
|
72 | 298800ca | Stefan Hajnoczi | { |
73 | 298800ca | Stefan Hajnoczi | CachedL2Table *entry, *next_entry; |
74 | 298800ca | Stefan Hajnoczi | |
75 | 298800ca | Stefan Hajnoczi | QTAILQ_FOREACH_SAFE(entry, &l2_cache->entries, node, next_entry) { |
76 | 298800ca | Stefan Hajnoczi | qemu_vfree(entry->table); |
77 | 7267c094 | Anthony Liguori | g_free(entry); |
78 | 298800ca | Stefan Hajnoczi | } |
79 | 298800ca | Stefan Hajnoczi | } |
80 | 298800ca | Stefan Hajnoczi | |
81 | 298800ca | Stefan Hajnoczi | /**
|
82 | 298800ca | Stefan Hajnoczi | * Allocate an uninitialized entry from the cache
|
83 | 298800ca | Stefan Hajnoczi | *
|
84 | 298800ca | Stefan Hajnoczi | * The returned entry has a reference count of 1 and is owned by the caller.
|
85 | 298800ca | Stefan Hajnoczi | * The caller must allocate the actual table field for this entry and it must
|
86 | 298800ca | Stefan Hajnoczi | * be freeable using qemu_vfree().
|
87 | 298800ca | Stefan Hajnoczi | */
|
88 | 298800ca | Stefan Hajnoczi | CachedL2Table *qed_alloc_l2_cache_entry(L2TableCache *l2_cache) |
89 | 298800ca | Stefan Hajnoczi | { |
90 | 298800ca | Stefan Hajnoczi | CachedL2Table *entry; |
91 | 298800ca | Stefan Hajnoczi | |
92 | 7267c094 | Anthony Liguori | entry = g_malloc0(sizeof(*entry));
|
93 | 298800ca | Stefan Hajnoczi | entry->ref++; |
94 | 298800ca | Stefan Hajnoczi | |
95 | 298800ca | Stefan Hajnoczi | trace_qed_alloc_l2_cache_entry(l2_cache, entry); |
96 | 298800ca | Stefan Hajnoczi | |
97 | 298800ca | Stefan Hajnoczi | return entry;
|
98 | 298800ca | Stefan Hajnoczi | } |
99 | 298800ca | Stefan Hajnoczi | |
100 | 298800ca | Stefan Hajnoczi | /**
|
101 | 298800ca | Stefan Hajnoczi | * Decrease an entry's reference count and free if necessary when the reference
|
102 | 298800ca | Stefan Hajnoczi | * count drops to zero.
|
103 | 298800ca | Stefan Hajnoczi | */
|
104 | 298800ca | Stefan Hajnoczi | void qed_unref_l2_cache_entry(CachedL2Table *entry)
|
105 | 298800ca | Stefan Hajnoczi | { |
106 | 298800ca | Stefan Hajnoczi | if (!entry) {
|
107 | 298800ca | Stefan Hajnoczi | return;
|
108 | 298800ca | Stefan Hajnoczi | } |
109 | 298800ca | Stefan Hajnoczi | |
110 | 298800ca | Stefan Hajnoczi | entry->ref--; |
111 | 298800ca | Stefan Hajnoczi | trace_qed_unref_l2_cache_entry(entry, entry->ref); |
112 | 298800ca | Stefan Hajnoczi | if (entry->ref == 0) { |
113 | 298800ca | Stefan Hajnoczi | qemu_vfree(entry->table); |
114 | 7267c094 | Anthony Liguori | g_free(entry); |
115 | 298800ca | Stefan Hajnoczi | } |
116 | 298800ca | Stefan Hajnoczi | } |
117 | 298800ca | Stefan Hajnoczi | |
118 | 298800ca | Stefan Hajnoczi | /**
|
119 | 298800ca | Stefan Hajnoczi | * Find an entry in the L2 cache. This may return NULL and it's up to the
|
120 | 298800ca | Stefan Hajnoczi | * caller to satisfy the cache miss.
|
121 | 298800ca | Stefan Hajnoczi | *
|
122 | 298800ca | Stefan Hajnoczi | * For a cached entry, this function increases the reference count and returns
|
123 | 298800ca | Stefan Hajnoczi | * the entry.
|
124 | 298800ca | Stefan Hajnoczi | */
|
125 | 298800ca | Stefan Hajnoczi | CachedL2Table *qed_find_l2_cache_entry(L2TableCache *l2_cache, uint64_t offset) |
126 | 298800ca | Stefan Hajnoczi | { |
127 | 298800ca | Stefan Hajnoczi | CachedL2Table *entry; |
128 | 298800ca | Stefan Hajnoczi | |
129 | 298800ca | Stefan Hajnoczi | QTAILQ_FOREACH(entry, &l2_cache->entries, node) { |
130 | 298800ca | Stefan Hajnoczi | if (entry->offset == offset) {
|
131 | 298800ca | Stefan Hajnoczi | trace_qed_find_l2_cache_entry(l2_cache, entry, offset, entry->ref); |
132 | 298800ca | Stefan Hajnoczi | entry->ref++; |
133 | 298800ca | Stefan Hajnoczi | return entry;
|
134 | 298800ca | Stefan Hajnoczi | } |
135 | 298800ca | Stefan Hajnoczi | } |
136 | 298800ca | Stefan Hajnoczi | return NULL; |
137 | 298800ca | Stefan Hajnoczi | } |
138 | 298800ca | Stefan Hajnoczi | |
139 | 298800ca | Stefan Hajnoczi | /**
|
140 | 298800ca | Stefan Hajnoczi | * Commit an L2 cache entry into the cache. This is meant to be used as part of
|
141 | 298800ca | Stefan Hajnoczi | * the process to satisfy a cache miss. A caller would allocate an entry which
|
142 | 298800ca | Stefan Hajnoczi | * is not actually in the L2 cache and then once the entry was valid and
|
143 | 298800ca | Stefan Hajnoczi | * present on disk, the entry can be committed into the cache.
|
144 | 298800ca | Stefan Hajnoczi | *
|
145 | 298800ca | Stefan Hajnoczi | * Since the cache is write-through, it's important that this function is not
|
146 | 298800ca | Stefan Hajnoczi | * called until the entry is present on disk and the L1 has been updated to
|
147 | 298800ca | Stefan Hajnoczi | * point to the entry.
|
148 | 298800ca | Stefan Hajnoczi | *
|
149 | 298800ca | Stefan Hajnoczi | * N.B. This function steals a reference to the l2_table from the caller so the
|
150 | 298800ca | Stefan Hajnoczi | * caller must obtain a new reference by issuing a call to
|
151 | 298800ca | Stefan Hajnoczi | * qed_find_l2_cache_entry().
|
152 | 298800ca | Stefan Hajnoczi | */
|
153 | 298800ca | Stefan Hajnoczi | void qed_commit_l2_cache_entry(L2TableCache *l2_cache, CachedL2Table *l2_table)
|
154 | 298800ca | Stefan Hajnoczi | { |
155 | 298800ca | Stefan Hajnoczi | CachedL2Table *entry; |
156 | 298800ca | Stefan Hajnoczi | |
157 | 298800ca | Stefan Hajnoczi | entry = qed_find_l2_cache_entry(l2_cache, l2_table->offset); |
158 | 298800ca | Stefan Hajnoczi | if (entry) {
|
159 | 298800ca | Stefan Hajnoczi | qed_unref_l2_cache_entry(entry); |
160 | 298800ca | Stefan Hajnoczi | qed_unref_l2_cache_entry(l2_table); |
161 | 298800ca | Stefan Hajnoczi | return;
|
162 | 298800ca | Stefan Hajnoczi | } |
163 | 298800ca | Stefan Hajnoczi | |
164 | 298800ca | Stefan Hajnoczi | if (l2_cache->n_entries >= MAX_L2_CACHE_SIZE) {
|
165 | 298800ca | Stefan Hajnoczi | entry = QTAILQ_FIRST(&l2_cache->entries); |
166 | 298800ca | Stefan Hajnoczi | QTAILQ_REMOVE(&l2_cache->entries, entry, node); |
167 | 298800ca | Stefan Hajnoczi | l2_cache->n_entries--; |
168 | 298800ca | Stefan Hajnoczi | qed_unref_l2_cache_entry(entry); |
169 | 298800ca | Stefan Hajnoczi | } |
170 | 298800ca | Stefan Hajnoczi | |
171 | 298800ca | Stefan Hajnoczi | l2_cache->n_entries++; |
172 | 298800ca | Stefan Hajnoczi | QTAILQ_INSERT_TAIL(&l2_cache->entries, l2_table, node); |
173 | 298800ca | Stefan Hajnoczi | } |