Skip to content

Commit 9cee56c

Browse files
colincrossWinnie Hsu
authored andcommitted
nvmap: page pools: replace background allocator with background zeroer
The background allocator rapidly becomes useless once the system has filled memory with cached pages. It refuses to allocate when free memory < 128MB (which it always is, the kernel is aiming to keep very little free), and freed pages are not returned to the page pool when zero_memory=1. Remove the background allocator completely, and instead return freed memory to the page pool in a separate list to be zeroed in the background. This results in a self-balancing pool of memory available to graphics, and reduces presure on the kernel's page allocator. If the pool grows too big it will get reduced by the shrinker. If it gets too small, the next allocation will fall back to the page allocator, and then later return those pages to the pool. Before this change incremental page pool hit rate reported by /d/nvmap/pagepool/page_pool_hits vs. /d/nvmap/pagepool/page_pool_misses goes to 0% after boot. After this change it is near 100% for small app launches and 75% for larger app launches. Change-Id: I4bc914498d7d0369eef9e621bda110d9b8be90b2 Signed-off-by: Colin Cross <[email protected]> Signed-off-by: Krishna Reddy <[email protected]> Reviewed-on: http://git-master/r/664674 GVS: Gerrit_Virtual_Submit Reviewed-on: http://git-master/r/736428 Reviewed-by: Alex Waterman <[email protected]> Tested-by: Alex Waterman <[email protected]>
1 parent c81dc11 commit 9cee56c

File tree

3 files changed

+79
-155
lines changed

3 files changed

+79
-155
lines changed

drivers/video/tegra/nvmap/nvmap_handle.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,7 @@ void _nvmap_handle_free(struct nvmap_handle *h)
122122
for (i = 0; i < nr_page; i++)
123123
h->pgalloc.pages[i] = nvmap_to_page(h->pgalloc.pages[i]);
124124

