1. /*
  2. The cache needs to be able to provide the following operating modes:
  3. - read sector(s) to an already allocated buffer
  4.   NOTE: we can provide an hint to cache whether the sectors should be cached or not if it's not already in the cache
  5. - write sectors(s) from an already filled buffer
  6.   NOTE: same remark
  7. - read a sector and lock a buffer to it
  8. - flag a locked sector as dirty
  9. - clean a locked sector (write it back if needed)
  10. - flag a locked sector as inconsistent (prevent writeback)
  11. - remove an inconsistency flag (should be done ASAP)
  12. - unlock a sector (maybe with a flag if it should be cleaned or not)
  13. - clean everything (and optionally flush the storage layer)
  14. - invalidate everything? (if we respect locks, this might cause deadlocks, if not, it might cause all hell of trouble. yet, we need this, to clean things out after e.g. changing microsd cards. just fail (and block card removal) if there are still open handles?)
  15. */
  16.  
  17. struct sectorcache_entry
  18. {
  19.     bool valid;      /* Contains valid and up-to-date data for the specified sector */
  20.     bool free;       /* Not expected to be needed again, should be reclaimed preferably */
  21.     int locks;       /* Number of users of this buffer */
  22.     bool exclusive;  /* Locked for exclusive access, usually for writing */
  23.     bool dirty;      /* Needs to be written back before it can be reclaimed */
  24.     bool consistent; /* Allowed to be written back in its current state */
  25.     IF_MD2(int drive;)
  26.     unsigned long sector;
  27.     unsigned char* buffer;
  28. };
  29.  
  30. static struct mutex sectorcache_mutex SHAREDBSS_ATTR;
  31. static struct wakeup sectorcache_block SHAREDBSS_ATTR;
  32. static int sectorcache_replace_idx;
  33. static struct sectorcache_entry[SECTORCACHE_SIZE];
  34. static unsigned char sectorcache_buffer[SECTORCACHE_SIZE][SECTOR_SIZE];
  35.  
  36. void sectorcache_init(void)
  37. {
  38.     int i;
  39.     sectorcache_replace_idx = 0;
  40.     memset(sectorcache_entry, 0, sizeof(sectorcache_entry));
  41.     mutex_init(&sectorcache_mutex);
  42.     wakeup_init(&sectorcache_block);
  43.     for (i = 0; i < SECTORCACHE_SIZE; i++)
  44.         sectorcache_entry[i].buffer = sectorcache_buffer[i];
  45. }
  46.  
  47. /* Blocks the current thread until a buffer becomes free, unlocked or consistent,
  48.    up to the specified number of ticks. Use TIMEOUT_BLOCK to block forever.
  49.    Please note that this will release the mutex while blocking! */
  50. static void sectorcache_wait_block(int timeout)
  51. {
  52.     mutex_unlock(&sectorcache_mutex);
  53.     wakeup_wait(&sectorcache_block, timeout);
  54.     mutex_lock(&sectorcache_mutex);
  55. }
  56.  
  57. /* Tries to find a valid buffer for the specified sector.
  58.    Returns its handle, or null if it couldn't find one.
  59.    Please note that this doesn't check whether that buffer is locked.
  60.    The mutex must have been locked by the caller before calling this. */
  61. static struct sectorcache_entry* sectorcache_find_entry(IF_MD2(int drive,) unsigned long sector)
  62. {
  63.     int i;
  64.     for (i = 0; i < SECTORCACHE_SIZE; i++)
  65.         if (sectorcache_entry[i].valid && sectorcache_entry[i].sector == sector
  66.             IF_MD2( && sectorcache_entry[i].drive == drive))
  67.             return &sectorcache_entry[i];
  68.     return null;
  69. }
  70.  
  71. /* Tries to find a reclaimable buffer.
  72.    Returns its handle, or null if it couldn't find one.
  73.    Please note that this doesn't mark the buffer as used and doesn't
  74.    check whether that buffer needs to be written back first.
  75.    The mutex must have been locked by the caller before calling this. */
  76. static struct sectorcache_entry* sectorcache_find_free(void)
  77. {
  78.     int i, idx;
  79.     sectorcache_replace_idx++;
  80.     for (int i = 0; i < SECTORCACHE_SIZE; i++)
  81.     {
  82.         idx = (sectorcache_replace_idx + i) % SECTORCACHE_SIZE;
  83.         if (!sectorcache_entry[idx].valid)
  84.             return &sectorcache_entry[idx];
  85.     }
  86.     for (int i = 0; i < SECTORCACHE_SIZE; i++)
  87.     {
  88.         idx = (sectorcache_replace_idx + i) % SECTORCACHE_SIZE;
  89.         if (sectorcache_entry[idx].free)
  90.             return &sectorcache_entry[idx];
  91.     }
  92.     for (int i = 0; i < SECTORCACHE_SIZE; i++)
  93.     {
  94.         idx = (sectorcache_replace_idx + i) % SECTORCACHE_SIZE;
  95.         if (!sectorcache_entry[idx].locks)
  96.             return &sectorcache_entry[idx];
  97.     }
  98.     return null;
  99. }
  100.  
  101. int sectorcache_readthrough(IF_MD2(int drive,) unsigned long start, int count, void* buf, bool keepincache);
  102. int sectorcache_writethrough(IF_MD2(int drive,) unsigned long start, int count, const void* buf, bool keepincache);
  103. int sectorcache_lock(IF_MD2(int drive,) unsigned long sector, struct sectorcache_entry** handle, bool exclusive);
  104. int sectorcache_unlock(struct sectorcache_entry** handle, bool clean, bool keepincache);
  105.  
  106. /* Indicate that the buffer will need to be written back before it can be reclaimed */
  107. int sectorcache_markdirty(struct sectorcache_entry* handle)
  108. {
  109.     mutex_lock(&sectorcache_mutex);
  110.     handle->dirty = true;
  111.     mutex_unlock(&sectorcache_mutex);
  112. }
  113.  
  114. /* Indicate that the buffer may not be written back until it has been
  115.    marked as consistent again. (Prevents syncing during inactivity)
  116.    Release this as soon as possible. */
  117. int sectorcache_markinconsistent(struct sectorcache_entry* handle)
  118. {
  119.     mutex_lock(&sectorcache_mutex);
  120.     handle->consistent = false;
  121.     mutex_unlock(&sectorcache_mutex);
  122. }
  123.  
  124. /* Indicate that the buffer may not be written back again. */
  125. int sectorcache_markconsistent(struct sectorcache_entry* handle)
  126. {
  127.     mutex_lock(&sectorcache_mutex);
  128.     handle->consistent = true;
  129.     mutex_unlock(&sectorcache_mutex);
  130.     wakeup_signal(&sectorcache_block);
  131. }
  132.  
  133. /* Write back the sector. Will mark it as consistent.
  134.    Make sure it is marked as consistent before calling this internally. */
  135. int sectorcache_clean(struct sectorcache_entry* handle);
  136.  
  137. /* Write back everything that's dirty,
  138.    and flush the storage layer if wanted.
  139.    This may block for several seconds! */
  140. int sectorcache_clean_all(bool flushstorage)
  141. {
  142.     int i;
  143.     bool retry = true;
  144.     mutex_lock(&sectorcache_mutex);
  145.     while (retry)
  146.     {
  147.         retry = false;
  148.         for (i = 0; i < SECTORCACHE_SIZE; i++)
  149.             if (sectorcache_entry[i].dirty)
  150.             {
  151.                 if (!sectorcache_entry[i].consistent)
  152.                 {
  153.                     sectorcache_wait_block(TIMEOUT_BLOCK);
  154.                     retry = true;
  155.                 }
  156.                 sectorcache_clean(&sectorcache_entry[i]);
  157.             }
  158.     }
  159. #ifdef HAVE_STORAGE_FLUSH
  160.     if (flushstorage) storage_flush();
  161. #endif
  162.     mutex_unlock(&sectorcache_mutex);
  163. }
  164.  
  165. /* Invalidate the whole cache for the drive, used by hotplug and USB.
  166.    Will panic if there are still handles open for this drive. */
  167. int sectorcache_invalidate(IF_MD2(int drive));