|
I have a need
to fetch sk_buff-based packets from my Loadable Kernel Module that
utilizes a Netfilter hook and to present them to user space in an efficient
manner.
The best overall approach I have come up with
is:
Userspace calls my LKM once to initialize
things:
LKM creates a
64KB VMA on behalf of the user proces using:
ulVaddr =
do_mmap_pgoff(NULL, /*
file */
0L, /* Address: 0 = pick VMA for us. */ ulLength, /* Length */ PROT_READ | PROT_WRITE, /* Protocol. */ MAP_SHARED, /* Flags. */ 0); /* Offset */ if (((long)ulVaddr) < 0) { printk(KERN_ALERT"do_mmap_pgoff error = %lu.\n", ulVaddr); return(NULL); } /* At this point we have a valid VMA added to the calling processes address space. There is a */ /* new VMA for the returned address. Now, we find it and then muck with it to map it to our */ /* kernel allocated memory using kmalloc(). Note, to do so with vmalloc requires additional */ /* stuff. See notes. */ /* Step two: Fetch the VMA that was just
created for the virtual address. */
/* We save this for later access to the user space. */ pVma = find_vma(current->mm, ulVaddr); Then for each packet netfilter sends to my LKM, we
queue it and then require the user process to call a GetNextPacket() call which
does:
pvKernel = pNextSkb->head; // Start of IP
Header.
iSize =
PAGE_ALIGN(pNextSkb->end - pNextSkb->head);
for (ulVaddr=(unsigned long)pvKernel;
ulVaddr<(unsigned long)pvKernel +
iSize;ulVaddr+=PAGE_SIZE)
{ /* Reserve all pages to make them remapable. remap_page_range() will ignore mapping if this is not done. */ SetPageReserved(virt_to_page(ulVaddr)); } /* Now, map the kernel memory into the user's VMA. */ /* Set mm semaphore per memory.c. */ down(¤t->mm->mmap_sem); /* We had better hit this with a previous page(s) PG_reserved set or else () frees the mapped pages and later */ /* when kfree() runs the kernel panics and the system stops(if we are VERY lucky). Meaning, it can do far worse */ /* things! */ if (remap_page_range(pVma->vm_start, virt_to_phys(pvKernel), iSize, PAGE_SHARED)) { up(¤t->mm->mmap_sem); printk(KERN_ALERT"remap_page_range() error for K=%p, V=%08lX, Size=%d.\n", pvKernel, pVma->vm_start, iSize); return(-EAGAIN); } up(¤t->mm->mmap_sem); This approach works just fine except for one thing,
a bunch of "forget_pte: old mapping existed!"
messages in syslog. I traced to mm/memory.c#287. This message is printed since I
am re-mapping a Page Table Entry that is already mapped. The call then frees the
PTE and all appears well. There is a function zap_page_range() called by
mem_unmap() which does what I need but it is not exported as a Kernel
symbol. When I commented out printk() in forget_pte() in mm/memory.c#287 and
rebuilt 2.4.1 kernel, my code seems to work over hundreds of millions of frames
with no apparent problems.
I would eventually like to release a product based
on the technique above but am limited by the printk() in mm/memory.c#287.
Any suggestions on how to proceed?
Thanks in advance.
Stuart
|