soc: qcom: Enable pageowner support in minidump
Register and dump pageowner on kernel panic to minidump table. Change-Id: I410fe9a6e875e1485bd2e9d0ad5798015d2ce3e6 Signed-off-by: Vijayanand Jitta <vjitta@codeaurora.org>
This commit is contained in:
parent
38b1718d28
commit
3f17f5a3dc
@ -44,6 +44,8 @@
|
||||
#include <linux/percpu.h>
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/cma.h>
|
||||
#include <linux/dma-contiguous.h>
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_QCOM_DYN_MINIDUMP_STACK
|
||||
@ -111,6 +113,11 @@ struct seq_buf *md_meminfo_seq_buf;
|
||||
|
||||
struct seq_buf *md_slabinfo_seq_buf;
|
||||
|
||||
#ifdef CONFIG_PAGE_OWNER
|
||||
size_t md_pageowner_dump_size = SZ_2M;
|
||||
char *md_pageowner_dump_addr;
|
||||
#endif
|
||||
|
||||
/* Modules information */
|
||||
#ifdef CONFIG_MODULES
|
||||
#define NUM_MD_MODULES 200
|
||||
@ -977,6 +984,10 @@ static int md_panic_handler(struct notifier_block *this,
|
||||
if (md_slabinfo_seq_buf)
|
||||
md_dump_slabinfo();
|
||||
|
||||
#ifdef CONFIG_PAGE_OWNER
|
||||
if (md_pageowner_dump_addr)
|
||||
md_dump_pageowner();
|
||||
#endif
|
||||
md_in_oops_handler = false;
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
@ -1039,6 +1050,139 @@ static int md_register_panic_entries(int num_pages, char *name,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool md_register_memory_dump(int size, char *name)
|
||||
{
|
||||
void *buffer_start;
|
||||
struct page *page;
|
||||
int ret;
|
||||
|
||||
page = cma_alloc(dev_get_cma_area(NULL), size >> PAGE_SHIFT,
|
||||
0, false);
|
||||
|
||||
if (!page) {
|
||||
pr_err("Failed to allocate %s minidump, increase cma size\n",
|
||||
name);
|
||||
return false;
|
||||
}
|
||||
|
||||
buffer_start = page_to_virt(page);
|
||||
ret = md_register_minidump_entry(name, (uintptr_t)buffer_start,
|
||||
virt_to_phys(buffer_start), size);
|
||||
if (ret < 0) {
|
||||
cma_release(dev_get_cma_area(NULL), page, size >> PAGE_SHIFT);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Complete registration before adding enteries */
|
||||
smp_mb();
|
||||
|
||||
#ifdef CONFIG_PAGE_OWNER
|
||||
if (!strcmp(name, "PAGEOWNER"))
|
||||
WRITE_ONCE(md_pageowner_dump_addr, buffer_start);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool md_unregister_memory_dump(char *name)
|
||||
{
|
||||
struct page *page;
|
||||
struct md_region *mdr;
|
||||
struct md_region md_entry;
|
||||
|
||||
mdr = md_get_region(name);
|
||||
if (!mdr) {
|
||||
pr_err("minidump entry for %s not found\n", name);
|
||||
return false;
|
||||
}
|
||||
strlcpy(md_entry.name, mdr->name, sizeof(md_entry.name));
|
||||
md_entry.virt_addr = mdr->virt_addr;
|
||||
md_entry.phys_addr = mdr->phys_addr;
|
||||
md_entry.size = mdr->size;
|
||||
page = virt_to_page(mdr->virt_addr);
|
||||
|
||||
if (msm_minidump_remove_region(&md_entry) < 0)
|
||||
return false;
|
||||
|
||||
cma_release(dev_get_cma_area(NULL), page,
|
||||
(md_entry.size) >> PAGE_SHIFT);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void update_dump_size(char *name, size_t size,
|
||||
char **addr, size_t *dump_size)
|
||||
{
|
||||
if ((*dump_size) == 0) {
|
||||
if (md_register_memory_dump(size * SZ_1M,
|
||||
name)) {
|
||||
*dump_size = size * SZ_1M;
|
||||
pr_info_ratelimited("%s Minidump set to %zd MB size\n",
|
||||
name, size);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (md_unregister_memory_dump(name)) {
|
||||
*addr = NULL;
|
||||
if (size == 0) {
|
||||
*dump_size = 0;
|
||||
pr_info_ratelimited("%s Minidump : disabled\n", name);
|
||||
return;
|
||||
}
|
||||
if (md_register_memory_dump(size * SZ_1M,
|
||||
name)) {
|
||||
*dump_size = size * SZ_1M;
|
||||
pr_info_ratelimited("%s Minidump : set to %zd MB\n",
|
||||
name, size);
|
||||
} else if (md_register_memory_dump(*dump_size,
|
||||
name)) {
|
||||
pr_info_ratelimited("%s Minidump : Fallback to %zd MB\n",
|
||||
name, (*dump_size) / SZ_1M);
|
||||
} else {
|
||||
pr_err_ratelimited("%s Minidump : disabled, Can't fallback to %zd MB,\n",
|
||||
name, (*dump_size) / SZ_1M);
|
||||
*dump_size = 0;
|
||||
}
|
||||
} else {
|
||||
pr_err_ratelimited("Failed to unregister %s Minidump\n", name);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PAGE_OWNER
|
||||
static DEFINE_MUTEX(page_owner_dump_size_lock);
|
||||
|
||||
static ssize_t page_owner_dump_size_write(struct file *file,
|
||||
const char __user *ubuf,
|
||||
size_t count, loff_t *offset)
|
||||
{
|
||||
unsigned long long size;
|
||||
|
||||
if (kstrtoull_from_user(ubuf, count, 0, &size)) {
|
||||
pr_err_ratelimited("Invalid format for size\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
mutex_lock(&page_owner_dump_size_lock);
|
||||
update_dump_size("PAGEOWNER", size,
|
||||
&md_pageowner_dump_addr, &md_pageowner_dump_size);
|
||||
mutex_unlock(&page_owner_dump_size_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t page_owner_dump_size_read(struct file *file, char __user *ubuf,
|
||||
size_t count, loff_t *offset)
|
||||
{
|
||||
char buf[100];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%llu MB\n",
|
||||
md_pageowner_dump_size / SZ_1M);
|
||||
return simple_read_from_buffer(ubuf, count, offset, buf, strlen(buf));
|
||||
}
|
||||
|
||||
static const struct file_operations proc_page_owner_dump_size_ops = {
|
||||
.open = simple_open,
|
||||
.write = page_owner_dump_size_write,
|
||||
.read = page_owner_dump_size_read,
|
||||
};
|
||||
#endif
|
||||
|
||||
static void md_register_panic_data(void)
|
||||
{
|
||||
md_register_panic_entries(MD_RUNQUEUE_PAGES, "KRUNQUEUE",
|
||||
@ -1051,6 +1195,11 @@ static void md_register_panic_data(void)
|
||||
&md_meminfo_seq_buf);
|
||||
md_register_panic_entries(MD_SLABINFO_PAGES, "SLABINFO",
|
||||
&md_slabinfo_seq_buf);
|
||||
if (is_page_owner_enabled()) {
|
||||
md_register_memory_dump(md_pageowner_dump_size, "PAGEOWNER");
|
||||
debugfs_create_file("page_owner_dump_size_mb", 0400, NULL, NULL,
|
||||
&proc_page_owner_dump_size_ops);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MODULES
|
||||
|
@ -72,4 +72,14 @@ extern void minidump_add_trace_event(char *buf, size_t size);
|
||||
#else
|
||||
static inline void minidump_add_trace_event(char *buf, size_t size) {}
|
||||
#endif
|
||||
#ifdef CONFIG_PAGE_OWNER
|
||||
extern size_t md_pageowner_dump_size;
|
||||
extern char *md_pageowner_dump_addr;
|
||||
|
||||
extern bool is_page_owner_enabled(void);
|
||||
extern void md_dump_pageowner(void);
|
||||
#else
|
||||
static inline void md_dump_pageowner(void) {}
|
||||
static inline bool is_page_owner_enabled(void) { return false; }
|
||||
#endif
|
||||
#endif
|
||||
|
338
mm/page_owner.c
338
mm/page_owner.c
@ -12,7 +12,10 @@
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/sched/clock.h>
|
||||
|
||||
#ifdef CONFIG_QCOM_MINIDUMP_PANIC_DUMP
|
||||
#include <soc/qcom/minidump.h>
|
||||
#include <linux/ctype.h>
|
||||
#endif
|
||||
#include "internal.h"
|
||||
|
||||
/*
|
||||
@ -423,6 +426,145 @@ print_page_owner(char __user *buf, size_t count, unsigned long pfn,
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_QCOM_MINIDUMP_PANIC_DUMP
|
||||
|
||||
static unsigned long page_owner_filter = 0xF;
|
||||
static unsigned long page_owner_handles_size = SZ_16K;
|
||||
static int nr_handles;
|
||||
static LIST_HEAD(accounted_call_site_list);
|
||||
static DEFINE_MUTEX(accounted_call_site_lock);
|
||||
struct accounted_call_site {
|
||||
struct list_head list;
|
||||
char name[50];
|
||||
};
|
||||
|
||||
bool is_page_owner_enabled(void)
|
||||
{
|
||||
return page_owner_enabled;
|
||||
}
|
||||
|
||||
static bool found_stack(depot_stack_handle_t handle,
|
||||
char *md_pageowner_dump_addr, char *cur)
|
||||
{
|
||||
int *handles, i;
|
||||
|
||||
handles = (int *) (md_pageowner_dump_addr +
|
||||
md_pageowner_dump_size - page_owner_handles_size);
|
||||
|
||||
for (i = 0; i < nr_handles; i++)
|
||||
if (handle == handles[i])
|
||||
return true;
|
||||
|
||||
if ((handles + nr_handles)
|
||||
< (int *)(md_pageowner_dump_addr +
|
||||
md_pageowner_dump_size)) {
|
||||
handles[nr_handles] = handle;
|
||||
nr_handles += 1;
|
||||
} else {
|
||||
pr_err_ratelimited("Can't stores handles increase page_owner_handles_size\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool check_unaccounted(char *buf, ssize_t count,
|
||||
struct page *page, depot_stack_handle_t handle)
|
||||
{
|
||||
int i, ret = 0;
|
||||
unsigned long *entries;
|
||||
unsigned int nr_entries;
|
||||
struct accounted_call_site *call_site;
|
||||
|
||||
if ((page->flags &
|
||||
((1UL << PG_lru) | (1UL << PG_slab) | (1UL << PG_swapbacked))))
|
||||
return false;
|
||||
|
||||
nr_entries = stack_depot_fetch(handle, &entries);
|
||||
for (i = 0; i < nr_entries; i++) {
|
||||
ret = scnprintf(buf, count, "%pS\n",
|
||||
(void *)entries[i]);
|
||||
if (ret == count)
|
||||
return false;
|
||||
|
||||
mutex_lock(&accounted_call_site_lock);
|
||||
list_for_each_entry(call_site,
|
||||
&accounted_call_site_list, list) {
|
||||
if (strnstr(buf, call_site->name,
|
||||
strlen(buf))) {
|
||||
mutex_unlock(&accounted_call_site_lock);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&accounted_call_site_lock);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
dump_page_owner_md(char *buf, size_t count, unsigned long pfn,
|
||||
struct page *page, struct page_owner *page_owner,
|
||||
depot_stack_handle_t handle)
|
||||
{
|
||||
int i, bit, ret = 0;
|
||||
unsigned long *entries;
|
||||
unsigned int nr_entries;
|
||||
|
||||
if (page_owner_filter == 0xF)
|
||||
goto dump;
|
||||
|
||||
for (bit = 1; page_owner_filter >= bit; bit *= 2) {
|
||||
if (page_owner_filter & bit) {
|
||||
switch (bit) {
|
||||
case 0x1:
|
||||
if (check_unaccounted(buf, count, page, handle))
|
||||
goto dump;
|
||||
break;
|
||||
case 0x2:
|
||||
if (page->flags & (1UL << PG_slab))
|
||||
goto dump;
|
||||
break;
|
||||
case 0x4:
|
||||
if (page->flags & (1UL << PG_swapbacked))
|
||||
goto dump;
|
||||
break;
|
||||
case 0x8:
|
||||
if ((page->flags & (1UL << PG_lru)) &&
|
||||
~(page->flags & (1UL << PG_swapbacked)))
|
||||
goto dump;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bit >= 0x8)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (bit > page_owner_filter)
|
||||
return ret;
|
||||
dump:
|
||||
nr_entries = stack_depot_fetch(handle, &entries);
|
||||
if ((buf > (md_pageowner_dump_addr +
|
||||
md_pageowner_dump_size - page_owner_handles_size))
|
||||
|| !found_stack(handle, md_pageowner_dump_addr, buf)) {
|
||||
ret = scnprintf(buf, count, "%lu %u %u\n",
|
||||
pfn, handle, nr_entries);
|
||||
if (ret == count)
|
||||
goto err;
|
||||
|
||||
for (i = 0; i < nr_entries; i++) {
|
||||
ret += scnprintf(buf + ret, count - ret,
|
||||
"%p\n", (void *)entries[i]);
|
||||
if (ret == count)
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
ret = scnprintf(buf, count, "%lu %u %u\n", pfn, handle, 0);
|
||||
}
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
void __dump_page_owner(struct page *page)
|
||||
{
|
||||
struct page_ext *page_ext = lookup_page_ext(page);
|
||||
@ -486,6 +628,12 @@ read_page_owner(struct file *file, char __user *buf, size_t count, loff_t *ppos)
|
||||
struct page_owner *page_owner;
|
||||
depot_stack_handle_t handle;
|
||||
|
||||
#ifdef CONFIG_QCOM_MINIDUMP_PANIC_DUMP
|
||||
char *addr;
|
||||
ssize_t size;
|
||||
|
||||
addr = md_pageowner_dump_addr;
|
||||
#endif
|
||||
if (!static_branch_unlikely(&page_owner_inited))
|
||||
return -EINVAL;
|
||||
|
||||
@ -496,7 +644,8 @@ read_page_owner(struct file *file, char __user *buf, size_t count, loff_t *ppos)
|
||||
while (!pfn_valid(pfn) && (pfn & (MAX_ORDER_NR_PAGES - 1)) != 0)
|
||||
pfn++;
|
||||
|
||||
drain_all_pages(NULL);
|
||||
if (file)
|
||||
drain_all_pages(NULL);
|
||||
|
||||
/* Find an allocated page */
|
||||
for (; pfn < max_pfn; pfn++) {
|
||||
@ -560,13 +709,34 @@ read_page_owner(struct file *file, char __user *buf, size_t count, loff_t *ppos)
|
||||
/* Record the next PFN to read in the file offset */
|
||||
*ppos = (pfn - min_low_pfn) + 1;
|
||||
|
||||
return print_page_owner(buf, count, pfn, page,
|
||||
if (file) {
|
||||
return print_page_owner(buf, count, pfn, page,
|
||||
page_owner, handle);
|
||||
} else {
|
||||
#ifdef CONFIG_QCOM_MINIDUMP_PANIC_DUMP
|
||||
size = dump_page_owner_md(addr, count, pfn, page,
|
||||
page_owner, handle);
|
||||
if (size == count) {
|
||||
pr_err("pageowner minidump region exhausted\n");
|
||||
return 0;
|
||||
}
|
||||
count -= size;
|
||||
addr += size;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_QCOM_MINIDUMP_PANIC_DUMP
|
||||
void md_dump_pageowner(void)
|
||||
{
|
||||
loff_t k = 0;
|
||||
|
||||
read_page_owner(NULL, NULL, md_pageowner_dump_size, &k);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void init_pages_in_zone(pg_data_t *pgdat, struct zone *zone)
|
||||
{
|
||||
unsigned long pfn = zone->zone_start_pfn;
|
||||
@ -664,6 +834,158 @@ static const struct file_operations proc_page_owner_operations = {
|
||||
.read = read_page_owner,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_QCOM_MINIDUMP_PANIC_DUMP
|
||||
static ssize_t page_owner_filter_write(struct file *file,
|
||||
const char __user *ubuf,
|
||||
size_t count, loff_t *offset)
|
||||
{
|
||||
unsigned long filter;
|
||||
|
||||
if (kstrtoul_from_user(ubuf, count, 0, &filter)) {
|
||||
pr_err_ratelimited("Invalid format for filter\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (filter & (~0xF)) {
|
||||
pr_err_ratelimited("Invalid filter : use following filters or any combinations of these\n"
|
||||
"0x1 - unaccounted\n"
|
||||
"0x2 - slab\n"
|
||||
"0x4 - Anon\n"
|
||||
"0x8 - File\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
page_owner_filter = filter;
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t page_owner_filter_read(struct file *file, char __user *ubuf,
|
||||
size_t count, loff_t *offset)
|
||||
{
|
||||
char buf[64];
|
||||
|
||||
snprintf(buf, sizeof(buf), "0x%lx\n", page_owner_filter);
|
||||
return simple_read_from_buffer(ubuf, count, offset, buf, strlen(buf));
|
||||
}
|
||||
|
||||
static const struct file_operations proc_page_owner_filter_ops = {
|
||||
.open = simple_open,
|
||||
.write = page_owner_filter_write,
|
||||
.read = page_owner_filter_read,
|
||||
};
|
||||
|
||||
static ssize_t page_owner_handle_write(struct file *file,
|
||||
const char __user *ubuf,
|
||||
size_t count, loff_t *offset)
|
||||
{
|
||||
unsigned long size;
|
||||
|
||||
if (kstrtoul_from_user(ubuf, count, 0, &size)) {
|
||||
pr_err_ratelimited("Invalid format for handle size\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (size) {
|
||||
if (size > (md_pageowner_dump_size / SZ_16K)) {
|
||||
pr_err_ratelimited("size : %lu KB exceeds max size : %lu KB\n",
|
||||
size, (md_pageowner_dump_size / SZ_16K));
|
||||
goto err;
|
||||
}
|
||||
page_owner_handles_size = size * SZ_1K;
|
||||
}
|
||||
err:
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t page_owner_handle_read(struct file *file, char __user *ubuf,
|
||||
size_t count, loff_t *offset)
|
||||
{
|
||||
char buf[64];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%lu KB\n",
|
||||
(page_owner_handles_size / SZ_1K));
|
||||
return simple_read_from_buffer(ubuf, count, offset, buf, strlen(buf));
|
||||
}
|
||||
|
||||
static const struct file_operations proc_page_owner_handle_ops = {
|
||||
.open = simple_open,
|
||||
.write = page_owner_handle_write,
|
||||
.read = page_owner_handle_read,
|
||||
};
|
||||
|
||||
static ssize_t page_owner_call_site_write(struct file *file,
|
||||
const char __user *ubuf,
|
||||
size_t count, loff_t *offset)
|
||||
{
|
||||
struct accounted_call_site *call_site;
|
||||
char buf[50];
|
||||
|
||||
if (count >= 50) {
|
||||
pr_err_ratelimited("Input string size too large\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(buf, 0, 50);
|
||||
|
||||
if (copy_from_user(buf, ubuf, count)) {
|
||||
pr_err_ratelimited("Couldn't copy from user\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (!isalpha(buf[0]) && buf[0] != '_') {
|
||||
pr_err_ratelimited("Invalid call site name\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
call_site = kzalloc(sizeof(*call_site), GFP_KERNEL);
|
||||
if (!call_site)
|
||||
return -ENOMEM;
|
||||
|
||||
strlcpy(call_site->name, buf, strlen(buf));
|
||||
mutex_lock(&accounted_call_site_lock);
|
||||
list_add_tail(&call_site->list, &accounted_call_site_list);
|
||||
mutex_unlock(&accounted_call_site_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t page_owner_call_site_read(struct file *file, char __user *ubuf,
|
||||
size_t count, loff_t *offset)
|
||||
{
|
||||
char *kbuf;
|
||||
struct accounted_call_site *call_site;
|
||||
int i = 1, ret = 0;
|
||||
size_t size = PAGE_SIZE;
|
||||
|
||||
kbuf = kmalloc(size, GFP_KERNEL);
|
||||
if (!kbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = scnprintf(kbuf, count, "%s\n", "Accounted call sites:");
|
||||
mutex_lock(&accounted_call_site_lock);
|
||||
list_for_each_entry(call_site, &accounted_call_site_list, list) {
|
||||
ret += scnprintf(kbuf + ret, size - ret,
|
||||
"%d. %s\n", i, call_site->name);
|
||||
i += 1;
|
||||
if (ret == size) {
|
||||
ret = -ENOMEM;
|
||||
mutex_unlock(&accounted_call_site_lock);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&accounted_call_site_lock);
|
||||
ret = simple_read_from_buffer(ubuf, count, offset, kbuf, strlen(kbuf));
|
||||
err:
|
||||
kfree(kbuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations proc_page_owner_call_site_ops = {
|
||||
.open = simple_open,
|
||||
.write = page_owner_call_site_write,
|
||||
.read = page_owner_call_site_read,
|
||||
};
|
||||
#endif
|
||||
|
||||
static int __init pageowner_init(void)
|
||||
{
|
||||
if (!static_branch_unlikely(&page_owner_inited)) {
|
||||
@ -674,6 +996,14 @@ static int __init pageowner_init(void)
|
||||
debugfs_create_file("page_owner", 0400, NULL, NULL,
|
||||
&proc_page_owner_operations);
|
||||
|
||||
#ifdef CONFIG_QCOM_MINIDUMP_PANIC_DUMP
|
||||
debugfs_create_file("page_owner_filter", 0400, NULL, NULL,
|
||||
&proc_page_owner_filter_ops);
|
||||
debugfs_create_file("page_owner_handles_size_kb", 0400, NULL, NULL,
|
||||
&proc_page_owner_handle_ops);
|
||||
debugfs_create_file("page_owner_call_sites", 0400, NULL, NULL,
|
||||
&proc_page_owner_call_site_ops);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
late_initcall(pageowner_init)
|
||||
|
Loading…
Reference in New Issue
Block a user