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/debugfs.h>
   9#include <linux/dma-mapping.h>
  10#include <linux/err.h>
  11#include <linux/fs.h>
  12#include <linux/list.h>
  13#include <linux/init.h>
  14#include <linux/slab.h>
  15#include <linux/swap.h>
  16
  17#include "ion.h"
  18
  19static void *ion_page_pool_alloc_pages(struct ion_page_pool *pool)
  20{
  21        struct page *page = alloc_pages(pool->gfp_mask, pool->order);
  22
  23        if (!page)
  24                return NULL;
  25        return page;
  26}
  27
  28static void ion_page_pool_free_pages(struct ion_page_pool *pool,
  29                                     struct page *page)
  30{
  31        __free_pages(page, pool->order);
  32}
  33
  34static int ion_page_pool_add(struct ion_page_pool *pool, struct page *page)
  35{
  36        mutex_lock(&pool->mutex);
  37        if (PageHighMem(page)) {
  38                list_add_tail(&page->lru, &pool->high_items);
  39                pool->high_count++;
  40        } else {
  41                list_add_tail(&page->lru, &pool->low_items);
  42                pool->low_count++;
  43        }
  44        mutex_unlock(&pool->mutex);
  45        return 0;
  46}
  47
  48static struct page *ion_page_pool_remove(struct ion_page_pool *pool, bool high)
  49{
  50        struct page *page;
  51
  52        if (high) {
  53                BUG_ON(!pool->high_count);
  54                page = list_first_entry(&pool->high_items, struct page, lru);
  55                pool->high_count--;
  56        } else {
  57                BUG_ON(!pool->low_count);
  58                page = list_first_entry(&pool->low_items, struct page, lru);
  59                pool->low_count--;
  60        }
  61
  62        list_del(&page->lru);
  63        return page;
  64}
  65
  66struct page *ion_page_pool_alloc(struct ion_page_pool *pool)
  67{
  68        struct page *page = NULL;
  69
  70        BUG_ON(!pool);
  71
  72        mutex_lock(&pool->mutex);
  73        if (pool->high_count)
  74                page = ion_page_pool_remove(pool, true);
  75        else if (pool->low_count)
  76                page = ion_page_pool_remove(pool, false);
  77        mutex_unlock(&pool->mutex);
  78
  79        if (!page)
  80                page = ion_page_pool_alloc_pages(pool);
  81
  82        return page;
  83}
  84
  85void ion_page_pool_free(struct ion_page_pool *pool, struct page *page)
  86{
  87        int ret;
  88
  89        BUG_ON(pool->order != compound_order(page));
  90
  91        ret = ion_page_pool_add(pool, page);
  92        if (ret)
  93                ion_page_pool_free_pages(pool, page);
  94}
  95
  96static int ion_page_pool_total(struct ion_page_pool *pool, bool high)
  97{
  98        int count = pool->low_count;
  99
 100        if (high)
 101                count += pool->high_count;
 102
 103        return count << pool->order;
 104}
 105
 106int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask,
 107                         int nr_to_scan)
 108{
 109        int freed = 0;
 110        bool high;
 111
 112        if (current_is_kswapd())
 113                high = true;
 114        else
 115                high = !!(gfp_mask & __GFP_HIGHMEM);
 116
 117        if (nr_to_scan == 0)
 118                return ion_page_pool_total(pool, high);
 119
 120        while (freed < nr_to_scan) {
 121                struct page *page;
 122
 123                mutex_lock(&pool->mutex);
 124                if (pool->low_count) {
 125                        page = ion_page_pool_remove(pool, false);
 126                } else if (high && pool->high_count) {
 127                        page = ion_page_pool_remove(pool, true);
 128                } else {
 129                        mutex_unlock(&pool->mutex);
 130                        break;
 131                }
 132                mutex_unlock(&pool->mutex);
 133                ion_page_pool_free_pages(pool, page);
 134                freed += (1 << pool->order);
 135        }
 136
 137        return freed;
 138}
 139
 140struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order,
 141                                           bool cached)
 142{
 143        struct ion_page_pool *pool = kmalloc(sizeof(*pool), GFP_KERNEL);
 144
 145        if (!pool)
 146                return NULL;
 147        pool->high_count = 0;
 148        pool->low_count = 0;
 149        INIT_LIST_HEAD(&pool->low_items);
 150        INIT_LIST_HEAD(&pool->high_items);
 151        pool->gfp_mask = gfp_mask | __GFP_COMP;
 152        pool->order = order;
 153        mutex_init(&pool->mutex);
 154        plist_node_init(&pool->list, order);
 155        if (cached)
 156                pool->cached = true;
 157
 158        return pool;
 159}
 160
 161void ion_page_pool_destroy(struct ion_page_pool *pool)
 162{
 163        kfree(pool);
 164}
 165
 166static int __init ion_page_pool_init(void)
 167{
 168        return 0;
 169}
 170device_initcall(ion_page_pool_init);
 171