From 83fd6b61a6e092a22d4d5e59ed95f05f5e287f11 Mon Sep 17 00:00:00 2001 From: Michael Vrable Date: Wed, 11 Aug 2010 22:16:35 -0700 Subject: [PATCH] Attempt at limiting the rate at which memory is dirtied. --- bluesky/bluesky.h | 3 +++ bluesky/cache.c | 52 +++++++++++++++++++++++++++++++++++++++-------- bluesky/inode.c | 1 + 3 files changed, 47 insertions(+), 9 deletions(-) diff --git a/bluesky/bluesky.h b/bluesky/bluesky.h index 099aad4..820fa12 100644 --- a/bluesky/bluesky.h +++ b/bluesky/bluesky.h @@ -169,6 +169,9 @@ typedef struct { /* Mutex for the flush daemon, to prevent concurrent execution. */ GMutex *flushd_lock; + /* Used to wait for the cache daemon to free up space */ + GCond *flushd_cond; + /* Mapping of object identifiers (blocks, inodes) to physical location (in * the local cache or in the logs in the cloud). */ GHashTable *locations; diff --git a/bluesky/cache.c b/bluesky/cache.c index 001095b..2c1bc92 100644 --- a/bluesky/cache.c +++ b/bluesky/cache.c @@ -14,6 +14,7 @@ #include "bluesky-private.h" #define WRITEBACK_DELAY (20 * 1000000) +#define CACHE_DROP_DELAY (20 * 1000000) /* Filesystem caching and cache coherency. There are actually a couple of * different tasks that are performed here: @@ -43,6 +44,17 @@ static void flushd_dirty_inode(BlueSkyInode *inode) bluesky_inode_start_sync(inode); } +/* Check whether memory usage may have dropped below critical thresholds for + * waking up waiting threads. */ +void flushd_check_wakeup(BlueSkyFS *fs) +{ + int dirty = g_atomic_int_get(&fs->cache_dirty); + dirty += g_atomic_int_get(&fs->cache_log_dirty); + + if (dirty <= bluesky_watermark_high_dirty) + g_cond_broadcast(fs->flushd_cond); +} + /* Try to flush dirty data to disk, either due to memory pressure or due to * timeouts. */ static void flushd_dirty(BlueSkyFS *fs) @@ -81,12 +93,18 @@ static void flushd_dirty(BlueSkyFS *fs) bluesky_inode_unref(inode); g_mutex_lock(fs->lock); + flushd_check_wakeup(fs); } + g_cond_broadcast(fs->flushd_cond); + g_mutex_unlock(fs->lock); } -/* Try to flush dirty data to the cloud. */ +/* Try to flush dirty data to the cloud. + * TODO: Rewrite this to work on cloud log items rather than inodes, so we can + * better track which logs are fully synchronized to the cloud and can be + * garbage collected if needed? */ static void flushd_cloud(BlueSkyFS *fs) { int64_t start_time = bluesky_get_current_time(); @@ -147,7 +165,11 @@ static void drop_caches(BlueSkyInode *inode) bluesky_file_drop_cached(inode); } -/* Drop clean data from the cache if needed due to memory pressure. */ +/* Drop clean data from the cache if needed. Clean data should generally be + * memory-mapped from log file or similar, so the kernel can drop this clean + * data from memory for us and hence memory management isn't too important. + * Mainly, we'll want to drop references to data that hasn't been accessed in a + * while so that it is possible to reclaim log segments on disk. */ static void flushd_clean(BlueSkyFS *fs) { g_mutex_lock(fs->lock); @@ -157,16 +179,15 @@ static void flushd_clean(BlueSkyFS *fs) inode_count = 1; while (inode_count-- > 0) { -#if 0 - if (g_atomic_int_get(&fs->cache_total) < bluesky_watermark_medium_total) - break; -#endif - BlueSkyInode *inode; if (fs->accessed_list.prev == NULL) break; inode = fs->accessed_list.prev->data; + uint64_t elapsed = bluesky_get_current_time() - inode->access_time; + if (elapsed < CACHE_DROP_DELAY) + break; + if (bluesky_verbose) { g_log("bluesky/flushd", G_LOG_LEVEL_DEBUG, "Considering dropping cached data for inode %"PRIu64, @@ -216,8 +237,7 @@ void bluesky_flushd_invoke(BlueSkyFS *fs) void bluesky_flushd_invoke_conditional(BlueSkyFS *fs) { - if (g_atomic_int_get(&fs->cache_dirty) < bluesky_watermark_high_dirty - /*&& g_atomic_int_get(&fs->cache_total) < bluesky_watermark_high_total*/) + if (g_atomic_int_get(&fs->cache_dirty) < bluesky_watermark_medium_dirty) return; if (bluesky_verbose) { @@ -227,6 +247,20 @@ void bluesky_flushd_invoke_conditional(BlueSkyFS *fs) } bluesky_flushd_invoke(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) { + 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); + g_mutex_unlock(fs->lock); + } } /* Start a perpetually-running thread that flushes the cache occasionally. */ diff --git a/bluesky/inode.c b/bluesky/inode.c index 39241ea..2696713 100644 --- a/bluesky/inode.c +++ b/bluesky/inode.c @@ -91,6 +91,7 @@ BlueSkyFS *bluesky_new_fs(gchar *name) fs->next_inum = BLUESKY_ROOT_INUM + 1; fs->store = bluesky_store_new("file"); fs->flushd_lock = g_mutex_new(); + fs->flushd_cond = g_cond_new(); fs->locations = g_hash_table_new(bluesky_cloudlog_hash, bluesky_cloudlog_equal); -- 2.20.1