2012-01-23 16 views
18

Estoy intentando navegar por las tablas de páginas para un proceso en Linux. En un módulo del kernel me di cuenta de la siguiente función:Tablas de páginas para caminar de un proceso en Linux

static struct page *walk_page_table(unsigned long addr) 
{ 
    pgd_t *pgd; 
    pte_t *ptep, pte; 
    pud_t *pud; 
    pmd_t *pmd; 

    struct page *page = NULL; 
    struct mm_struct *mm = current->mm; 

    pgd = pgd_offset(mm, addr); 
    if (pgd_none(*pgd) || pgd_bad(*pgd)) 
     goto out; 
    printk(KERN_NOTICE "Valid pgd"); 

    pud = pud_offset(pgd, addr); 
    if (pud_none(*pud) || pud_bad(*pud)) 
     goto out; 
    printk(KERN_NOTICE "Valid pud"); 

    pmd = pmd_offset(pud, addr); 
    if (pmd_none(*pmd) || pmd_bad(*pmd)) 
     goto out; 
    printk(KERN_NOTICE "Valid pmd"); 

    ptep = pte_offset_map(pmd, addr); 
    if (!ptep) 
     goto out; 
    pte = *ptep; 

    page = pte_page(pte); 
    if (page) 
     printk(KERN_INFO "page frame struct is @ %p", page); 

out: 
    return page; 
} 

Esta función se llama desde el ioctl y addr es una dirección virtual en el espacio de direcciones del proceso:

static int my_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long addr) 
{ 
    struct page *page = walk_page_table(addr); 
    ... 
    return 0; 
} 

Lo extraño es que llamar ioctl en un proceso de espacio de usuario, esto segfaults ... pero parece que la forma en que estoy buscando la entrada de la tabla de la página es correcta porque con dmesg obtengo por ejemplo para cada llamada ioctl:

[ 1721.437104] Valid pgd 
[ 1721.437108] Valid pud 
[ 1721.437108] Valid pmd 
[ 1721.437110] page frame struct is @ c17d9b80 

¿Por qué el proceso no puede completar correctamente la llamada `ioctl '? ¿Tal vez tengo que bloquear algo antes de navegar por las tablas de páginas?

Estoy trabajando con kernel 2.6.35-22 y tablas de páginas de tres niveles.

¡Gracias a todos!

+0

¿Es posible que ioctl syscall regrese con éxito y el código se segfaulting después de eso? –

+0

No porque ioctl syscall es la última instrucción en 'main' antes' return'.Si digo 'ioctl', el proceso no segfault. – MirkoBanchi

+0

¿Por qué escondiste la parte donde usas la dirección de la 'struct page'? ¿Estás seguro de que tus segfaults no provienen de aquí? ¿Has probado a depurar esto en qemu? –

Respuesta

11
pte_unmap(ptep); 

falta justo antes de la etiqueta. Intente cambiar el código de esta manera:

... 
    page = pte_page(pte); 
    if (page) 
     printk(KERN_INFO "page frame struct is @ %p", page); 

    pte_unmap(ptep); 

out: 
+0

Gracias. Estaba seguro de que el núcleo se compiló sin CONFIG_HIGHPTE, en cambio, definió que se configuró para que 'pte_offset_map' hiciera un' kmap'. – MirkoBanchi

+0

¡Gracias! Seguí recibiendo un bloqueo con mensajes como " devuelto con desequilibrio de preferencia". Finalmente rastreó el incremento preempt_count() a pte_offset_map()! Agregar el pte_unmap lo disminuyó y todo bien. – kaiwan

5

Mira /proc/<pid>/smaps sistema de archivos, se puede ver la memoria de espacio de usuario:

cat smaps 
bfa60000-bfa81000 rw-p 00000000 00:00 0   [stack] 
Size:    136 kB 
Rss:     44 kB 

y la forma en que se imprime es a través de fs/proc/task_mmu.c (de fuentes del núcleo):

http://lxr.linux.no/linux+v3.0.4/fs/proc/task_mmu.c

if (vma->vm_mm && !is_vm_hugetlb_page(vma)) 
       walk_page_range(vma->vm_start, vma->vm_end, &smaps_walk); 
       show_map_vma(m, vma.....); 
     seq_printf(m, 
        "Size:   %8lu kB\n" 
        "Rss:   %8lu kB\n" 
        "Pss:   %8lu kB\n" 

Y su función es algo así como la de walk_page_range(). Mirando hacia walk_page_range() se puede ver que la estructura smaps_walk no se supone que cambiar mientras se está caminando:

http://lxr.linux.no/linux+v3.0.4/mm/pagewalk.c#L153 

For eg: 

       } 
201    if (walk->pgd_entry) 
202      err = walk->pgd_entry(pgd, addr, next, walk); 
203    if (!err && 
204     (walk->pud_entry || walk->pmd_entry || walk->pte_entry 

Si paseo fuera a cambiar, a continuación, toda la comprobación anterior puede obtener inconsistente.

Todo esto simplemente significa que usted tiene para bloquear el mmap_sem al caminar la tabla de páginas:

if (!down_read_trylock(&mm->mmap_sem)) { 
      /* 
      * Activate page so shrink_inactive_list is unlikely to unmap 
      * its ptes while lock is dropped, so swapoff can make progress. 
      */ 
      activate_page(page); 
      unlock_page(page); 
      down_read(&mm->mmap_sem); 
      lock_page(page); 
    } 

y luego seguido de desbloqueo:

up_read(&mm->mmap_sem); 

Y, por supuesto, cuando se emite printk () de la tabla de páginas dentro de su módulo kernel, el módulo kernel se está ejecutando en el contexto del proceso de su insmod (simplemente imprima el "comm" y puede ver "insmod") lo que significa que mmap_sem es lock, también significa que el proceso NO es funcionando, y por lo tanto no hay salida de consola hasta que el proceso sea completado (todos los resultados de printk() van a la memoria solamente).

¿Suena lógico?

+0

Gracias Peter, traté de mantener 'mmap_sem' antes de la primera instrucción que lee las tablas de páginas pero no funciona ... el mismo error de falla de segmentación. Sin embargo, cuando llamo 'walk_page_table' no estoy en el contexto del proceso' insmod': lo llamo adentro 'my_ioctl' entonces estoy en el contexto del proceso que invoca' ioctl' syscall. ¿Podría esto hacer la diferencia? – MirkoBanchi

+0

Sí, hace la diferencia. Debido a que los diferentes procesos tienen diferentes tablas de páginas por proceso, si recorre la parte que no es del kernel. Pero todos los procesos comparten la misma tabla de páginas cuando se trata de las direcciones del kernel. –

Cuestiones relacionadas