qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

qed-l2-cache.c (6149B)


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