net: hns3: add query basic info support for VF
[linux-2.6-microblaze.git] / mm / huge_memory.c
index 9b31ef8..63ed6b2 100644 (file)
@@ -7,6 +7,7 @@
 
 #include <linux/mm.h>
 #include <linux/sched.h>
+#include <linux/sched/mm.h>
 #include <linux/sched/coredump.h>
 #include <linux/sched/numa_balancing.h>
 #include <linux/highmem.h>
@@ -1791,8 +1792,8 @@ bool move_huge_pmd(struct vm_area_struct *vma, unsigned long old_addr,
 /*
  * Returns
  *  - 0 if PMD could not be locked
- *  - 1 if PMD was locked but protections unchange and TLB flush unnecessary
- *  - HPAGE_PMD_NR is protections changed and TLB flush necessary
+ *  - 1 if PMD was locked but protections unchanged and TLB flush unnecessary
+ *  - HPAGE_PMD_NR if protections changed and TLB flush necessary
  */
 int change_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
                unsigned long addr, pgprot_t newprot, unsigned long cp_flags)
@@ -2468,7 +2469,7 @@ static void __split_huge_page(struct page *page, struct list_head *list,
                xa_lock(&swap_cache->i_pages);
        }
 
-       /* lock lru list/PageCompound, ref freezed by page_ref_freeze */
+       /* lock lru list/PageCompound, ref frozen by page_ref_freeze */
        lruvec = lock_page_lruvec(head);
 
        for (i = nr - 1; i >= 1; i--) {
@@ -2829,8 +2830,8 @@ void deferred_split_huge_page(struct page *page)
                ds_queue->split_queue_len++;
 #ifdef CONFIG_MEMCG
                if (memcg)
-                       memcg_set_shrinker_bit(memcg, page_to_nid(page),
-                                              deferred_split_shrinker.id);
+                       set_shrinker_bit(memcg, page_to_nid(page),
+                                        deferred_split_shrinker.id);
 #endif
        }
        spin_unlock_irqrestore(&ds_queue->split_queue_lock, flags);
@@ -2915,16 +2916,14 @@ static struct shrinker deferred_split_shrinker = {
 };
 
 #ifdef CONFIG_DEBUG_FS
-static int split_huge_pages_set(void *data, u64 val)
+static void split_huge_pages_all(void)
 {
        struct zone *zone;
        struct page *page;
        unsigned long pfn, max_zone_pfn;
        unsigned long total = 0, split = 0;
 
-       if (val != 1)
-               return -EINVAL;
-
+       pr_debug("Split all THPs\n");
        for_each_populated_zone(zone) {
                max_zone_pfn = zone_end_pfn(zone);
                for (pfn = zone->zone_start_pfn; pfn < max_zone_pfn; pfn++) {
@@ -2948,15 +2947,243 @@ static int split_huge_pages_set(void *data, u64 val)
                        unlock_page(page);
 next:
                        put_page(page);
+                       cond_resched();
                }
        }
 
-       pr_info("%lu of %lu THP split\n", split, total);
+       pr_debug("%lu of %lu THP split\n", split, total);
+}
 
