linux/mm/cma_debug.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * CMA DebugFS Interface
   4 *
   5 * Copyright (c) 2015 Sasha Levin <sasha.levin@oracle.com>
   6 */
   7
   8
   9#include <linux/debugfs.h>
  10#include <linux/cma.h>
  11#include <linux/list.h>
  12#include <linux/kernel.h>
  13#include <linux/slab.h>
  14#include <linux/mm_types.h>
  15
  16#include "cma.h"
  17
  18struct cma_mem {
  19        struct hlist_node node;
  20        struct page *p;
  21        unsigned long n;
  22};
  23
  24static struct dentry *cma_debugfs_root;
  25
  26static int cma_debugfs_get(void *data, u64 *val)
  27{
  28        unsigned long *p = data;
  29
  30        *val = *p;
  31
  32        return 0;
  33}
  34DEFINE_SIMPLE_ATTRIBUTE(cma_debugfs_fops, cma_debugfs_get, NULL, "%llu\n");
  35
  36static int cma_used_get(void *data, u64 *val)
  37{
  38        struct cma *cma = data;
  39        unsigned long used;
  40
  41        mutex_lock(&cma->lock);
  42        /* pages counter is smaller than sizeof(int) */
  43        used = bitmap_weight(cma->bitmap, (int)cma_bitmap_maxno(cma));
  44        mutex_unlock(&cma->lock);
  45        *val = (u64)used << cma->order_per_bit;
  46
  47        return 0;
  48}
  49DEFINE_SIMPLE_ATTRIBUTE(cma_used_fops, cma_used_get, NULL, "%llu\n");
  50
  51static int cma_maxchunk_get(void *data, u64 *val)
  52{
  53        struct cma *cma = data;
  54        unsigned long maxchunk = 0;
  55        unsigned long start, end = 0;
  56        unsigned long bitmap_maxno = cma_bitmap_maxno(cma);
  57
  58        mutex_lock(&cma->lock);
  59        for (;;) {
  60                start = find_next_zero_bit(cma->bitmap, bitmap_maxno, end);
  61                if (start >= cma->count)
  62                        break;
  63                end = find_next_bit(cma->bitmap, bitmap_maxno, start);
  64                maxchunk = max(end - start, maxchunk);
  65        }
  66        mutex_unlock(&cma->lock);
  67        *val = (u64)maxchunk << cma->order_per_bit;
  68
  69        return 0;
  70}
  71DEFINE_SIMPLE_ATTRIBUTE(cma_maxchunk_fops, cma_maxchunk_get, NULL, "%llu\n");
  72
  73static void cma_add_to_cma_mem_list(struct cma *cma, struct cma_mem *mem)
  74{
  75        spin_lock(&cma->mem_head_lock);
  76        hlist_add_head(&mem->node, &cma->mem_head);
  77        spin_unlock(&cma->mem_head_lock);
  78}
  79
  80static struct cma_mem *cma_get_entry_from_list(struct cma *cma)
  81{
  82        struct cma_mem *mem = NULL;
  83
  84        spin_lock(&cma->mem_head_lock);
  85        if (!hlist_empty(&cma->mem_head)) {
  86                mem = hlist_entry(cma->mem_head.first, struct cma_mem, node);
  87                hlist_del_init(&mem->node);
  88        }
  89        spin_unlock(&cma->mem_head_lock);
  90
  91        return mem;
  92}
  93
  94static int cma_free_mem(struct cma *cma, int count)
  95{
  96        struct cma_mem *mem = NULL;
  97
  98        while (count) {
  99                mem = cma_get_entry_from_list(cma);
 100                if (mem == NULL)
 101                        return 0;
 102
 103                if (mem->n <= count) {
 104                        cma_release(cma, mem->p, mem->n);
 105                        count -= mem->n;
 106                        kfree(mem);
 107                } else if (cma->order_per_bit == 0) {
 108                        cma_release(cma, mem->p, count);
 109                        mem->p += count;
 110                        mem->n -= count;
 111                        count = 0;
 112                        cma_add_to_cma_mem_list(cma, mem);
 113                } else {
 114                        pr_debug("cma: cannot release partial block when order_per_bit != 0\n");
 115                        cma_add_to_cma_mem_list(cma, mem);
 116                        break;
 117                }
 118        }
 119
 120        return 0;
 121
 122}
 123
 124static int cma_free_write(void *data, u64 val)
 125{
 126        int pages = val;
 127        struct cma *cma = data;
 128
 129        return cma_free_mem(cma, pages);
 130}
 131DEFINE_SIMPLE_ATTRIBUTE(cma_free_fops, NULL, cma_free_write, "%llu\n");
 132
 133static int cma_alloc_mem(struct cma *cma, int count)
 134{
 135        struct cma_mem *mem;
 136        struct page *p;
 137
 138        mem = kzalloc(sizeof(*mem), GFP_KERNEL);
 139        if (!mem)
 140                return -ENOMEM;
 141
 142        p = cma_alloc(cma, count, 0, false);
 143        if (!p) {
 144                kfree(mem);
 145                return -ENOMEM;
 146        }
 147
 148        mem->p = p;
 149        mem->n = count;
 150
 151        cma_add_to_cma_mem_list(cma, mem);
 152
 153        return 0;
 154}
 155
 156static int cma_alloc_write(void *data, u64 val)
 157{
 158        int pages = val;
 159        struct cma *cma = data;
 160
 161        return cma_alloc_mem(cma, pages);
 162}
 163DEFINE_SIMPLE_ATTRIBUTE(cma_alloc_fops, NULL, cma_alloc_write, "%llu\n");
 164
 165static void cma_debugfs_add_one(struct cma *cma, int idx)
 166{
 167        struct dentry *tmp;
 168        char name[16];
 169        int u32s;
 170
 171        scnprintf(name, sizeof(name), "cma-%s", cma->name);
 172
 173        tmp = debugfs_create_dir(name, cma_debugfs_root);
 174
 175        debugfs_create_file("alloc", 0200, tmp, cma, &cma_alloc_fops);
 176        debugfs_create_file("free", 0200, tmp, cma, &cma_free_fops);
 177        debugfs_create_file("base_pfn", 0444, tmp,
 178                            &cma->base_pfn, &cma_debugfs_fops);
 179        debugfs_create_file("count", 0444, tmp, &cma->count, &cma_debugfs_fops);
 180        debugfs_create_file("order_per_bit", 0444, tmp,
 181                            &cma->order_per_bit, &cma_debugfs_fops);
 182        debugfs_create_file("used", 0444, tmp, cma, &cma_used_fops);
 183        debugfs_create_file("maxchunk", 0444, tmp, cma, &cma_maxchunk_fops);
 184
 185        u32s = DIV_ROUND_UP(cma_bitmap_maxno(cma), BITS_PER_BYTE * sizeof(u32));
 186        debugfs_create_u32_array("bitmap", 0444, tmp, (u32 *)cma->bitmap, u32s);
 187}
 188
 189static int __init cma_debugfs_init(void)
 190{
 191        int i;
 192
 193        cma_debugfs_root = debugfs_create_dir("cma", NULL);
 194        if (!cma_debugfs_root)
 195                return -ENOMEM;
 196
 197        for (i = 0; i < cma_area_count; i++)
 198                cma_debugfs_add_one(&cma_areas[i], i);
 199
 200        return 0;
 201}
 202late_initcall(cma_debugfs_init);
 203