Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Adds a second block cleaning heuristic. Clean dirty blocks based on

some amount of "inactivity". The default policy is to clean dirty
blocks that were inactive for the last 60 seconds. But this can be
changed with fallow_delay (seconds). Setting fallow_delay to 0 completely
turns off the "clean based on inactivity" policy.
  • Loading branch information...
commit a9cf0f7b4549cac62b76ba2ddf579a053a5d1389 1 parent be44cb5
Mohan Srinivasan authored
View
5 doc/flashcache-doc.txt
@@ -99,6 +99,11 @@ strives to keep the percentage of dirty blocks in each set below the
dirty threshold. When the dirty blocks in a set exceeds the dirty
threshold, the set is eligible for cleaning.
+Dirty blocks are also cleaned based on "idleness" By defalt a
+dirty block not read or written for 60 seconds
+(dev.flashcache.fallow_delay) will be cleaned. To disable idle
+cleaning set that value to 0.
+
DIRTY blocks are selected for cleaning based on the replacement policy
(FIFO vs LRU). Once we have a target set of blocks to clean, we sort
these blocks, search for other contigous dirty blocks in the set
View
8 doc/flashcache-sa-guide.txt
@@ -162,10 +162,14 @@ dev.flashcache.dirty_thresh_pct:
dev.flashcache.do_sync:
Schedule cleaning of all dirty blocks in the cache.
dev.flashcache.stop_sync:
- Stop the sync in progress.
-dev.flashcache.cache_all:
+ Stop the sync in progress.dev.flashcache.cache_all:
Global caching mode to cache everything or cache nothing.
See section on Caching Controls. Defaults to "cache everything".
+dev.flashcache.fallow_delay:
+ In seconds. Clean dirty blocks that have been "idle" (not
+ read or written) for fallow_delay seconds. Default is 60
+ seconds.
+ Setting this to 0 disables idle cleaning completely.
There is little reason to change these :
View
27 src/flashcache.h
@@ -125,9 +125,11 @@ struct cacheblock {
struct cache_set {
u_int32_t set_fifo_next;
u_int32_t set_clean_next;
- u_int32_t clean_inprog;
- u_int32_t nr_dirty;
+ u_int16_t clean_inprog;
+ u_int16_t nr_dirty;
u_int16_t lru_head, lru_tail;
+ u_int16_t dirty_fallow;
+ unsigned long fallow_tstamp;
};
/*
@@ -199,6 +201,7 @@ struct cache_c {
#endif
unsigned long enqueues; /* enqueues on pending queue */
unsigned long cleanings;
+ unsigned long fallow_cleanings;
unsigned long noroom; /* No room in set */
unsigned long md_write_dirty; /* Metadata sector writes dirtying block */
unsigned long md_write_clean; /* Metadata sector writes cleaning block */
@@ -214,12 +217,7 @@ struct cache_c {
unsigned long ssd_reads, ssd_writes;
unsigned long uncached_io_requeue;
- unsigned long clean_set_calls;
- unsigned long clean_set_less_dirty;
- unsigned long clean_set_fails;
unsigned long clean_set_ios;
- unsigned long set_limit_reached;
- unsigned long total_limit_reached;
unsigned long pending_jobs_count;
/* Errors */
@@ -295,7 +293,18 @@ struct pending_job {
#define CACHEREADINPROG 0x0010 /* Read from cache in progress */
#define CACHEWRITEINPROG 0x0020 /* Write to cache in progress */
#define DIRTY 0x0040 /* Dirty, needs writeback to disk */
+/*
+ * Old and Dirty blocks are cleaned with a Clock like algorithm. The leading hand
+ * marks DIRTY_FALLOW_1. 60 seconds (default) later, the trailing hand comes along and
+ * marks DIRTY_FALLOW_2 if DIRTY_FALLOW_1 is already set. If the block was used in the
+ * interim, (DIRTY_FALLOW_1|DIRTY_FALLOW_2) is cleared. Any block that has both
+ * DIRTY_FALLOW_1 and DIRTY_FALLOW_2 marked is considered old and is eligible
+ * for cleaning.
+ */
+#define DIRTY_FALLOW_1 0x0080
+#define DIRTY_FALLOW_2 0x0100
+#define FALLOW_DOCLEAN (DIRTY_FALLOW_1 | DIRTY_FALLOW_2)
#define BLOCK_IO_INPROG (DISKREADINPROG | DISKWRITEINPROG | CACHEREADINPROG | CACHEWRITEINPROG)
/* Cache metadata is read by Flashcache utilities */
@@ -440,6 +449,7 @@ void flashcache_dtr(struct dm_target *ti);
int flashcache_status(struct dm_target *ti, status_type_t type,
char *result, unsigned int maxlen);
+
struct kcached_job *flashcache_alloc_cache_job(void);
void flashcache_free_cache_job(struct kcached_job *job);
struct pending_job *flashcache_alloc_pending_job(struct cache_c *dmc);
@@ -505,6 +515,9 @@ int dm_io_async_bvec(unsigned int num_regions,
void *context);
#endif
+void flashcache_detect_fallow(struct cache_c *dmc, int index);
+void flashcache_clear_fallow(struct cache_c *dmc, int index);
+
#endif /* __KERNEL__ */
#endif
View
36 src/flashcache_conf.c
@@ -78,6 +78,7 @@ int sysctl_pid_expiry_check = 60;
int sysctl_pid_do_expiry = 0;
int sysctl_flashcache_fast_remove = 0;
int sysctl_cache_all = 1;
+int sysctl_fallow_delay = 60;
struct cache_c *cache_list_head = NULL;
struct work_struct _kcached_wq;
@@ -463,6 +464,16 @@ static ctl_table flashcache_table[] = {
.mode = 0644,
.proc_handler = &proc_dointvec,
},
+ {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
+ .ctl_name = CTL_UNNUMBERED,
+#endif
+ .procname = "fallow_delay",
+ .data = &sysctl_fallow_delay,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.ctl_name = 0
@@ -1458,6 +1469,8 @@ flashcache_ctr(struct dm_target *ti, unsigned int argc, char **argv)
dmc->cache_sets[i].set_clean_next = i * dmc->assoc;
dmc->cache_sets[i].nr_dirty = 0;
dmc->cache_sets[i].clean_inprog = 0;
+ dmc->cache_sets[i].dirty_fallow = 0;
+ dmc->cache_sets[i].fallow_tstamp = jiffies;
dmc->cache_sets[i].lru_tail = FLASHCACHE_LRU_NULL;
dmc->cache_sets[i].lru_head = FLASHCACHE_LRU_NULL;
}
@@ -1563,6 +1576,7 @@ flashcache_zero_stats(struct cache_c *dmc)
dmc->rd_invalidates = 0;
dmc->pending_inval = 0;
dmc->enqueues = 0;
+ dmc->fallow_cleanings = 0;
dmc->cleanings = 0;
dmc->noroom = 0;
dmc->md_write_dirty = 0;
@@ -1574,9 +1588,7 @@ flashcache_zero_stats(struct cache_c *dmc)
dmc->checksum_valid = 0;
dmc->checksum_invalid = 0;
#endif
- dmc->clean_set_calls = dmc->clean_set_less_dirty = 0;
- dmc->clean_set_fails = dmc->clean_set_ios = 0;
- dmc->set_limit_reached = dmc->total_limit_reached = 0;
+ dmc->clean_set_ios = 0;
dmc->front_merge = dmc->back_merge = 0;
dmc->pid_drops = dmc->pid_adds = dmc->pid_dels = dmc->expiry = 0;
dmc->uncached_reads = dmc->uncached_writes = 0;
@@ -1619,25 +1631,27 @@ flashcache_dtr(struct dm_target *ti)
DMINFO("stats: reads(%lu), writes(%lu), read hits(%lu), write hits(%lu), " \
"read hit percent(%ld), replacement(%lu), write invalidates(%lu), " \
"read invalidates(%lu), write replacement(%lu), pending enqueues(%lu), " \
- "pending inval(%lu) cleanings(%lu), " \
+ "pending inval(%lu) cleanings(%lu), fallow cleanings(%lu) " \
"checksum invalid(%ld), checksum store(%ld), checksum valid(%ld)" \
"front merge(%ld) back merge(%ld)",
dmc->reads, dmc->writes, dmc->read_hits, dmc->write_hits,
dmc->read_hits*100/dmc->reads,
dmc->replace, dmc->wr_invalidates, dmc->rd_invalidates,
- dmc->wr_replace, dmc->enqueues, dmc->pending_inval, dmc->cleanings,
+ dmc->wr_replace, dmc->enqueues,
+ dmc->pending_inval, dmc->cleanings, dmc->fallow_cleanings,
dmc->checksum_store, dmc->checksum_valid, dmc->checksum_invalid,
dmc->front_merge, dmc->back_merge);
#else
DMINFO("stats: reads(%lu), writes(%lu), read hits(%lu), write hits(%lu), " \
"read hit percent(%ld), replacement(%lu), write invalidates(%lu), " \
"read invalidates(%lu), write replacement(%lu), pending enqueues(%lu), " \
- "pending inval(%lu) cleanings(%lu)" \
+ "pending inval(%lu) cleanings(%lu) fallow cleanings(%lu)" \
"front merge(%ld) back merge(%ld)",
dmc->reads, dmc->writes, dmc->read_hits, dmc->write_hits,
dmc->read_hits*100/dmc->reads,
dmc->replace, dmc->wr_invalidates, dmc->rd_invalidates,
- dmc->wr_replace, dmc->enqueues, dmc->pending_inval, dmc->cleanings,
+ dmc->wr_replace, dmc->enqueues,
+ dmc->pending_inval, dmc->cleanings, dmc->fallow_cleanings,
dmc->front_merge, dmc->back_merge);
#endif
@@ -1709,7 +1723,7 @@ flashcache_status_info(struct cache_c *dmc, status_type_t type,
"\tpending enqueues(%lu), pending inval(%lu)\n" \
"\tmetadata dirties(%lu), metadata cleans(%lu)\n" \
"\tmetadata batch(%lu) metadata ssd writes(%lu)\n" \
- "\tcleanings(%lu), no room(%lu) front merge(%lu) back merge(%lu)\n" \
+ "\tcleanings(%lu) fallow cleanings(%lu) no room(%lu) front merge(%lu) back merge(%lu)\n" \
"\tdisk reads(%lu), disk writes(%lu) ssd reads(%lu) ssd writes(%lu)\n" \
"\tuncached reads(%lu), uncached writes(%lu), uncached IO requeue(%lu)\n" \
"\tpid_adds(%lu), pid_dels(%lu), pid_drops(%lu) pid_expiry(%lu)",
@@ -1721,7 +1735,7 @@ flashcache_status_info(struct cache_c *dmc, status_type_t type,
dmc->enqueues, dmc->pending_inval,
dmc->md_write_dirty, dmc->md_write_clean,
dmc->md_write_batch, dmc->md_ssd_writes,
- dmc->cleanings, dmc->noroom, dmc->front_merge, dmc->back_merge,
+ dmc->cleanings, dmc->fallow_cleanings, dmc->noroom, dmc->front_merge, dmc->back_merge,
dmc->disk_reads, dmc->disk_writes, dmc->ssd_reads, dmc->ssd_writes,
dmc->uncached_reads, dmc->uncached_writes, dmc->uncached_io_requeue,
dmc->pid_adds, dmc->pid_dels, dmc->pid_drops, dmc->expiry);
@@ -1734,7 +1748,7 @@ flashcache_status_info(struct cache_c *dmc, status_type_t type,
"\tpending enqueues(%lu) pending inval(%lu)\n" \
"\tmetadata dirties(%lu) metadata cleans(%lu)\n" \
"\tmetadata batch(%lu) metadata ssd writes(%lu)\n" \
- "\tcleanings(%lu) no room(%lu) front merge(%lu) back merge(%lu)\n" \
+ "\tcleanings(%lu) fallow cleanings(%lu) no room(%lu) front merge(%lu) back merge(%lu)\n" \
"\tdisk reads(%lu) disk writes(%lu) ssd reads(%lu) ssd writes(%lu)\n" \
"\tuncached reads(%lu) uncached writes(%lu), uncached IO requeue(%lu)\n" \
"\tpid_adds(%lu) pid_dels(%lu) pid_drops(%lu) pid_expiry(%lu)",
@@ -1745,7 +1759,7 @@ flashcache_status_info(struct cache_c *dmc, status_type_t type,
dmc->enqueues, dmc->pending_inval,
dmc->md_write_dirty, dmc->md_write_clean,
dmc->md_write_batch, dmc->md_ssd_writes,
- dmc->cleanings, dmc->noroom, dmc->front_merge, dmc->back_merge,
+ dmc->cleanings, dmc->fallow_cleanings, dmc->noroom, dmc->front_merge, dmc->back_merge,
dmc->disk_reads, dmc->disk_writes, dmc->ssd_reads, dmc->ssd_writes,
dmc->uncached_reads, dmc->uncached_writes, dmc->uncached_io_requeue,
dmc->pid_adds, dmc->pid_dels, dmc->pid_drops, dmc->expiry);
View
211 src/flashcache_main.c
@@ -120,6 +120,51 @@ int dm_io_async_bvec(unsigned int num_regions,
}
#endif
+/*
+ * A simple 2-hand clock like algorithm is used to identify dirty blocks
+ * that lie fallow in the cache and thus are candidates for cleaning.
+ * Note that we could have such fallow blocks in sets where the dirty blocks
+ * is under the configured threshold.
+ * The hands are spaced 60 seconds apart (one sweep runs every 60 seconds).
+ * The interval is configurable via a sysctl.
+ * Blocks are moved to DIRTY_FALLOW_1, if they are found to be in DIRTY_FALLOW_1
+ * for 60 seconds or more, they are moved to DIRTY_FALLOW_1 | DIRTY_FALLOW_2, at
+ * which point they are eligible for cleaning. Of course any intervening use
+ * of the block within the interval turns off these 2 bits.
+ *
+ * Cleaning of these blocks happens from the flashcache_clean_set() function.
+ */
+void
+flashcache_detect_fallow(struct cache_c *dmc, int index)
+{
+ struct cacheblock *cacheblk = &dmc->cache[index];
+
+ if ((cacheblk->cache_state & DIRTY) &&
+ ((cacheblk->cache_state & BLOCK_IO_INPROG) == 0)) {
+ if ((cacheblk->cache_state & DIRTY_FALLOW_1) == 0)
+ cacheblk->cache_state |= DIRTY_FALLOW_1;
+ else if ((cacheblk->cache_state & DIRTY_FALLOW_2) == 0) {
+ dmc->cache_sets[index / dmc->assoc].dirty_fallow++;
+ cacheblk->cache_state |= DIRTY_FALLOW_2;
+ }
+ }
+}
+
+void
+flashcache_clear_fallow(struct cache_c *dmc, int index)
+{
+ struct cacheblock *cacheblk = &dmc->cache[index];
+ int set = index / dmc->assoc;
+
+ if (cacheblk->cache_state & FALLOW_DOCLEAN) {
+ if (cacheblk->cache_state & DIRTY_FALLOW_2) {
+ VERIFY(dmc->cache_sets[set].dirty_fallow > 0);
+ dmc->cache_sets[set].dirty_fallow--;
+ }
+ cacheblk->cache_state &= ~FALLOW_DOCLEAN;
+ }
+}
+
void
flashcache_io_callback(unsigned long error, void *context)
{
@@ -317,6 +362,7 @@ flashcache_do_pending_noerror(struct kcached_job *job)
if (cacheblk->cache_state & DIRTY) {
cacheblk->cache_state &= ~(BLOCK_IO_INPROG);
cacheblk->cache_state |= DISKWRITEINPROG;
+ flashcache_clear_fallow(dmc, index);
spin_unlock_irqrestore(&dmc->cache_spin_lock, flags);
flashcache_dirty_writeback(dmc, index);
goto out;
@@ -426,6 +472,11 @@ find_valid_dbn(struct cache_c *dmc, sector_t dbn,
if (sysctl_flashcache_reclaim_policy == FLASHCACHE_LRU &&
((dmc->cache[i].cache_state & BLOCK_IO_INPROG) == 0))
flashcache_reclaim_lru_movetail(dmc, i);
+ /*
+ * If the block was DIRTY and earmarked for cleaning because it was old, make
+ * the block young again.
+ */
+ flashcache_clear_fallow(dmc, i);
return;
}
}
@@ -443,6 +494,7 @@ find_invalid_dbn(struct cache_c *dmc, int start_index)
if (dmc->cache[i].cache_state == INVALID) {
if (sysctl_flashcache_reclaim_policy == FLASHCACHE_LRU)
flashcache_reclaim_lru_movetail(dmc, i);
+ VERIFY((dmc->cache[i].cache_state & FALLOW_DOCLEAN) == 0);
return i;
}
}
@@ -454,18 +506,21 @@ static void
find_reclaim_dbn(struct cache_c *dmc, int start_index, int *index)
{
int set = start_index / dmc->assoc;
+ struct cache_set *cache_set = &dmc->cache_sets[set];
+ struct cacheblock *cacheblk;
if (sysctl_flashcache_reclaim_policy == FLASHCACHE_FIFO) {
int end_index = start_index + dmc->assoc;
int slots_searched = 0;
int i;
- i = dmc->cache_sets[set].set_fifo_next;
+ i = cache_set->set_fifo_next;
while (slots_searched < dmc->assoc) {
VERIFY(i >= start_index);
VERIFY(i < end_index);
if (dmc->cache[i].cache_state == VALID) {
*index = i;
+ VERIFY((dmc->cache[*index].cache_state & FALLOW_DOCLEAN) == 0);
break;
}
slots_searched++;
@@ -476,18 +531,18 @@ find_reclaim_dbn(struct cache_c *dmc, int start_index, int *index)
i++;
if (i == end_index)
i = start_index;
- dmc->cache_sets[set].set_fifo_next = i;
+ cache_set->set_fifo_next = i;
} else { /* flashcache_reclaim_policy == FLASHCACHE_LRU */
- struct cacheblock *cacheblk;
int lru_rel_index;
- lru_rel_index = dmc->cache_sets[set].lru_head;
+ lru_rel_index = cache_set->lru_head;
while (lru_rel_index != FLASHCACHE_LRU_NULL) {
cacheblk = &dmc->cache[lru_rel_index + start_index];
if (cacheblk->cache_state == VALID) {
VERIFY((cacheblk - &dmc->cache[0]) ==
(lru_rel_index + start_index));
*index = cacheblk - &dmc->cache[0];
+ VERIFY((dmc->cache[*index].cache_state & FALLOW_DOCLEAN) == 0);
flashcache_reclaim_lru_movetail(dmc, *index);
break;
}
@@ -964,23 +1019,51 @@ flashcache_dirty_writeback(struct cache_c *dmc, int index)
}
/*
- * Clean dirty blocks in this set as needed.
+ * This function encodes the background disk cleaning logic.
+ * Background disk cleaning is triggered for 2 reasons.
+ A) Dirty blocks are lying fallow in the set, making them good
+ candidates for being cleaned.
+ B) This set has dirty blocks over the configured threshold
+ for a set.
+ * (A) takes precedence over (B). Fallow dirty blocks are cleaned
+ * first.
+ * The cleaning of disk blocks is subject to the write limits per
+ * set and across the cache, which this function enforces.
*
- * 1) Select the n blocks that we want to clean (choosing whatever policy), sort them.
- * 2) Then sweep the entire set looking for other DIRTY blocks that can be tacked onto
- * any of these blocks to form larger contigous writes. The idea here is that if you
- * are going to do a write anyway, then we might as well opportunistically write out
- * any contigous blocks for free (Bob's idea).
+ * 1) Select the n blocks that we want to clean (choosing whatever policy),
+ * sort them.
+ * 2) Then sweep the entire set looking for other DIRTY blocks that can be
+ * tacked onto any of these blocks to form larger contigous writes.
+ * The idea here is that if you are going to do a write anyway, then we
+ * might as well opportunistically write out any contigous blocks for
+ * free.
*/
+
+/* Are we under the limits for disk cleaning ? */
+static inline int
+flashcache_can_clean(struct cache_c *dmc,
+ struct cache_set *cache_set,
+ int nr_writes)
+{
+ return ((cache_set->clean_inprog + nr_writes) < dmc->max_clean_ios_set &&
+ (nr_writes + dmc->clean_inprog) < dmc->max_clean_ios_total);
+}
+
+extern int sysctl_fallow_delay;
+
void
flashcache_clean_set(struct cache_c *dmc, int set)
{
unsigned long flags;
- int to_clean = 0;
+ int threshold_clean = 0;
struct dbn_index_pair *writes_list;
- int nr_writes = 0;
- int start_index = set * dmc->assoc;
-
+ int nr_writes = 0, i;
+ int start_index = set * dmc->assoc;
+ int end_index = start_index + dmc->assoc;
+ struct cache_set *cache_set = &dmc->cache_sets[set];
+ struct cacheblock *cacheblk;
+ int do_delayed_clean = 0;
+
/*
* If a (fast) removal of this device is in progress, don't kick off
* any more cleanings. This isn't sufficient though. We still need to
@@ -1000,31 +1083,61 @@ flashcache_clean_set(struct cache_c *dmc, int set)
dmc->memory_alloc_errors++;
return;
}
- dmc->clean_set_calls++;
spin_lock_irqsave(&dmc->cache_spin_lock, flags);
- if (dmc->cache_sets[set].nr_dirty < dmc->dirty_thresh_set) {
- dmc->clean_set_less_dirty++;
- spin_unlock_irqrestore(&dmc->cache_spin_lock, flags);
- kfree(writes_list);
- return;
- } else
- to_clean = dmc->cache_sets[set].nr_dirty - dmc->dirty_thresh_set;
+ /*
+ * Before we try to clean any blocks, check the last time the fallow block
+ * detection was done. If it has been more than 60 seconds, make a sweep
+ * through the set to detect (mark) fallow blocks.
+ */
+ if (sysctl_fallow_delay && time_before(cache_set->fallow_tstamp, jiffies)) {
+ for (i = start_index ; i < end_index ; i++)
+ flashcache_detect_fallow(dmc, i);
+ cache_set->fallow_tstamp = jiffies + sysctl_fallow_delay * HZ;
+ }
+ /* If there are any dirty fallow blocks, clean them first */
+ for (i = start_index ; cache_set->dirty_fallow > 0 && i < end_index ; i++) {
+ cacheblk = &dmc->cache[i];
+ if (!(cacheblk->cache_state & DIRTY_FALLOW_2))
+ continue;
+ if (!flashcache_can_clean(dmc, cache_set, nr_writes)) {
+ /*
+ * There are fallow blocks that need cleaning, but we can't clean
+ * them this pass, schedule delayed cleaning later.
+ */
+ do_delayed_clean = 1;
+ goto out;
+ }
+ VERIFY(cacheblk->cache_state & DIRTY);
+ VERIFY((cacheblk->cache_state & BLOCK_IO_INPROG) == 0);
+ cacheblk->cache_state |= DISKWRITEINPROG;
+ flashcache_clear_fallow(dmc, i);
+ writes_list[nr_writes].dbn = cacheblk->dbn;
+ writes_list[nr_writes].index = i;
+ dmc->fallow_cleanings++;
+ nr_writes++;
+ }
+ if (cache_set->nr_dirty < dmc->dirty_thresh_set ||
+ !flashcache_can_clean(dmc, cache_set, nr_writes))
+ goto out;
+ /*
+ * We picked up all the dirty fallow blocks we can. We can still clean more to
+ * remain under the dirty threshold. Clean some more blocks.
+ */
+ threshold_clean = cache_set->nr_dirty - dmc->dirty_thresh_set;
if (sysctl_flashcache_reclaim_policy == FLASHCACHE_FIFO) {
- int i, scanned;
- int start_index, end_index;
-
- start_index = set * dmc->assoc;
- end_index = start_index + dmc->assoc;
+ int scanned;
+
scanned = 0;
- i = dmc->cache_sets[set].set_clean_next;
+ i = cache_set->set_clean_next;
DPRINTK("flashcache_clean_set: Set %d", set);
while (scanned < dmc->assoc &&
- ((dmc->cache_sets[set].clean_inprog + nr_writes) < dmc->max_clean_ios_set) &&
- ((nr_writes + dmc->clean_inprog) < dmc->max_clean_ios_total) &&
- nr_writes < to_clean) {
- if ((dmc->cache[i].cache_state & (DIRTY | BLOCK_IO_INPROG)) == DIRTY) {
- dmc->cache[i].cache_state |= DISKWRITEINPROG;
- writes_list[nr_writes].dbn = dmc->cache[i].dbn;
+ flashcache_can_clean(dmc, cache_set, nr_writes) &&
+ nr_writes < threshold_clean) {
+ cacheblk = &dmc->cache[i];
+ if ((cacheblk->cache_state & (DIRTY | BLOCK_IO_INPROG)) == DIRTY) {
+ cacheblk->cache_state |= DISKWRITEINPROG;
+ flashcache_clear_fallow(dmc, i);
+ writes_list[nr_writes].dbn = cacheblk->dbn;
writes_list[nr_writes].index = i;
nr_writes++;
}
@@ -1033,19 +1146,18 @@ flashcache_clean_set(struct cache_c *dmc, int set)
if (i == end_index)
i = start_index;
}
- dmc->cache_sets[set].set_clean_next = i;
+ cache_set->set_clean_next = i;
} else { /* flashcache_reclaim_policy == FLASHCACHE_LRU */
- struct cacheblock *cacheblk;
int lru_rel_index;
- lru_rel_index = dmc->cache_sets[set].lru_head;
+ lru_rel_index = cache_set->lru_head;
while (lru_rel_index != FLASHCACHE_LRU_NULL &&
- ((dmc->cache_sets[set].clean_inprog + nr_writes) < dmc->max_clean_ios_set) &&
- ((nr_writes + dmc->clean_inprog) < dmc->max_clean_ios_total) &&
- nr_writes < to_clean) {
- cacheblk = &dmc->cache[lru_rel_index + start_index];
+ flashcache_can_clean(dmc, cache_set, nr_writes) &&
+ nr_writes < threshold_clean) {
+ cacheblk = &dmc->cache[lru_rel_index + start_index];
if ((cacheblk->cache_state & (DIRTY | BLOCK_IO_INPROG)) == DIRTY) {
cacheblk->cache_state |= DISKWRITEINPROG;
+ flashcache_clear_fallow(dmc, i);
writes_list[nr_writes].dbn = cacheblk->dbn;
writes_list[nr_writes].index = cacheblk - &dmc->cache[0];
nr_writes++;
@@ -1053,27 +1165,19 @@ flashcache_clean_set(struct cache_c *dmc, int set)
lru_rel_index = cacheblk->lru_next;
}
}
+out:
if (nr_writes > 0) {
- int i;
-
flashcache_merge_writes(dmc, writes_list, &nr_writes, set);
dmc->clean_set_ios += nr_writes;
spin_unlock_irqrestore(&dmc->cache_spin_lock, flags);
for (i = 0 ; i < nr_writes ; i++)
flashcache_dirty_writeback(dmc, writes_list[i].index);
} else {
- int do_delayed_clean = 0;
-
- if (dmc->cache_sets[set].nr_dirty > dmc->dirty_thresh_set)
+ if (cache_set->nr_dirty > dmc->dirty_thresh_set)
do_delayed_clean = 1;
spin_unlock_irqrestore(&dmc->cache_spin_lock, flags);
- if (dmc->cache_sets[set].clean_inprog >= dmc->max_clean_ios_set)
- dmc->set_limit_reached++;
- if (dmc->clean_inprog >= dmc->max_clean_ios_total)
- dmc->total_limit_reached++;
if (do_delayed_clean)
schedule_delayed_work(&dmc->delayed_clean, 1*HZ);
- dmc->clean_set_fails++;
}
kfree(writes_list);
}
@@ -1308,6 +1412,7 @@ flashcache_inval_block_set(struct cache_c *dmc, int set, struct bio *bio, int rw
* at the cost of a context switch.
*/
cacheblk->cache_state |= DISKWRITEINPROG;
+ flashcache_clear_fallow(dmc, i);
spin_unlock_irq(&dmc->cache_spin_lock);
flashcache_dirty_writeback(dmc, i); /* Must inc nr_jobs */
spin_lock_irq(&dmc->cache_spin_lock);
@@ -1642,6 +1747,7 @@ flashcache_dirty_writeback_sync(struct cache_c *dmc, int index)
struct cacheblock *cacheblk = &dmc->cache[index];
int device_removal = 0;
+ VERIFY((cacheblk->cache_state & FALLOW_DOCLEAN) == 0);
DPRINTK("flashcache_dirty_writeback_sync: Index %d", index);
spin_lock_irqsave(&dmc->cache_spin_lock, flags);
VERIFY((cacheblk->cache_state & BLOCK_IO_INPROG) == DISKWRITEINPROG);
@@ -1744,8 +1850,9 @@ flashcache_sync_blocks(struct cache_c *dmc)
cacheblk = &dmc->cache[index];
if ((cacheblk->cache_state & (DIRTY | BLOCK_IO_INPROG)) == DIRTY) {
cacheblk->cache_state |= DISKWRITEINPROG;
+ flashcache_clear_fallow(dmc, index);
writes_list[nr_writes].dbn = cacheblk->dbn;
- writes_list[nr_writes].index = cacheblk - &dmc->cache[0];
+ writes_list[nr_writes].index = index;
set = index / dmc->assoc;
nr_writes++;
}
View
13 src/flashcache_subr.c
@@ -483,7 +483,8 @@ flashcache_merge_writes(struct cache_c *dmc, struct dbn_index_pair *writes_list,
int new_inserts = 0;
struct dbn_index_pair *set_dirty_list = NULL;
int ix, nr_set_dirty;
-
+ struct cacheblock *cacheblk;
+
if (unlikely(*nr_writes == 0))
return;
sort(writes_list, *nr_writes, sizeof(struct dbn_index_pair),
@@ -497,8 +498,7 @@ flashcache_merge_writes(struct cache_c *dmc, struct dbn_index_pair *writes_list,
}
nr_set_dirty = 0;
for (ix = start_index ; ix < end_index ; ix++) {
- struct cacheblock *cacheblk = &dmc->cache[ix];
-
+ cacheblk = &dmc->cache[ix];
/*
* Any DIRTY block in "writes_list" will be marked as
* DISKWRITEINPROG already, so we'll skip over those here.
@@ -514,10 +514,10 @@ flashcache_merge_writes(struct cache_c *dmc, struct dbn_index_pair *writes_list,
sort(set_dirty_list, nr_set_dirty, sizeof(struct dbn_index_pair),
cmp_dbn, swap_dbn_index_pair);
for (ix = 0 ; ix < nr_set_dirty ; ix++) {
- struct cacheblock *cacheblk = &dmc->cache[set_dirty_list[ix].index];
int back_merge, k;
int i;
+ cacheblk = &dmc->cache[set_dirty_list[ix].index];
back_merge = -1;
VERIFY((cacheblk->cache_state & (DIRTY | BLOCK_IO_INPROG)) == DIRTY);
for (i = 0 ; i < *nr_writes ; i++) {
@@ -539,6 +539,7 @@ flashcache_merge_writes(struct cache_c *dmc, struct dbn_index_pair *writes_list,
VERIFY(j < dmc->assoc);
if (insert) {
cacheblk->cache_state |= DISKWRITEINPROG;
+ flashcache_clear_fallow(dmc, set_dirty_list[ix].index);
/*
* Shift down everthing from j to ((*nr_writes) - 1) to
* make room for the new entry. And add the new entry.
@@ -568,9 +569,11 @@ flashcache_merge_writes(struct cache_c *dmc, struct dbn_index_pair *writes_list,
for (k = ix - 1 ; k >= 0 ; k--) {
int n;
- if (set_dirty_list[k].dbn + dmc->block_size != writes_list[back_merge].dbn)
+ if (set_dirty_list[k].dbn + dmc->block_size !=
+ writes_list[back_merge].dbn)
break;
dmc->cache[set_dirty_list[k].index].cache_state |= DISKWRITEINPROG;
+ flashcache_clear_fallow(dmc, set_dirty_list[k].index);
for (n = (*nr_writes) - 1 ; n >= back_merge ; n--)
writes_list[n + 1] = writes_list[n];
writes_list[back_merge].dbn = set_dirty_list[k].dbn;

0 comments on commit a9cf0f7

Please sign in to comment.
Something went wrong with that request. Please try again.