-       return 0;
+static inline bool vma_not_suitable_for_thp_split(struct vm_area_struct *vma)
+{
+       return vma_is_special_huge(vma) || (vma->vm_flags & VM_IO) ||
+                   is_vm_hugetlb_page(vma);
+}
+
+static int split_huge_pages_pid(int pid, unsigned long vaddr_start,
+                               unsigned long vaddr_end)
+{
+       int ret = 0;
+       struct task_struct *task;
+       struct mm_struct *mm;
+       unsigned long total = 0, split = 0;
+       unsigned long addr;
+
+       vaddr_start &= PAGE_MASK;
+       vaddr_end &= PAGE_MASK;
+
+       /* Find the task_struct from pid */
+       rcu_read_lock();
+       task = find_task_by_vpid(pid);
+       if (!task) {
+               rcu_read_unlock();
+               ret = -ESRCH;
+               goto out;
+       }
+       get_task_struct(task);
+       rcu_read_unlock();
+
+       /* Find the mm_struct */
+       mm = get_task_mm(task);
+       put_task_struct(task);
+
+       if (!mm) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       pr_debug("Split huge pages in pid: %d, vaddr: [0x%lx - 0x%lx]\n",
+                pid, vaddr_start, vaddr_end);
+
+       mmap_read_lock(mm);
+       /*
+        * always increase addr by PAGE_SIZE, since we could have a PTE page
+        * table filled with PTE-mapped THPs, each of which is distinct.
+        */
+       for (addr = vaddr_start; addr < vaddr_end; addr += PAGE_SIZE) {
+               struct vm_area_struct *vma = find_vma(mm, addr);
+               unsigned int follflags;
+               struct page *page;
+
+               if (!vma || addr < vma->vm_start)
+                       break;
+
+               /* skip special VMA and hugetlb VMA */
+               if (vma_not_suitable_for_thp_split(vma)) {
+                       addr = vma->vm_end;
+                       continue;
+               }
+
+               /* FOLL_DUMP to ignore special (like zero) pages */
+               follflags = FOLL_GET | FOLL_DUMP;
+               page = follow_page(vma, addr, follflags);
+
+               if (IS_ERR(page))
+                       continue;
+               if (!page)
+                       continue;
+
+               if (!is_transparent_hugepage(page))
+                       goto next;
+
+               total++;
+               if (!can_split_huge_page(compound_head(page), NULL))
+                       goto next;
+
+               if (!trylock_page(page))
+                       goto next;
+
+               if (!split_huge_page(page))
+                       split++;
+
+               unlock_page(page);
+next:
+               put_page(page);
+               cond_resched();
+       }
+       mmap_read_unlock(mm);
+       mmput(mm);
+
+       pr_debug("%lu of %lu THP split\n", split, total);
+
+out:
+       return ret;
+}
+
+static int split_huge_pages_in_file(const char *file_path, pgoff_t off_start,
+                               pgoff_t off_end)
+{
+       struct filename *file;
+       struct file *candidate;
+       struct address_space *mapping;
+       int ret = -EINVAL;
+       pgoff_t index;
+       int nr_pages = 1;
+       unsigned long total = 0, split = 0;
+
+       file = getname_kernel(file_path);
+       if (IS_ERR(file))
+               return ret;
+
+       candidate = file_open_name(file, O_RDONLY, 0);
+       if (IS_ERR(candidate))
+               goto out;
+
+       pr_debug("split file-backed THPs in file: %s, page offset: [0x%lx - 0x%lx]\n",
+                file_path, off_start, off_end);
+
+       mapping = candidate->f_mapping;
+
+       for (index = off_start; index < off_end; index += nr_pages) {
+               struct page *fpage = pagecache_get_page(mapping, index,
+                                               FGP_ENTRY | FGP_HEAD, 0);
+
+               nr_pages = 1;
+               if (xa_is_value(fpage) || !fpage)
+                       continue;
+
+               if (!is_transparent_hugepage(fpage))
+                       goto next;
+
+               total++;
+               nr_pages = thp_nr_pages(fpage);
+
+               if (!trylock_page(fpage))
+                       goto next;
+
+               if (!split_huge_page(fpage))
+                       split++;
+
+               unlock_page(fpage);
+next:
+               put_page(fpage);
+               cond_resched();
+       }
+
+       filp_close(candidate, NULL);
+       ret = 0;
+
+       pr_debug("%lu of %lu file-backed THP split\n", split, total);
+out:
+       putname(file);
+       return ret;
 }
-DEFINE_DEBUGFS_ATTRIBUTE(split_huge_pages_fops, NULL, split_huge_pages_set,
-               "%llu\n");
+
+#define MAX_INPUT_BUF_SZ 255
+
+static ssize_t split_huge_pages_write(struct file *file, const char __user *buf,
+                               size_t count, loff_t *ppops)
+{
+       static DEFINE_MUTEX(split_debug_mutex);
+       ssize_t ret;
+       /* hold pid, start_vaddr, end_vaddr or file_path, off_start, off_end */
+       char input_buf[MAX_INPUT_BUF_SZ];
+       int pid;
+       unsigned long vaddr_start, vaddr_end;
+
+       ret = mutex_lock_interruptible(&split_debug_mutex);
+       if (ret)
+               return ret;
+
+       ret = -EFAULT;
+
+       memset(input_buf, 0, MAX_INPUT_BUF_SZ);
+       if (copy_from_user(input_buf, buf, min_t(size_t, count, MAX_INPUT_BUF_SZ)))
+               goto out;
+
+       input_buf[MAX_INPUT_BUF_SZ - 1] = '\0';
+
+       if (input_buf[0] == '/') {
+               char *tok;
+               char *buf = input_buf;
+               char file_path[MAX_INPUT_BUF_SZ];
+               pgoff_t off_start = 0, off_end = 0;
+               size_t input_len = strlen(input_buf);
+
+               tok = strsep(&buf, ",");
+               if (tok) {
+                       strncpy(file_path, tok, MAX_INPUT_BUF_SZ);
+               } else {
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               ret = sscanf(buf, "0x%lx,0x%lx", &off_start, &off_end);
+               if (ret != 2) {
+                       ret = -EINVAL;
+                       goto out;
+               }
+               ret = split_huge_pages_in_file(file_path, off_start, off_end);
+               if (!ret)
+                       ret = input_len;
+
+               goto out;
+       }
+
+       ret = sscanf(input_buf, "%d,0x%lx,0x%lx", &pid, &vaddr_start, &vaddr_end);
+       if (ret == 1 && pid == 1) {
+               split_huge_pages_all();
+               ret = strlen(input_buf);
+               goto out;
+       } else if (ret != 3) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ret = split_huge_pages_pid(pid, vaddr_start, vaddr_end);
+       if (!ret)
+               ret = strlen(input_buf);
+out:
+       mutex_unlock(&split_debug_mutex);
+       return ret;
+
+}
+
+static const struct file_operations split_huge_pages_fops = {
+       .owner   = THIS_MODULE,
+       .write   = split_huge_pages_write,
+       .llseek  = no_llseek,
+};
 
 static int __init split_huge_pages_debugfs(void)
 {