| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * Copyright (C) 2018-2020 Oplus. All rights reserved. |
| */ |
| |
| #include <linux/stddef.h> |
| #include <linux/mm.h> |
| #include <linux/swap.h> |
| #include <linux/interrupt.h> |
| #include <linux/pagemap.h> |
| #include <linux/jiffies.h> |
| #include <linux/bootmem.h> |
| #include <linux/memblock.h> |
| #include <linux/compiler.h> |
| #include <linux/kernel.h> |
| #include <linux/kasan.h> |
| #include <linux/module.h> |
| #include <linux/suspend.h> |
| #include <linux/pagevec.h> |
| #include <linux/blkdev.h> |
| #include <linux/slab.h> |
| #include <linux/ratelimit.h> |
| #include <linux/oom.h> |
| #include <linux/topology.h> |
| #include <linux/sysctl.h> |
| #include <linux/cpu.h> |
| #include <linux/cpuset.h> |
| #include <linux/memory_hotplug.h> |
| #include <linux/nodemask.h> |
| #include <linux/vmalloc.h> |
| #include <linux/vmstat.h> |
| #include <linux/mempolicy.h> |
| #include <linux/memremap.h> |
| #include <linux/stop_machine.h> |
| #include <linux/sort.h> |
| #include <linux/pfn.h> |
| #include <linux/backing-dev.h> |
| #include <linux/fault-inject.h> |
| #include <linux/page-isolation.h> |
| #include <linux/page_ext.h> |
| #include <linux/debugobjects.h> |
| #include <linux/kmemleak.h> |
| #include <linux/compaction.h> |
| #include <trace/events/kmem.h> |
| #include <trace/events/oom.h> |
| #include <linux/prefetch.h> |
| #include <linux/mm_inline.h> |
| #include <linux/migrate.h> |
| #include <linux/hugetlb.h> |
| #include <linux/sched/rt.h> |
| #include <linux/sched/mm.h> |
| #include <linux/page_owner.h> |
| #include <linux/kthread.h> |
| #include <linux/memcontrol.h> |
| #include <linux/ftrace.h> |
| #include <linux/lockdep.h> |
| #include <linux/nmi.h> |
| #include <linux/psi.h> |
| #include <asm/sections.h> |
| #include <asm/tlbflush.h> |
| #include <asm/div64.h> |
| #include <linux/fs.h> |
| #include <linux/err.h> |
| #include <linux/cpu.h> |
| #include <linux/cpumask.h> |
| #include <linux/vmstat.h> |
| #include <linux/proc_fs.h> |
| #include <linux/seq_file.h> |
| #include <linux/debugfs.h> |
| #include <linux/math64.h> |
| #include <linux/writeback.h> |
| #include "internal.h" |
| |
| #ifdef VENDOR_EDIT |
| #include "multi_freearea.h" |
| #endif |
| |
| |
| #include "internal.h" |
| |
| static unsigned int show_order = 0; |
| #define SHOW_ALL (11) |
| |
| static char * const zone_names[MAX_NR_ZONES] = { |
| #ifdef CONFIG_ZONE_DMA |
| "DMA", |
| #endif |
| #ifdef CONFIG_ZONE_DMA32 |
| "DMA32", |
| #endif |
| "Normal", |
| #ifdef CONFIG_HIGHMEM |
| "HighMem", |
| #endif |
| "Movable", |
| #ifdef CONFIG_ZONE_DEVICE |
| "Device", |
| #endif |
| }; |
| |
| static int proc_free_area_show(struct seq_file *m, void *p) |
| { |
| unsigned int order, t, flc; |
| pg_data_t *pgdat = NODE_DATA(0); |
| struct page *page; |
| int zone_type; |
| |
| |
| for (zone_type = 0; zone_type < MAX_NR_ZONES; zone_type++) { |
| struct zone *zone = &pgdat->node_zones[zone_type]; |
| |
| if (!managed_zone(zone)) { |
| continue; |
| } |
| seq_printf(m, "---------------------------------------------------------------------------------------------------------------\n"); |
| seq_printf(m, "zone_name = %s, show_order = %u\n", zone_names[zone_type], show_order); |
| for (flc = 0; flc < FREE_AREA_COUNTS; flc++) |
| seq_printf(m, "[%d]: label = %lu, segment = %lu\n", flc, zone->zone_label[flc].label, zone->zone_label[flc].segment); |
| seq_printf(m, "\n---------------------------------------------------------------------------------------------------------------\n"); |
| for (flc = 0; flc < FREE_AREA_COUNTS; flc++) { |
| seq_printf(m, "flc = %u\n", flc); |
| for_each_migratetype_order(order, t) { |
| if (order == show_order || show_order == SHOW_ALL) { |
| seq_printf(m, "order = %u, mt = %u\n", order, t); |
| list_for_each_entry(page, &(zone->free_area[flc][order].free_list[t]), lru) { |
| seq_printf(m, "%lu\t", page_to_pfn(page)); |
| } |
| seq_printf(m, "\n"); |
| } |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| static ssize_t proc_free_area_write(struct file *file, const char __user *buff, size_t len, loff_t *ppos) |
| { |
| char write_data[16] = {0}; |
| int ret = 0; |
| |
| if (copy_from_user(&write_data, buff, len)) { |
| return -EFAULT; |
| } |
| ret = kstrtouint(write_data, 10, &show_order); |
| |
| return len; |
| } |
| |
| static int proc_free_area_open(struct inode *inode, struct file *file) |
| { |
| return single_open(file, proc_free_area_show, NULL); |
| } |
| |
| const struct file_operations proc_free_area_fops = { |
| .open = proc_free_area_open, |
| .read = seq_read, |
| .llseek = seq_lseek, |
| .release = single_release, |
| .write = proc_free_area_write, |
| }; |
| |
| |
| void list_sort_add(struct page *page, struct zone *zone, unsigned int order, int mt) |
| { |
| struct list_head *list = &(zone->free_area[0][order].free_list[mt]); |
| unsigned long pfn = 0, segment = 0; |
| int i = 0; |
| |
| pfn = page_to_pfn(page); |
| |
| if (unlikely(pfn > zone->zone_label[FREE_AREA_COUNTS - 1].label)) { |
| list = &(zone->free_area[FREE_AREA_COUNTS - 1][order].free_list[mt]); |
| segment = zone->zone_label[FREE_AREA_COUNTS - 1].segment; |
| goto add_page; |
| } |
| |
| for (i = 0; i < FREE_AREA_COUNTS; i++) { |
| if (pfn <= zone->zone_label[i].label) { |
| list = &(zone->free_area[i][order].free_list[mt]); |
| segment = zone->zone_label[i].segment; |
| break; |
| } |
| } |
| |
| add_page: |
| if (pfn >= segment) |
| list_add_tail(&page->lru, list); |
| else |
| list_add(&page->lru, list); |
| } |
| |
| int page_to_flc(struct page *page) |
| { |
| struct zone *zone = page_zone(page); |
| unsigned long pfn = page_to_pfn(page); |
| int flc = 0; |
| |
| if (unlikely(pfn > zone->zone_label[FREE_AREA_COUNTS - 1].label)) |
| return FREE_AREA_COUNTS - 1; |
| |
| for (flc = 0; flc < FREE_AREA_COUNTS; flc++) { |
| if (pfn <= zone->zone_label[flc].label) |
| return flc; |
| } |
| |
| return flc; |
| } |
| |
| void ajust_zone_label(struct zone *zone) |
| { |
| int i; |
| unsigned long prev_base; |
| |
| for (i = 0; i < FREE_AREA_COUNTS; i++) { |
| zone->zone_label[i].label = zone->zone_start_pfn + zone->spanned_pages * (i + 1) / FREE_AREA_COUNTS; |
| } |
| |
| for (i = 0; i < FREE_AREA_COUNTS; i++) { |
| if (i == 0) |
| prev_base = zone->zone_start_pfn; |
| else |
| prev_base = zone->zone_label[i - 1].label; |
| |
| zone->zone_label[i].segment = prev_base + |
| ((zone->zone_label[i].label - prev_base) >> 1); |
| } |
| } |
| |
| unsigned int ajust_flc(unsigned int current_flc, unsigned int order) |
| { |
| /* when alloc_order >= HIGH_ORDER_TO_FLC, |
| * we like to alloc in free_area: 4->3->2->1 |
| */ |
| if (order >= HIGH_ORDER_TO_FLC) |
| return (FREE_AREA_COUNTS - 1 - current_flc); |
| |
| return current_flc; |
| } |
| |
| |