Add proper per-file copyright notices/licenses and top-level license.
[bluesky.git] / bluesky / cache.c
index fd4797d..838c049 100644 (file)
@@ -3,7 +3,29 @@
  * Copyright (C) 2009  The Regents of the University of California
  * Written by Michael Vrable <mvrable@cs.ucsd.edu>
  *
- * TODO: Licensing
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
  */
 
 #define _GNU_SOURCE
@@ -328,12 +350,14 @@ void bluesky_cachefile_gc(BlueSkyFS *fs)
          * then we'll just skip the file on this pass. */
         if (g_mutex_trylock(cachefile->lock)) {
             int64_t age = bluesky_get_current_time() - cachefile->atime;
-            g_print("%s addr=%p mapcount=%d refcount=%d size=%d atime_age=%f",
-                    cachefile->filename, cachefile->addr, cachefile->mapcount,
-                    cachefile->refcount, cachefile->disk_used, age / 1e6);
-            if (cachefile->fetching)
-                g_print(" (fetching)");
-            g_print("\n");
+            if (bluesky_verbose) {
+                g_print("%s addr=%p mapcount=%d refcount=%d size=%d atime_age=%f",
+                        cachefile->filename, cachefile->addr, cachefile->mapcount,
+                        cachefile->refcount, cachefile->disk_used, age / 1e6);
+                if (cachefile->fetching)
+                    g_print(" (fetching)");
+                g_print("\n");
+            }
 
             gboolean deletion_candidate = FALSE;
             if (g_atomic_int_get(&fs->log->disk_used)
@@ -353,7 +377,9 @@ void bluesky_cachefile_gc(BlueSkyFS *fs)
             }
 
             if (deletion_candidate) {
-                g_print("   ...deleting\n");
+                if (bluesky_verbose) {
+                    g_print("   ...deleting\n");
+                }
                 if (unlinkat(fs->log->dirfd, cachefile->filename, 0) < 0) {
                     fprintf(stderr, "Unable to unlink journal %s: %m\n",
                             cachefile->filename);
@@ -362,6 +388,8 @@ void bluesky_cachefile_gc(BlueSkyFS *fs)
                 g_atomic_int_add(&fs->log->disk_used, -cachefile->disk_used);
                 g_hash_table_remove(fs->log->mmap_cache, cachefile->filename);
                 bluesky_rangeset_free(cachefile->items);
+                if (cachefile->prefetches != NULL)
+                    bluesky_rangeset_free(cachefile->prefetches);
                 g_mutex_unlock(cachefile->lock);
                 g_mutex_free(cachefile->lock);
                 g_cond_free(cachefile->cond);
@@ -415,9 +443,68 @@ void bluesky_flushd_invoke(BlueSkyFS *fs)
     g_thread_create((GThreadFunc)flushd_task, fs, FALSE, NULL);
 }
 
+/* How urgent is flushing out data?  Returns one of several values:
+ *   0 - memory state is fine
+ *   1 - should launch flushd if not already running
+ *   2 - should delay writers while memory frees up
+ *   3 - should block writers until memory frees up
+ */
+static int compute_pressure(BlueSkyFS *fs)
+{
+    /* LEVEL 3 */
+    /* Too much dirty data in memory? */
+    if (g_atomic_int_get(&fs->cache_dirty)
+                + g_atomic_int_get(&fs->cache_log_dirty)
+           > bluesky_watermark_high_dirty)
+    {
+        g_print("pressure: too much dirty data (3)\n");
+        return 3;
+    }
+
+    /* Too much uncommitted data in the journal on disk, not yet flushed to the
+     * cloud? */
+    /*printf("Dirty journals: %d to %d\n",
+           fs->log->journal_watermark, fs->log->seq_num);*/
+    int dirty_limit;
+    dirty_limit = bluesky_options.cache_size / (LOG_SEGMENT_SIZE / 1024) / 2;
+    int dirty_journals = fs->log->seq_num - fs->log->journal_watermark + 1;
+    if (dirty_journals > 1 && dirty_journals >= dirty_limit) {
+        printf("pressure: too many dirty journals (%d >= %d) (3)\n",
+               dirty_journals, dirty_limit);
+        return 3;
+    }
+
+    /* LEVEL 2 */
+    if (g_atomic_int_get(&fs->cache_dirty) > bluesky_watermark_med2_dirty) {
+        g_print("pressure: too much dirty data (2)\n");
+        return 2;
+    }
+
+    if (dirty_journals > 1 && dirty_journals > dirty_limit * 3 / 4) {
+        printf("pressure: many dirty journals (%d), should start writeback (2)\n",
+               dirty_journals);
+        return 2;
+    }
+
+    /* LEVEL 1 */
+    if (g_atomic_int_get(&fs->cache_dirty) > bluesky_watermark_medium_dirty) {
+        g_print("pressure: too much dirty data (1)\n");
+        return 1;
+    }
+
+    if (dirty_journals > 1 && dirty_journals > dirty_limit / 2) {
+        printf("pressure: many dirty journals (%d), should start writeback (1)\n",
+               dirty_journals);
+        return 1;
+    }
+
+    return 0;
+}
+
 void bluesky_flushd_invoke_conditional(BlueSkyFS *fs)
 {
-    if (g_atomic_int_get(&fs->cache_dirty) < bluesky_watermark_medium_dirty)
+    int pressure = compute_pressure(fs);
+    if (pressure == 0)
         return;
 
     if (bluesky_verbose) {
@@ -430,16 +517,26 @@ void bluesky_flushd_invoke_conditional(BlueSkyFS *fs)
 
     /* If the system is under heavy memory pressure, actually delay execution
      * so the flush daemon can catch up. */
-    while (g_atomic_int_get(&fs->cache_dirty)
-                + g_atomic_int_get(&fs->cache_log_dirty)
-           > bluesky_watermark_high_dirty) {
+    while (pressure > 1) {
         g_log("bluesky/flushd", G_LOG_LEVEL_DEBUG,
               "Waiting due to memory pressure, dirty=%d + %d",
               g_atomic_int_get(&fs->cache_dirty),
               g_atomic_int_get(&fs->cache_log_dirty));
         g_mutex_lock(fs->lock);
-        g_cond_wait(fs->flushd_cond, fs->lock);
+        pressure = compute_pressure(fs);
+        if (pressure > 2) {
+            g_cond_wait(fs->flushd_cond, fs->lock);
+        } else if (pressure > 1) {
+            /* Wait for up to 10 seconds. */
+            GTimeVal timeout;
+            g_get_current_time(&timeout);
+            g_time_val_add(&timeout, 10 * 1000 * 1000);
+            g_cond_timed_wait(fs->flushd_cond, fs->lock, &timeout);
+        }
         g_mutex_unlock(fs->lock);
+        pressure = compute_pressure(fs);
+        if (pressure == 1)
+            break;  /* Do not loop indefinitely for a pressure of 1 */
     }
 }