aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoe Thornber <ejt@redhat.com>2014-10-09 06:10:25 -0400
committerMike Snitzer <snitzer@redhat.com>2014-11-10 15:25:26 -0500
commit33096a7822de63bc7dbdd090870b656a0304fa35 (patch)
tree1814ee0de89579652025599746cae9c7fa942aed
parent4e420c452b11edf9d510c8180ac66f529e5b6206 (diff)
dm bufio: evict buffers that are past the max age but retain some buffers
These changes help keep metadata backed by dm-bufio in-core longer which fixes reports of metadata churn in the face of heavy random IO workloads. Before, bufio evicted all buffers older than DM_BUFIO_DEFAULT_AGE_SECS. Having a device (e.g. dm-thinp or dm-cache) lose all metadata just because associated buffers had been idle for some time is unfriendly. Now, the user may now configure the number of bytes that bufio retains using the 'retain_bytes' module parameter. The default is 256K. Also, the DM_BUFIO_WORK_TIMER_SECS and DM_BUFIO_DEFAULT_AGE_SECS defaults were quite low so increase them (to 30 and 300 respectively). Signed-off-by: Joe Thornber <ejt@redhat.com> Signed-off-by: Mike Snitzer <snitzer@redhat.com>
-rw-r--r--drivers/md/dm-bufio.c109
1 files changed, 75 insertions, 34 deletions
diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c
index dcaa1d9dfbe4..99579c81ae0a 100644
--- a/drivers/md/dm-bufio.c
+++ b/drivers/md/dm-bufio.c
@@ -35,12 +35,17 @@
35/* 35/*
36 * Check buffer ages in this interval (seconds) 36 * Check buffer ages in this interval (seconds)
37 */ 37 */
38#define DM_BUFIO_WORK_TIMER_SECS 10 38#define DM_BUFIO_WORK_TIMER_SECS 30
39 39
40/* 40/*
41 * Free buffers when they are older than this (seconds) 41 * Free buffers when they are older than this (seconds)
42 */ 42 */
43#define DM_BUFIO_DEFAULT_AGE_SECS 60 43#define DM_BUFIO_DEFAULT_AGE_SECS 300
44
45/*
46 * The nr of bytes of cached data to keep around.
47 */
48#define DM_BUFIO_DEFAULT_RETAIN_BYTES (256 * 1024)
44 49
45/* 50/*
46 * The number of bvec entries that are embedded directly in the buffer. 51 * The number of bvec entries that are embedded directly in the buffer.
@@ -216,6 +221,7 @@ static DEFINE_SPINLOCK(param_spinlock);
216 * Buffers are freed after this timeout 221 * Buffers are freed after this timeout
217 */ 222 */
218static unsigned dm_bufio_max_age = DM_BUFIO_DEFAULT_AGE_SECS; 223static unsigned dm_bufio_max_age = DM_BUFIO_DEFAULT_AGE_SECS;
224static unsigned dm_bufio_retain_bytes = DM_BUFIO_DEFAULT_RETAIN_BYTES;
219 225
220static unsigned long dm_bufio_peak_allocated; 226static unsigned long dm_bufio_peak_allocated;
221static unsigned long dm_bufio_allocated_kmem_cache; 227static unsigned long dm_bufio_allocated_kmem_cache;
@@ -1457,45 +1463,52 @@ static void drop_buffers(struct dm_bufio_client *c)
1457} 1463}
1458 1464
1459/* 1465/*
1460 * Test if the buffer is unused and too old, and commit it. 1466 * We may not be able to evict this buffer if IO pending or the client
1467 * is still using it. Caller is expected to know buffer is too old.
1468 *
1461 * And if GFP_NOFS is used, we must not do any I/O because we hold 1469 * And if GFP_NOFS is used, we must not do any I/O because we hold
1462 * dm_bufio_clients_lock and we would risk deadlock if the I/O gets 1470 * dm_bufio_clients_lock and we would risk deadlock if the I/O gets
1463 * rerouted to different bufio client. 1471 * rerouted to different bufio client.
1464 */ 1472 */
1465static int __cleanup_old_buffer(struct dm_buffer *b, gfp_t gfp, 1473static bool __try_evict_buffer(struct dm_buffer *b, gfp_t gfp)
1466 unsigned long max_jiffies)
1467{ 1474{
1468 if (jiffies - b->last_accessed < max_jiffies)
1469 return 0;
1470
1471 if (!(gfp & __GFP_FS)) { 1475 if (!(gfp & __GFP_FS)) {
1472 if (test_bit(B_READING, &b->state) || 1476 if (test_bit(B_READING, &b->state) ||
1473 test_bit(B_WRITING, &b->state) || 1477 test_bit(B_WRITING, &b->state) ||
1474 test_bit(B_DIRTY, &b->state)) 1478 test_bit(B_DIRTY, &b->state))
1475 return 0; 1479 return false;
1476 } 1480 }
1477 1481
1478 if (b->hold_count) 1482 if (b->hold_count)
1479 return 0; 1483 return false;
1480 1484
1481 __make_buffer_clean(b); 1485 __make_buffer_clean(b);
1482 __unlink_buffer(b); 1486 __unlink_buffer(b);
1483 __free_buffer_wake(b); 1487 __free_buffer_wake(b);
1484 1488
1485 return 1; 1489 return true;
1486} 1490}
1487 1491
1488static long __scan(struct dm_bufio_client *c, unsigned long nr_to_scan, 1492static unsigned get_retain_buffers(struct dm_bufio_client *c)
1489 gfp_t gfp_mask) 1493{
1494 unsigned retain_bytes = ACCESS_ONCE(dm_bufio_retain_bytes);
1495 return retain_bytes / c->block_size;
1496}
1497
1498static unsigned long __scan(struct dm_bufio_client *c, unsigned long nr_to_scan,
1499 gfp_t gfp_mask)
1490{ 1500{
1491 int l; 1501 int l;
1492 struct dm_buffer *b, *tmp; 1502 struct dm_buffer *b, *tmp;
1493 long freed = 0; 1503 unsigned long freed = 0;
1504 unsigned long count = nr_to_scan;
1505 unsigned retain_target = get_retain_buffers(c);
1494 1506
1495 for (l = 0; l < LIST_SIZE; l++) { 1507 for (l = 0; l < LIST_SIZE; l++) {
1496 list_for_each_entry_safe_reverse(b, tmp, &c->lru[l], lru_list) { 1508 list_for_each_entry_safe_reverse(b, tmp, &c->lru[l], lru_list) {
1497 freed += __cleanup_old_buffer(b, gfp_mask, 0); 1509 if (__try_evict_buffer(b, gfp_mask))
1498 if (!--nr_to_scan) 1510 freed++;
1511 if (!--nr_to_scan || ((count - freed) <= retain_target))
1499 return freed; 1512 return freed;
1500 dm_bufio_cond_resched(); 1513 dm_bufio_cond_resched();
1501 } 1514 }
@@ -1697,31 +1710,56 @@ void dm_bufio_client_destroy(struct dm_bufio_client *c)
1697} 1710}
1698EXPORT_SYMBOL_GPL(dm_bufio_client_destroy); 1711EXPORT_SYMBOL_GPL(dm_bufio_client_destroy);
1699 1712
1700static void cleanup_old_buffers(void) 1713static unsigned get_max_age_hz(void)
1701{ 1714{
1702 unsigned long max_age = ACCESS_ONCE(dm_bufio_max_age); 1715 unsigned max_age = ACCESS_ONCE(dm_bufio_max_age);
1703 struct dm_bufio_client *c;
1704 1716
1705 if (max_age > ULONG_MAX / HZ) 1717 if (max_age > UINT_MAX / HZ)
1706 max_age = ULONG_MAX / HZ; 1718 max_age = UINT_MAX / HZ;
1707 1719
1708 mutex_lock(&dm_bufio_clients_lock); 1720 return max_age * HZ;
1709 list_for_each_entry(c, &dm_bufio_all_clients, client_list) { 1721}
1710 if (!dm_bufio_trylock(c))
1711 continue;
1712 1722
1713 while (!list_empty(&c->lru[LIST_CLEAN])) { 1723static bool older_than(struct dm_buffer *b, unsigned long age_hz)
1714 struct dm_buffer *b; 1724{
1715 b = list_entry(c->lru[LIST_CLEAN].prev, 1725 return (jiffies - b->last_accessed) >= age_hz;
1716 struct dm_buffer, lru_list); 1726}
1717 if (!__cleanup_old_buffer(b, 0, max_age * HZ)) 1727
1718 break; 1728static void __evict_old_buffers(struct dm_bufio_client *c, unsigned long age_hz)
1719 dm_bufio_cond_resched(); 1729{
1720 } 1730 struct dm_buffer *b, *tmp;
1731 unsigned retain_target = get_retain_buffers(c);
1732 unsigned count;
1733
1734 dm_bufio_lock(c);
1735
1736 count = c->n_buffers[LIST_CLEAN] + c->n_buffers[LIST_DIRTY];
1737 list_for_each_entry_safe_reverse(b, tmp, &c->lru[LIST_CLEAN], lru_list) {
1738 if (count <= retain_target)
1739 break;
1740
1741 if (!older_than(b, age_hz))
1742 break;
1743
1744 if (__try_evict_buffer(b, 0))
1745 count--;
1721 1746
1722 dm_bufio_unlock(c);
1723 dm_bufio_cond_resched(); 1747 dm_bufio_cond_resched();
1724 } 1748 }
1749
1750 dm_bufio_unlock(c);
1751}
1752
1753static void cleanup_old_buffers(void)
1754{
1755 unsigned long max_age_hz = get_max_age_hz();
1756 struct dm_bufio_client *c;
1757
1758 mutex_lock(&dm_bufio_clients_lock);
1759
1760 list_for_each_entry(c, &dm_bufio_all_clients, client_list)
1761 __evict_old_buffers(c, max_age_hz);
1762
1725 mutex_unlock(&dm_bufio_clients_lock); 1763 mutex_unlock(&dm_bufio_clients_lock);
1726} 1764}
1727 1765
@@ -1846,6 +1884,9 @@ MODULE_PARM_DESC(max_cache_size_bytes, "Size of metadata cache");
1846module_param_named(max_age_seconds, dm_bufio_max_age, uint, S_IRUGO | S_IWUSR); 1884module_param_named(max_age_seconds, dm_bufio_max_age, uint, S_IRUGO | S_IWUSR);
1847MODULE_PARM_DESC(max_age_seconds, "Max age of a buffer in seconds"); 1885MODULE_PARM_DESC(max_age_seconds, "Max age of a buffer in seconds");
1848 1886