125-
if (!zero_memory)
126-
page_index = nvmap_page_pool_fill_lots(&nvmap_dev->pool,
125+
page_index = nvmap_page_pool_fill_lots(&nvmap_dev->pool,
127126
h->pgalloc.pages, nr_page);
128127

129128
for (i = page_index; i < nr_page; i++)

drivers/video/tegra/nvmap/nvmap_pp.c

Lines changed: 78 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,14 @@
3434
#include "nvmap_priv.h"
3535

3636
#define NVMAP_TEST_PAGE_POOL_SHRINKER 1
37-
#define PENDING_PAGES_SIZE 128
38-
#define MIN_AVAILABLE_MB 128
37+
#define PENDING_PAGES_SIZE 32
3938

4039
static bool enable_pp = 1;
4140
static int pool_size;
4241

4342
static struct task_struct *background_allocator;
4443
static DECLARE_WAIT_QUEUE_HEAD(nvmap_bg_wait);
4544
static struct page *pending_pages[PENDING_PAGES_SIZE];
46-
static atomic_t bg_pages_to_fill;
4745

4846
#ifdef CONFIG_NVMAP_PAGE_POOL_DEBUG
4947
static inline void __pp_dbg_var_add(u64 *dbg_var, u32 nr)
@@ -62,6 +60,21 @@ static inline void __pp_dbg_var_add(u64 *dbg_var, u32 nr)
6260
static int __nvmap_page_pool_fill_lots_locked(struct nvmap_page_pool *pool,
6361
struct page **pages, u32 nr);
6462

63+
static inline struct page *get_zero_list_page(struct nvmap_page_pool *pool)
64+
{
65+
struct page *page;
66+
67+
if (list_empty(&pool->zero_list))
68+
return NULL;
69+
70+
page = list_first_entry(&pool->zero_list, struct page, lru);
71+
list_del(&page->lru);
72+
73+
pool->to_zero--;
74+
75+
return page;
76+
}
77+
6578
static inline struct page *get_page_list_page(struct nvmap_page_pool *pool)
6679
{
6780
struct page *page;
@@ -82,81 +95,50 @@ static inline bool nvmap_bg_should_run(struct nvmap_page_pool *pool)
8295
bool ret;
8396

8497
mutex_lock(&pool->lock);
85-
ret = (pool->to_zero > 0 || atomic_read(&bg_pages_to_fill));
98+
ret = (pool->to_zero > 0);
8699
mutex_unlock(&pool->lock);
87100

88101
return ret;
89102
}
90103

91-
/*
92-
* Allocate n pages one by one. Not the most efficient allocation scheme ever;
93-
* however, it will make it easier later on to handle single or small number of
94-
* page allocations from the page pool being individually freed.
95-
*/
96-
static int __nvmap_pp_alloc_n_pages(struct page **pages, int n, gfp_t flags)
104+
static int nvmap_pp_zero_pages(struct page **pages, int nr)
97105
{
98106
int i;
99107

100-
for (i = 0; i < n; i++) {
101-
pages[i] = alloc_page(flags);
102-
if (!pages[i])
103-
goto no_mem;
104-
}
108+
for (i = 0; i < nr; i++)
109+
clear_highpage(pages[i]);
105110

106111
return 0;
107-
108-
no_mem:
109-
for (i -= 1; i >= 0; i--)
110-
__free_page(pages[i]);
111-
return -ENOMEM;
112112
}
113113

114-
/*
115-
* Actually do the fill. This requires a few steps:
116-
*
117-
* 1. Allocate a bunch of pages.
118-
*
119-
* 2. Fill the page pool with the allocated pages. We don't want to hold the
120-
* PP lock for too long so this is the only time we hold the PP lock.
121-
*
122-
* 3. Rinse and repeat until we have allocated all the pages we think we need
123-
* or the page pool is full. Since we are not holding the lock for the
124-
* entire fill it is possible that other pages were filled into the pool.
125-
*
126-
* 4. Free any left over pages if the pool is filled before we finish.
127-
*/
128-
static void nvmap_pp_do_background_fill(struct nvmap_page_pool *pool)
114+
static void nvmap_pp_do_background_zero_pages(struct nvmap_page_pool *pool)
129115
{
130-
int err;
131-
u32 pages = 0, nr, i;
132-
gfp_t gfp = GFP_NVMAP | __GFP_NOMEMALLOC |
133-
__GFP_NORETRY | __GFP_NO_KSWAPD;
134-
135-
pages = (u32)atomic_xchg(&bg_pages_to_fill, pages);
136-
137-
if (!pages || !enable_pp)
138-
return;
116+
int i;
117+
struct page *page;
118+
int ret;
139119

140-
/* If this param is set, force zero page allocation. */
141-
if (zero_memory)
142-
gfp |= __GFP_ZERO;
120+
mutex_lock(&pool->lock);
121+
for (i = 0; i < PENDING_PAGES_SIZE; i++) {
122+
page = get_zero_list_page(pool);
123+
if (page == NULL)
124+
break;
125+
pending_pages[i] = page;
126+
}
127+
mutex_unlock(&pool->lock);
143128

144-
do {
145-
nr = min_t(u32, PENDING_PAGES_SIZE, pages);
146-
err = __nvmap_pp_alloc_n_pages(pending_pages, nr, gfp);
147-
if (err) {
148-
pr_info("Failed to alloc %u pages for PP!\n", pages);
149-
return;
150-
}
129+
ret = nvmap_pp_zero_pages(pending_pages, i);
130+
if (ret < 0) {
131+
ret = 0;
132+
goto out;
133+
}
151134

152-
mutex_lock(&pool->lock);
153-
i = __nvmap_page_pool_fill_lots_locked(pool, pending_pages, nr);
154-
mutex_unlock(&pool->lock);
155-
pages -= nr;
156-
} while (pages && i == nr);
135+
mutex_lock(&pool->lock);
136+
ret = __nvmap_page_pool_fill_lots_locked(pool, pending_pages, i);
137+
mutex_unlock(&pool->lock);
157138

158-
for (; i < nr; i++)
159-
__free_page(pending_pages[i]);
139+
out:
140+
for (; ret < i; ret++)
141+
__free_page(pending_pages[ret]);
160142
}
161143

162144
/*
@@ -167,18 +149,20 @@ static void nvmap_pp_do_background_fill(struct nvmap_page_pool *pool)
167149
* not directly seen by userspace. Of course if the page pools are empty user
168150
* space will suffer.
169151
*/
170-
static int nvmap_background_zero_allocator(void *arg)
152+
static int nvmap_background_zero_thread(void *arg)
171153
{
172154
struct nvmap_page_pool *pool = &nvmap_dev->pool;
173155
struct sched_param param = { .sched_priority = 0 };
174156

175-
pr_info("PP alloc thread starting.\n");
157+
pr_info("PP zeroing thread starting.\n");
176158

177159
set_freezable();
178160
sched_setscheduler(current, SCHED_IDLE, &param);
179161

180162
while (!kthread_should_stop()) {
181-
nvmap_pp_do_background_fill(pool);
163+
while (nvmap_bg_should_run(pool)) {
164+
nvmap_pp_do_background_zero_pages(pool);
165+
}
182166

183167
wait_event_freezable(nvmap_bg_wait,
184168
nvmap_bg_should_run(pool) ||
@@ -188,44 +172,6 @@ static int nvmap_background_zero_allocator(void *arg)
188172
return 0;
189173
}
190174

191-
/*
192-
* Call this if the background allocator should possibly wake up. This function
193-
* will check to make sure its actually a good idea for that to happen before
194-
* waking the allocator up.
195-
*/
196-
static inline void nvmap_pp_wake_up_allocator(void)
197-
{
198-
struct nvmap_page_pool *pool = &nvmap_dev->pool;
199-
struct sysinfo info;
200-
int free_pages, tmp;
201-
202-
if (!enable_pp)
203-
return;
204-
205-
/* Hueristic: if we don't need to prefill explicitly zero'ed memory then
206-
* lots of memory can be placed back in the pools by possible frees.
207-
* Therefor don't fill the pool unless we really need to as we may get
208-
* more memory without needing to alloc pages.
209-
*/
210-
if (!zero_memory && pool->count > NVMAP_PP_ZERO_MEM_FILL_MIN)
211-
return;
212-
213-
if (pool->max - pool->count < NVMAP_PP_DEF_FILL_THRESH)
214-
return;
215-
216-
si_meminfo(&info);
217-
free_pages = (int)info.freeram;
218-
219-
tmp = free_pages - (MIN_AVAILABLE_MB << (20 - PAGE_SHIFT));
220-
if (tmp <= 0)
221-
return;
222-
223-
/* Let the background thread know how much memory to fill. */
224-
atomic_set(&bg_pages_to_fill,
225-
min(tmp, (int)(pool->max - pool->count)));
226-
wake_up_interruptible(&nvmap_bg_wait);
227-
}
228-
229175
/*
230176
* This removes a page from the page pool. If ignore_disable is set, then
231177
* the enable_pp flag is ignored.
@@ -295,7 +241,6 @@ static int __nvmap_page_pool_alloc_lots_locked(struct nvmap_page_pool *pool,
295241
pp_alloc_add(pool, ind);
296242
pp_hit_add(pool, ind);
297243
pp_miss_add(pool, nr - ind);
298-
nvmap_pp_wake_up_allocator();
299244

300245
return ind;
301246
}
@@ -312,33 +257,6 @@ int nvmap_page_pool_alloc_lots(struct nvmap_page_pool *pool,
312257
return ret;
313258
}
314259

315-
/*
316-
* This adds a page to the pool. Returns true if the passed page is added.
317-
* That means if the pool is full this operation will fail.
318-
*/
319-
static bool nvmap_page_pool_fill_locked(struct nvmap_page_pool *pool,
320-
struct page *page)
321-
{
322-
if (!enable_pp)
323-
return false;
324-
325-
if (pool->count >= pool->max)
326-
return false;
327-
328-
/* Sanity check. */
329-
if (IS_ENABLED(CONFIG_NVMAP_PAGE_POOL_DEBUG)) {
330-
atomic_inc(&page->_count);
331-
BUG_ON(atomic_read(&page->_count) != 2);
332-
BUG_ON(pool->count > pool->max);
333-
}
334-
335-
list_add_tail(&page->lru, &pool->page_list);
336-
pool->count++;
337-
pp_fill_add(pool, 1);
338-
339-
return true;
340-
}
341-
342260
/*
343261
* Fill a bunch of pages into the page pool. This will fill as many as it can
344262
* and return the number of pages filled. Pages are used from the start of the
@@ -379,21 +297,23 @@ int nvmap_page_pool_fill_lots(struct nvmap_page_pool *pool,
379297
int ret;
380298

381299
mutex_lock(&pool->lock);
382-
ret = __nvmap_page_pool_fill_lots_locked(pool, pages, nr);
383-
mutex_unlock(&pool->lock);
300+
if (zero_memory) {
301+
int i;
384302

385-
return ret;
386-
}
303+
nr = min(nr, pool->max - pool->count - pool->to_zero);
387304

388-
bool nvmap_page_pool_fill(struct nvmap_page_pool *pool, struct page *page)
389-
{
390-
bool ret = false;
305+
for (i = 0; i < nr; i++) {
306+
list_add_tail(&pages[i]->lru, &pool->zero_list);
307+
pool->to_zero++;
308+
}
391309

392-
if (pool) {
393-
mutex_lock(&pool->lock);
394-
ret = nvmap_page_pool_fill_locked(pool, page);
395-
mutex_unlock(&pool->lock);
310+
wake_up_interruptible(&nvmap_bg_wait);
311+
312+
ret = i;
313+
} else {
314+
ret = __nvmap_page_pool_fill_lots_locked(pool, pages, nr);
396315
}
316+
mutex_unlock(&pool->lock);
397317

398318
return ret;
399319
}
@@ -413,7 +333,9 @@ static int nvmap_page_pool_free(struct nvmap_page_pool *pool, int nr_free)
413333

414334
mutex_lock(&pool->lock);
415335
while (i) {
416-
page = nvmap_page_pool_alloc_locked(pool, 1);
336+
page = get_zero_list_page(pool);
337+
if (!page)
338+
page = nvmap_page_pool_alloc_locked(pool, 1);
417339
if (!page)
418340
break;
419341
__free_page(page);
@@ -431,7 +353,7 @@ ulong nvmap_page_pool_get_unused_pages(void)
431353
if (!nvmap_dev)
432354
return 0;
433355

434-
total = nvmap_dev->pool.count;
356+
total = nvmap_dev->pool.count + nvmap_dev->pool.to_zero;
435357

436358
return total;
437359
}
@@ -450,14 +372,18 @@ int nvmap_page_pool_clear(void)
450372
while ((page = nvmap_page_pool_alloc_locked(pool, 1)) != NULL)
451373
__free_page(page);
452374

375+
while (!list_empty(&pool->zero_list)) {
376+
page = get_zero_list_page(pool);
377+
__free_page(page);
378+
}
379+
453380
/* For some reason, if an error occured... */
454-
if (!list_empty(&pool->page_list)) {
381+
if (!list_empty(&pool->page_list) || !list_empty(&pool->zero_list)) {
455382
mutex_unlock(&pool->lock);
456383
return -ENOMEM;
457384
}
458385

459386
mutex_unlock(&pool->lock);
460-
nvmap_pp_wake_up_allocator();
461387

462388
return 0;
463389
}
@@ -610,6 +536,9 @@ int nvmap_page_pool_debugfs_init(struct dentry *nvmap_root)
610536
debugfs_create_u32("page_pool_available_pages",
611537
S_IRUGO, pp_root,
612538
&nvmap_dev->pool.count);
539+
debugfs_create_u32("page_pool_pages_to_zero",
540+
S_IRUGO, pp_root,
541+
&nvmap_dev->pool.to_zero);
613542
#ifdef CONFIG_NVMAP_PAGE_POOL_DEBUG
614543
debugfs_create_u64("page_pool_allocs",
615544
S_IRUGO, pp_root,
@@ -630,14 +559,14 @@ int nvmap_page_pool_debugfs_init(struct dentry *nvmap_root)
630559

631560
int nvmap_page_pool_init(struct nvmap_device *dev)
632561
{
633-
static int reg = 1;
634562
unsigned long totalram_mb;
635563
struct sysinfo info;
636564
struct nvmap_page_pool *pool = &dev->pool;
637565

638566
memset(pool, 0x0, sizeof(*pool));
639567
mutex_init(&pool->lock);
640568
INIT_LIST_HEAD(&pool->page_list);
569+
INIT_LIST_HEAD(&pool->zero_list);
641570

642571
si_meminfo(&info);
643572
totalram_mb = (info.totalram * info.mem_unit) >> 20;
@@ -656,16 +585,13 @@ int nvmap_page_pool_init(struct nvmap_device *dev)
656585
pr_info("nvmap page pool size: %u pages (%u MB)\n", pool->max,
657586
(pool->max * info.mem_unit) >> 20);
658587

659-
if (reg) {
660-
reg = 0;
661-
register_shrinker(&nvmap_page_pool_shrinker);
662-
}
663-
664-
background_allocator = kthread_create(nvmap_background_zero_allocator,
588+
background_allocator = kthread_run(nvmap_background_zero_thread,
665589
NULL, "nvmap-bz");
666590
if (IS_ERR_OR_NULL(background_allocator))
667591
goto fail;
668592

593+
register_shrinker(&nvmap_page_pool_shrinker);
594+
669595
return 0;
670596
fail:
671597
nvmap_page_pool_fini(dev);

drivers/video/tegra/nvmap/nvmap_priv.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,6 @@ struct nvmap_page_pool {
188188
int nvmap_page_pool_init(struct nvmap_device *dev);
189189
int nvmap_page_pool_fini(struct nvmap_device *dev);
190190
struct page *nvmap_page_pool_alloc(struct nvmap_page_pool *pool);
191-
bool nvmap_page_pool_fill(struct nvmap_page_pool *pool, struct page *page);
192191
int nvmap_page_pool_alloc_lots(struct nvmap_page_pool *pool,
193192
struct page **pages, u32 nr);
194193
int nvmap_page_pool_fill_lots(struct nvmap_page_pool *pool,

0 commit comments

Comments
 (0)