linux/drivers/staging/android/ion/ion_page_pool.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * drivers/staging/android/ion/ion_mem_pool.c
   4 *
   5 * Copyright (C) 2011 Google, Inc.
   6 */
   7
   8#include <linux/list.h>
   9#include <linux/slab.h>
  10#include <linux/swap.h>
  11
  12#include "ion.h"
  13
  14static inline struct page *ion_page_pool_alloc_pages(struct ion_page_pool *pool)
  15{
  16        return alloc_pages(pool->gfp_mask, pool->order);
  17}
  18
  19static void ion_page_pool_free_pages(struct ion_page_pool *pool,
  20                                     struct page *page)
  21{
  22        __free_pages(page, pool->order);
  23}
  24
  25static void ion_page_pool_add(struct ion_page_pool *pool, struct page *page)
  26{
  27        mutex_lock(&pool->mutex);
  28        if (PageHighMem(page)) {
  29                list_add_tail(&page->lru, &pool->high_items);
  30                pool->high_count++;
  31        } else {
  32                list_add_tail(&page->lru, &pool->low_items);
  33                pool->low_count++;
  34        }
  35        mutex_unlock(&pool->mutex);
  36}
  37
  38static struct page *ion_page_pool_remove(struct ion_page_pool *pool, bool high)
  39{
  40        struct page *page;
  41
  42        if (high) {
  43                BUG_ON(!pool->high_count);
  44                page = list_first_entry(&pool->high_items, struct page, lru);
  45                pool->high_count--;
  46        } else {
  47                BUG_ON(!pool->low_count);
  48                page = list_first_entry(&pool->low_items, struct page, lru);
  49                pool->low_count--;
  50        }
  51
  52        list_del(&page->lru);
  53        return page;
  54}
  55
  56struct page *ion_page_pool_alloc(struct ion_page_pool *pool)
  57{
  58        struct page *page = NULL;
  59
  60        BUG_ON(!pool);
  61
  62        mutex_lock(&pool->mutex);
  63        if (pool->high_count)
  64                page = ion_page_pool_remove(pool, true);
  65        else if (pool->low_count)
  66                page = ion_page_pool_remove(pool, false);
  67        mutex_unlock(&pool->mutex);
  68
  69        if (!page)
  70                page = ion_page_pool_alloc_pages(pool);
  71
  72        return page;
  73}
  74
  75void ion_page_pool_free(struct ion_page_pool *pool, struct page *page)
  76{
  77        BUG_ON(pool->order != compound_order(page));
  78
  79        ion_page_pool_add(pool, page);
  80}
  81
  82static int ion_page_pool_total(struct ion_page_pool *pool, bool high)
  83{
  84        int count = pool->low_count;
  85
  86        if (high)
  87                count += pool->high_count;
  88
  89        return count << pool->order;
  90}
  91
  92int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask,
  93                         int nr_to_scan)
  94{
  95        int freed = 0;
  96        bool high;
  97
  98        if (current_is_kswapd())
  99                high = true;
 100        else
 101                high = !!(gfp_mask & __GFP_HIGHMEM);
 102
 103        if (nr_to_scan == 0)
 104                return ion_page_pool_total(pool, high);
 105
 106        while (freed < nr_to_scan) {
 107                struct page *page;
 108
 109                mutex_lock(&pool->mutex);
 110                if (pool->low_count) {
 111                        page = ion_page_pool_remove(pool, false);
 112                } else if (high && pool->high_count) {
 113                        page = ion_page_pool_remove(pool, true);
 114                } else {
 115                        mutex_unlock(&pool->mutex);
 116                        break;
 117                }
 118                mutex_unlock(&pool->mutex);
 119                ion_page_pool_free_pages(pool, page);
 120                freed += (1 << pool->order);
 121        }
 122
 123        return freed;
 124}
 125
 126struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order)
 127{
 128        struct ion_page_pool *pool = kmalloc(sizeof(*pool), GFP_KERNEL);
 129
 130        if (!pool)
 131                return NULL;
 132        pool->high_count = 0;
 133        pool->low_count = 0;
 134        INIT_LIST_HEAD(&pool->low_items);
 135        INIT_LIST_HEAD(&pool->high_items);
 136        pool->gfp_mask = gfp_mask | __GFP_COMP;
 137        pool->order = order;
 138        mutex_init(&pool->mutex);
 139        plist_node_init(&pool->list, order);
 140
 141        return pool;
 142}
 143
 144void ion_page_pool_destroy(struct ion_page_pool *pool)
 145{
 146        kfree(pool);
 147}
 148