lkml.org 
[lkml]   [2011]   [Jul]   [25]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
/
From
Date
SubjectQuestion: Is it possible to clean memory in unmap_vmas?
Hi folks,

I need to implement an on-free memory-cleanning feature within the
kernel module. I've dig a kernel and found that that task may be
accomplished with the unmap_vmas function. So, I hook it with the JUMP
(on x86) and get the control when the process tries to free it's
memory. Next, I get an address range and walk through the page table
entries and then if the PTE is present, writable I looked for the page
structure it corresponds. Then, if page_count(page) is equals to 1 I
write a data from the kernel page to the user space. All is fine
except for some cases when huge processes like chrome and firefox are
crashed. It seems that I spoil some parts of their memory space
thinking that it's need to be fried as unmap_vmas called.

So the first question, it that method can work or am I miss something?..
.. and the second one -- given the following code where might be an error?

I'll be happy to get any sugesstion,
Thanks.

---- code sample

#define PAGE_SIZE_4K PAGE_SIZE
#define PAGE_SIZE_2M PAGE_SIZE_4K * 512
#define PAGE_SIZE_4M PAGE_SIZE_4K * 512 * 2
#define PAGE_SIZE_1G PAGE_SIZE_4K * 512 * 512

/* invalid */
#define PAGE_SIZE_LEVEL0 0
/* 1G mapping */
#define PAGE_SIZE_LEVEL1 PAGE_SIZE_1G
/* 2M or 4M mapping */
#if defined(CONFIG_X86_64) || defined(CONFIG_X86_PAE)
#define PAGE_SIZE_LEVEL2 PAGE_SIZE_2M
#else
#define PAGE_SIZE_LEVEL2 PAGE_SIZE_4M
#endif
/* 4K mapping */
#define PAGE_SIZE_LEVEL3 PAGE_SIZE_4K

static int follow_virtual_address(struct mm_struct * mm, unsigned long
address, pte_t * ppte)
{
int size = PAGE_SIZE_LEVEL0;
pgd_t * pgd; pud_t * pud; pmd_t * pmd; pte_t * pte;

pgd = pgd_offset(mm, address);
if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
goto return_result;

pud = pud_offset(pgd, address);
if (pud_none(*pud))
goto return_result;
if (pud_large(*pud)) {
size = PAGE_SIZE_LEVEL1;
*ppte = *(pte_t *)pud;
goto return_result;
}
if (unlikely(pud_bad(*pud)))
goto return_result;

pmd = pmd_offset(pud, address);
if (pmd_none(*pmd))
goto return_result;
if (pmd_large(*pmd)) {
size = PAGE_SIZE_LEVEL2;
*ppte = *(pte_t *)pmd;
goto return_result;
}
if (unlikely(pmd_bad(*pmd)))
goto return_result;

pte = pte_offset_map(pmd, address);
if (pte) {
size = PAGE_SIZE_LEVEL3;
*ppte = *(pte_t *)pte;
pte_unmap(pte);
}

return_result:
return size;
}

static void inline __clear_memory_range(unsigned long address, int size)
{
int count = size / PAGE_SIZE;

printk("Clean: at %p clearing %d pages\n", address, count);

while (count--) {
copy_to_user((void *)address, (void *)zero_page, PAGE_SIZE);
address += PAGE_SIZE;
}
}

static void clear_memory_range(struct mm_struct * mm, unsigned long
start, unsigned long end)
{
int size;
pte_t pte;

printk("Clean: memory range <%.16lx - %.16lx> [%s]\n", start, end,
current->comm);

while (start < end) {
size = follow_virtual_address(mm, start, &pte);
if (size != PAGE_SIZE_LEVEL0) {
if (pte_present(pte) && pte_write(pte)) {
struct page * page = pte_page(pte);

if (page && page_count(page) == 1) {
__clear_memory_range(start, size);
}
}
} else {
size = PAGE_SIZE;
}
start += size;
}
}

static int my_unmap_vmas(void * tlb, struct vm_area_struct * vma, \
unsigned long start_addr, unsigned long end_addr, \
unsigned long * accounted, void * details)
{
unsigned long start, end;
struct vm_area_struct * vms;

for (vms = vma; vms && vms->vm_start < end_addr; vms = vms->vm_next) {
start = max(vms->vm_start, start_addr);
if (start >= vms->vm_end)
continue;
end = min(vms->vm_end, end_addr);
if (end <= vms->vm_start)
continue;

clear_memory_range(vms->vm_mm, start, end);
}

return real_unmap_vmas(unmap_vmas, tlb, vma, start_addr, end_addr,
accounted, details);
}

---- code sample ends


\
 
 \ /
  Last update: 2011-07-25 16:21    [W:0.063 / U:0.116 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site