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