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
34#define DM_BUFIO_MEMORY_PERCENT 234#define DM_BUFIO_MEMORY_PERCENT 2
35#define DM_BUFIO_VMALLOC_PERCENT 2535#define DM_BUFIO_VMALLOC_PERCENT 25
36#define DM_BUFIO_WRITEBACK_RATIO 336#define DM_BUFIO_WRITEBACK_RATIO 3
37#define DM_BUFIO_LOW_WATERMARK_RATIO 16
37 38
38/*39/*
39 * Check buffer ages in this interval (seconds)40 * Check buffer ages in this interval (seconds)
@@ -139,6 +140,7 @@ struct dm_buffer { · 99 unmodified lines
139 unsigned char list_mode; /* LIST_* */140 unsigned char list_mode; /* LIST_* */
140 blk_status_t read_error;141 blk_status_t read_error;
141 blk_status_t write_error;142 blk_status_t write_error;
143 unsigned accessed;
142 unsigned hold_count;144 unsigned hold_count;
143 unsigned long state;145 unsigned long state;
144 unsigned long last_accessed;146 unsigned long last_accessed;
@@ -197,6 +199,8 @@ static DEFINE_SPINLOCK(global_spinlock); · 52 unmodified lines
197 199
198static LIST_HEAD(global_queue);200static LIST_HEAD(global_queue);
199 201
202static unsigned long global_num = 0;
203
200/*204/*
201 * Buffers are freed after this timeout205 * Buffers are freed after this timeout
202 */206 */
@@ -226,6 +230,11 @@ static LIST_HEAD(dm_bufio_all_clients); · 23 unmodified lines
226 */230 */
227static DEFINE_MUTEX(dm_bufio_clients_lock);231static DEFINE_MUTEX(dm_bufio_clients_lock);
228 232
233static struct workqueue_struct *dm_bufio_wq;
234static struct delayed_work dm_bufio_cleanup_old_work;
235static struct work_struct dm_bufio_replacement_work;
236
237
229#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING238#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
230static void buffer_record_stack(struct dm_buffer *b)239static void buffer_record_stack(struct dm_buffer *b)
231{240{
@@ -307,10 +316,16 @@ static void adjust_total_allocated(struct dm_buffer *b, bool unlink) · 75 unmodified lines
307 if (dm_bufio_current_allocated > dm_bufio_peak_allocated)316 if (dm_bufio_current_allocated > dm_bufio_peak_allocated)
308 dm_bufio_peak_allocated = dm_bufio_current_allocated;317 dm_bufio_peak_allocated = dm_bufio_current_allocated;
309 318
319 b->accessed = 1;
320
310 if (!unlink) {321 if (!unlink) {
311 list_add(&b->global_list, &global_queue);322 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);
312 } else {326 } else {
313 list_del(&b->global_list);327 list_del(&b->global_list);
328 global_num--;
314 }329 }
315 330
316 spin_unlock(&global_spinlock);331 spin_unlock(&global_spinlock);
@@ -496,6 +511,8 @@ static void __relink_lru(struct dm_buffer *b, int dirty) · 179 unmodified lines
496{511{
497 struct dm_bufio_client *c = b->c;512 struct dm_bufio_client *c = b->c;
498 513
514 b->accessed = 1;
515
499 BUG_ON(!c->n_buffers[b->list_mode]);516 BUG_ON(!c->n_buffers[b->list_mode]);
500 517
501 c->n_buffers[b->list_mode]--;518 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
1803 dm_bufio_unlock(c);1820 dm_bufio_unlock(c);
1804}1821}
1805 1822
1823static 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;
1843get_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
1806static void cleanup_old_buffers(void)1891static void cleanup_old_buffers(void)
1807{1892{
1808 unsigned long max_age_hz = get_max_age_hz();1893 unsigned long max_age_hz = get_max_age_hz();
@@ -1818,14 +1903,11 @@ static void cleanup_old_buffers(void) · 9 unmodified lines
1818 mutex_unlock(&dm_bufio_clients_lock);1903 mutex_unlock(&dm_bufio_clients_lock);
1819}1904}
1820 1905
1821static struct workqueue_struct *dm_bufio_wq;
1822static struct delayed_work dm_bufio_work;
1823
1824static void work_fn(struct work_struct *w)1906static void work_fn(struct work_struct *w)
1825{1907{
1826 cleanup_old_buffers();1908 cleanup_old_buffers();
1827 1909
1828 queue_delayed_work(dm_bufio_wq, &dm_bufio_work,1910 queue_delayed_work(dm_bufio_wq, &dm_bufio_cleanup_old_work,
1829 DM_BUFIO_WORK_TIMER_SECS * HZ);1911 DM_BUFIO_WORK_TIMER_SECS * HZ);
1830}1912}
1831 1913
@@ -1867,8 +1949,9 @@ static int __init dm_bufio_init(void) · 35 unmodified lines
1867 if (!dm_bufio_wq)1949 if (!dm_bufio_wq)
1868 return -ENOMEM;1950 return -ENOMEM;
1869 1951
1870 INIT_DELAYED_WORK(&dm_bufio_work, work_fn);1952 INIT_DELAYED_WORK(&dm_bufio_cleanup_old_work, work_fn);
1871 queue_delayed_work(dm_bufio_wq, &dm_bufio_work,1953 INIT_WORK(&dm_bufio_replacement_work, do_global_cleanup);
1954 queue_delayed_work(dm_bufio_wq, &dm_bufio_cleanup_old_work,
1872 DM_BUFIO_WORK_TIMER_SECS * HZ);1955 DM_BUFIO_WORK_TIMER_SECS * HZ);
1873 1956
1874 return 0;1957 return 0;
@@ -1881,7 +1964,8 @@ static void __exit dm_bufio_exit(void) · 6 unmodified lines
1881{1964{
1882 int bug = 0;1965 int bug = 0;
1883 1966
1884 cancel_delayed_work_sync(&dm_bufio_work);1967 cancel_delayed_work_sync(&dm_bufio_cleanup_old_work);
1968 flush_workqueue(dm_bufio_wq);
1885 destroy_workqueue(dm_bufio_wq);1969 destroy_workqueue(dm_bufio_wq);
1886 1970
1887 if (dm_bufio_client_count) {1971 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