| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * Copyright (C) 2018-2020 Oplus. All rights reserved. |
| */ |
| |
| #include <soc/oplus/oppo_healthinfo.h> |
| #include <linux/uaccess.h> |
| #include <linux/kernel.h> |
| #include <linux/kobject.h> |
| #include <linux/ratelimit.h> |
| #include <linux/blkdev.h> |
| #include <linux/cpufreq.h> |
| #include <linux/blkdev.h> |
| #include <linux/ktime.h> |
| #include <linux/seq_file.h> |
| #include <linux/ktime.h> |
| #include <linux/seq_file.h> |
| |
| #ifdef CONFIG_VIRTUAL_RESERVE_MEMORY |
| #include <linux/resmap_account.h> |
| #endif |
| #include <linux/vm_anti_fragment.h> |
| #include <linux/security.h> |
| int vm_fra_op_enabled = 1; |
| |
| static ssize_t vm_fra_op_read(struct file *filp, char __user *buff, size_t count, loff_t *off) |
| { |
| char page[256] = {0}; |
| int len = 0; |
| |
| len = sprintf( page, "%d\n", vm_fra_op_enabled); |
| |
| if (len > *off) { |
| len -= *off; |
| } else { |
| len = 0; |
| } |
| if (copy_to_user(buff, page, (len < count ? len : count))) { |
| return -EFAULT; |
| } |
| *off += len < count ? len : count; |
| return (len < count ? len : count); |
| } |
| |
| static ssize_t vm_fra_op_write(struct file *file, const char __user *buff, size_t len, loff_t *ppos) |
| { |
| char write_data[32] = {0}; |
| int len_tmp = len; |
| |
| if (len > 31) |
| len = 31; |
| |
| if (copy_from_user(&write_data, buff, len)) { |
| ohm_err("write error.\n"); |
| return -EFAULT; |
| } |
| write_data[len] = '\0'; |
| if (write_data[len - 1] == '\n') { |
| write_data[len - 1] = '\0'; |
| } |
| |
| if (0 == strncmp(write_data, "0", 1)) { |
| vm_fra_op_enabled = 0; |
| } else { |
| vm_fra_op_enabled = 1; |
| } |
| return len_tmp; |
| } |
| |
| const struct file_operations vm_fra_op_fops = { |
| .read = vm_fra_op_read, |
| .write = vm_fra_op_write, |
| }; |
| |
| int cpu_oom_event_enable = 1; |
| static unsigned long prev_jiffies; |
| static int prev_pid = -1; |
| static unsigned long rest_size; |
| void trigger_cpu_oom_event(unsigned long len) |
| { |
| int fra = 0, rvma = 0, pid = current->pid; |
| unsigned int tmp = 0, rest = 0, rest_reserve = 0, subtree = 0, subtree_reserve = 0, |
| chunk = 0, chunk_reserve = 0; |
| char *svm_oom_msg = NULL; |
| struct mm_struct *mm = current->mm; |
| struct vm_area_struct *vma = NULL; |
| unsigned long gap_start = 0, gap_end = 0, chunk_start = 0, chunk_start_reserve = 0; |
| |
| if (!cpu_oom_event_enable || !mm) |
| return; |
| |
| if (mm->reserve_vma) |
| rvma = 1; |
| vma = mm->mmap; |
| gap_end = max(PAGE_SIZE, mmap_min_addr); |
| while (vma) { |
| gap_start = vma->vm_start; |
| |
| if (gap_start < gap_end) { |
| vma = vma->vm_next; |
| continue; |
| } |
| |
| tmp = gap_start - gap_end > 0 ? |
| gap_start - gap_end : 0; |
| rest += tmp; |
| |
| if (chunk < tmp) { |
| chunk = tmp; |
| chunk_start = gap_end; |
| } |
| gap_end = vma->vm_end; |
| vma = vma->vm_next; |
| if (tmp >= len) { |
| subtree++; |
| } |
| } |
| if (gap_end < 0xffffffff) { |
| rest += 0xffffffff - gap_end; |
| subtree++; |
| } |
| |
| if (!mm->reserve_vma || !mm->reserve_mmap) |
| goto ignore_reserve_subtree; |
| gap_end = mm->reserve_vma->vm_start; |
| |
| for (vma = mm->reserve_mmap; vma; vma = vma->vm_next) { |
| gap_start = vma->vm_start; |
| |
| if (gap_start < gap_end) |
| continue; |
| |
| if (gap_start >= mm->reserve_vma->vm_start && |
| gap_start <= mm->reserve_vma->vm_end) { |
| |
| tmp = gap_start - gap_end > 0 ? |
| gap_start - gap_end : 0; |
| |
| if (chunk_reserve < tmp) { |
| chunk_reserve = tmp; |
| chunk_start_reserve = gap_end; |
| } |
| rest_reserve += tmp; |
| if (tmp >= len) { |
| subtree_reserve++; |
| } |
| } else { |
| break; |
| } |
| gap_end = vma->vm_end; |
| } |
| |
| if (gap_end < mm->reserve_vma->vm_end) { |
| rest_reserve += mm->reserve_vma->vm_end - gap_end; |
| subtree_reserve++; |
| } |
| |
| ignore_reserve_subtree: |
| if ((rest + rest_reserve > len * 10) && |
| (rest + rest_reserve > HUNDRED_M)) |
| fra = 1; |
| |
| if (pid == prev_pid && time_before(jiffies, prev_jiffies + CPU_OOM_TRIGGER_GAP) |
| && (rest + rest_reserve <= rest_size)) { |
| return; |
| } |
| svm_oom_msg = (char*)kmalloc(128, GFP_KERNEL); |
| if (!svm_oom_msg) |
| return; |
| len = snprintf(svm_oom_msg, 127, |
| "{cpu_oom: len: %lu,pid: %d,type: %s,%u,%u,%u,%u,%d}", |
| len, pid, |
| (fra ? "fra" : "no_fra"), |
| rest, subtree, rest_reserve, subtree_reserve, rvma); |
| svm_oom_msg[len] = '\0'; |
| |
| #ifdef CONFIG_OPPO_HEALTHINFO |
| ohm_action_trig_with_msg(OHM_MEM_VMA_ALLOC_ERR, svm_oom_msg); |
| #endif |
| |
| prev_pid = current->pid; |
| rest_size = rest + rest_reserve; |
| prev_jiffies = jiffies; |
| kfree(svm_oom_msg); |
| return; |
| } |
| |
| int vm_search_two_way = 2; |
| ssize_t vm_search_two_way_op_read(struct file *filp, char __user *buff, size_t count, loff_t *off) |
| { |
| char page[256] = {0}; |
| int len = 0; |
| |
| len = sprintf( page, "%d\n", vm_search_two_way); |
| |
| if (len > *off) { |
| len -= *off; |
| } else { |
| len = 0; |
| } |
| if (copy_to_user(buff, page, (len < count ? len : count))) { |
| return -EFAULT; |
| } |
| *off += len < count ? len : count; |
| return (len < count ? len : count); |
| } |
| |
| static ssize_t vm_search_two_way_op_write(struct file *file, const char __user *buff, size_t len, loff_t *ppos) |
| { |
| char write_data[32] = {0}; |
| struct task_struct *task = current; |
| struct mm_struct *mm = task->mm; |
| |
| if (len > 31) |
| len = 31; |
| |
| if (!mm) |
| return len; |
| |
| if (copy_from_user(&write_data, buff, len)) { |
| ohm_err("write error.\n"); |
| return -EFAULT; |
| } |
| write_data[len] = '\0'; |
| if (write_data[len - 1] == '\n') { |
| write_data[len - 1] = '\0'; |
| } |
| /* |
| * 0: disable current process vm_search_two_way |
| * 1: enable current process vm_search_two_way |
| * 2: disable vm_search_two_way |
| * 3: enable vm_search_two_way |
| * |
| */ |
| if (0 == strncmp(write_data, "1", 1) && vm_search_two_way == 3 && mm) { |
| mm->vm_search_two_way = true; |
| } else if (0 == strncmp(write_data, "0", 1) && mm) { |
| mm->vm_search_two_way = false; |
| } else if (0 == strncmp(write_data, "3", 1)) { |
| vm_search_two_way = 3; |
| } else if (0 == strncmp(write_data, "2", 1)) { |
| vm_search_two_way = 2; |
| } |
| return len; |
| } |
| |
| const struct file_operations vm_search_two_way_fops = { |
| .read = vm_search_two_way_op_read, |
| .write = vm_search_two_way_op_write, |
| }; |