History

dm bufio: introduce a global cache replacement

This commit introduces a global cache replacement (instead of per-client cleanup). If one bufio client uses the cache heavily and another client is not using it, we want to let the first client use most of the cache. The old algorithm would partition the cache equally betwen the clients and that is sub-optimal. For cache replacement, we use the clock algorithm because it doesn't require taking any lock when the buffer is accessed.

Mikulas Patocka committed 7 years ago

Commit 6e913b21 parent b132ff31 file · +91 −7Browse files

Unified · Split

drivers/md/dm-bufio.c+91−7
@@ -34,6 +34,7 @@ · 33 unmodified lines
3434 #define DM_BUFIO_MEMORY_PERCENT 2
3535 #define DM_BUFIO_VMALLOC_PERCENT 25
3636 #define DM_BUFIO_WRITEBACK_RATIO 3
37+#define DM_BUFIO_LOW_WATERMARK_RATIO 16
3738
3839 /*
3940 * Check buffer ages in this interval (seconds)
@@ -139,6 +140,7 @@ struct dm_buffer { · 99 unmodified lines
139140 unsigned char list_mode; /* LIST_* */
140141 blk_status_t read_error;
141142 blk_status_t write_error;
143+ unsigned accessed;
142144 unsigned hold_count;
143145 unsigned long state;
144146 unsigned long last_accessed;
@@ -197,6 +199,8 @@ static DEFINE_SPINLOCK(global_spinlock); · 52 unmodified lines
197199
198200 static LIST_HEAD(global_queue);
199201
202+static unsigned long global_num = 0;
203+
200204 /*
201205 * Buffers are freed after this timeout
202206 */
@@ -226,6 +230,11 @@ static LIST_HEAD(dm_bufio_all_clients); · 23 unmodified lines
226230 */
227231 static DEFINE_MUTEX(dm_bufio_clients_lock);
228232
233+static struct workqueue_struct *dm_bufio_wq;
234+static struct delayed_work dm_bufio_cleanup_old_work;
235+static struct work_struct dm_bufio_replacement_work;
236+
237+
229238 #ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
230239 static void buffer_record_stack(struct dm_buffer *b)
231240 {
@@ -307,10 +316,16 @@ static void adjust_total_allocated(struct dm_buffer *b, bool unlink) · 75 unmodified lines
307316 if (dm_bufio_current_allocated > dm_bufio_peak_allocated)
308317 dm_bufio_peak_allocated = dm_bufio_current_allocated;
309318
319+ b->accessed = 1;
320+
310321 if (!unlink) {
311322 list_add(&b->global_list, &global_queue);
323+ global_num++;
324+ if (dm_bufio_current_allocated > dm_bufio_cache_size)
325+ queue_work(dm_bufio_wq, &dm_bufio_replacement_work);
312326 } else {
313327 list_del(&b->global_list);
328+ global_num--;
314329 }
315330
316331 spin_unlock(&global_spinlock);
@@ -496,6 +511,8 @@ static void __relink_lru(struct dm_buffer *b, int dirty) · 179 unmodified lines
496511 {
497512 struct dm_bufio_client *c = b->c;
498513
514+ b->accessed = 1;
515+
499516 BUG_ON(!c->n_buffers[b->list_mode]);
500517
501518 c->n_buffers[b->list_mode]--;
@@ -1803,6 +1820,74 @@ static void __evict_old_buffers(struct dm_bufio_client *c, unsigned long age_hz) · 1301 unmodified lines
18031820 dm_bufio_unlock(c);
18041821 }
18051822
1823+static void do_global_cleanup(struct work_struct *w)
1824+{
1825+ struct dm_bufio_client *locked_client = NULL;
1826+ struct dm_bufio_client *current_client;
1827+ struct dm_buffer *b;
1828+ unsigned spinlock_hold_count;
1829+ unsigned long threshold = dm_bufio_cache_size -
1830+ dm_bufio_cache_size / DM_BUFIO_LOW_WATERMARK_RATIO;
1831+ unsigned long loops = global_num * 2;
1832+
1833+ mutex_lock(&dm_bufio_clients_lock);
1834+
1835+ while (1) {
1836+ cond_resched();
1837+
1838+ spin_lock(&global_spinlock);
1839+ if (unlikely(dm_bufio_current_allocated <= threshold))
1840+ break;
1841+
1842+ spinlock_hold_count = 0;
1843+get_next:
1844+ if (!loops--)
1845+ break;
1846+ if (unlikely(list_empty(&global_queue)))
1847+ break;
1848+ b = list_entry(global_queue.prev, struct dm_buffer, global_list);
1849+
1850+ if (b->accessed) {
1851+ b->accessed = 0;
1852+ list_move(&b->global_list, &global_queue);
1853+ if (likely(++spinlock_hold_count < 16))
1854+ goto get_next;
1855+ spin_unlock(&global_spinlock);
1856+ continue;
1857+ }
1858+
1859+ current_client = b->c;
1860+ if (unlikely(current_client != locked_client)) {
1861+ if (locked_client)
1862+ dm_bufio_unlock(locked_client);
1863+
1864+ if (!dm_bufio_trylock(current_client)) {
1865+ spin_unlock(&global_spinlock);
1866+ dm_bufio_lock(current_client);
1867+ locked_client = current_client;
1868+ continue;
1869+ }
1870+
1871+ locked_client = current_client;
1872+ }
1873+
1874+ spin_unlock(&global_spinlock);
1875+
1876+ if (unlikely(!__try_evict_buffer(b, GFP_KERNEL))) {
1877+ spin_lock(&global_spinlock);
1878+ list_move(&b->global_list, &global_queue);
1879+ spin_unlock(&global_spinlock);
1880+ }
1881+ }
1882+
1883+ spin_unlock(&global_spinlock);
1884+
1885+ if (locked_client)
1886+ dm_bufio_unlock(locked_client);
1887+
1888+ mutex_unlock(&dm_bufio_clients_lock);
1889+}
1890+
18061891 static void cleanup_old_buffers(void)
18071892 {
18081893 unsigned long max_age_hz = get_max_age_hz();
@@ -1818,14 +1903,11 @@ static void cleanup_old_buffers(void) · 9 unmodified lines
18181903 mutex_unlock(&dm_bufio_clients_lock);
18191904 }
18201905
1821−static struct workqueue_struct *dm_bufio_wq;
1822−static struct delayed_work dm_bufio_work;
1823−
18241906 static void work_fn(struct work_struct *w)
18251907 {
18261908 cleanup_old_buffers();
18271909
1828− queue_delayed_work(dm_bufio_wq, &dm_bufio_work,
1910+ queue_delayed_work(dm_bufio_wq, &dm_bufio_cleanup_old_work,
18291911 DM_BUFIO_WORK_TIMER_SECS * HZ);
18301912 }
18311913
@@ -1867,8 +1949,9 @@ static int __init dm_bufio_init(void) · 35 unmodified lines
18671949 if (!dm_bufio_wq)
18681950 return -ENOMEM;
18691951
1870− INIT_DELAYED_WORK(&dm_bufio_work, work_fn);
1871− queue_delayed_work(dm_bufio_wq, &dm_bufio_work,
1952+ INIT_DELAYED_WORK(&dm_bufio_cleanup_old_work, work_fn);
1953+ INIT_WORK(&dm_bufio_replacement_work, do_global_cleanup);
1954+ queue_delayed_work(dm_bufio_wq, &dm_bufio_cleanup_old_work,
18721955 DM_BUFIO_WORK_TIMER_SECS * HZ);
18731956
18741957 return 0;
@@ -1881,7 +1964,8 @@ static void __exit dm_bufio_exit(void) · 6 unmodified lines
18811964 {
18821965 int bug = 0;
18831966
1884− cancel_delayed_work_sync(&dm_bufio_work);
1967+ cancel_delayed_work_sync(&dm_bufio_cleanup_old_work);
1968+ flush_workqueue(dm_bufio_wq);
18851969 destroy_workqueue(dm_bufio_wq);
18861970
18871971 if (dm_bufio_client_count) {

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t