LINUX.ORG.RU

mmap() в linux


0

0

пытаюсь реализовать mmap - но он не работает - не могу понять почему (как говорится - хоть лопни...).

ядро 2.6.13 - буфер выделяется kmalloc()

userspace - всё вроде как надо - а маппит чёрт-ти чего. В чём тут может быть дело?

если нужно - задавайте наводящие вопросы или могу часть кода привести...

anonymous
Ответ на: комментарий от tmp

Userspace поток открывает девайс (fd=open(...)) и делает mmap():
...
        h->data_raw = mmap(NULL, r1, PROT_READ|PROT_WRITE, MAP_SHARED, h->fd, 0);
...

Метод mmap() в драйвере:
...
static int camera_mmap(struct file *file, struct vm_area_struct *vma)
{
    struct usb_camera *dev;
    int retval = 0;
    unsigned long start = vma->vm_start;
    unsigned long size = vma->vm_end-vma->vm_start;
    unsigned long pos, page;
                                                                                                               
    dev = (struct usb_camera *)file->private_data;
    print_device_state(dev);
// verify that the device wasn't unplugged
    if ( dev->udev == NULL ) {
        return -ENODEV;
    }
    if ( (dev->kurb == NULL) || (dev->kbuffer == NULL) ) {
        err("%s - mmap() (no memory to map)", __FUNCTION__);
        return -ENOMEM;
    }
    if ( down_interruptible(&dev->sem) )
        return -EINTR;
    pos = (unsigned long )dev->kbuffer;
    while ( size > 0 ) {
        page = virt_to_phys((void *)pos)>>PAGE_SHIFT;
        info("Map from %lX to %lX, page %lX (pos %lX)", start, start+PAGE_SIZE, page, pos);
        if ( remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED) ) {
            up(&dev->sem);
            info("-EAGAIN in mmap()!");
            return -EAGAIN;
        }
        start += PAGE_SIZE;
        pos += PAGE_SIZE;
        if ( size > PAGE_SIZE )
            size -= PAGE_SIZE;
        else
            size = 0;
    }
    dev->kbuffer_mapped = ON;
    up(&dev->sem);
    return retval;
}
...

Приложение выводит данные чёрт-ти откуда - если посмотреть maps файл в /proc то хоть mmap() успешно завершился в приложении он указывает на адрес 0x00000000 - что за...?

anonymous
()
Ответ на: комментарий от anonymous

> Приложение выводит данные чёрт-ти откуда - если посмотреть maps файл в /proc то хоть mmap() успешно завершился в приложении он указывает на адрес 0x00000000 - что за...?

"mmap(2) успешно завершился" == "вернул указатель != MAP_FAILED" ?

http://www.opengroup.org/onlinepubs/009695399/functions/mmap.html

// wbr

klalafuda ★☆☆
()
Ответ на: комментарий от klalafuda

Да. Только у меня сравнивается с (void *)-1. Не равен - всё ОК (тут).

anonymous
()
Ответ на: комментарий от ananas

Не понял - при чём сдесь generic_file_mmap() - ведь никакого файла нет - маппится только область памяти (ядерный буфер)?

anonymous
()
Ответ на: комментарий от anonymous

>err("%s - mmap() (no memory to map)", __FUNCTION__);

err() это BSD extensions, а __FUNCTION__ это gcc'ism. Вы это знали? Просто я их нигде не использую...

php-coder ★★★★★
()
Ответ на: комментарий от php-coder

> err() это BSD extensions, а __FUNCTION__ это gcc'ism. Вы это знали? Просто я их нигде не использую...

а по существу вопроса будут комментарии?

// wbr

klalafuda ★☆☆
()
Ответ на: комментарий от anonymous

page = virt_to_phys((void *)pos)>>PAGE_SHIFT; ..... if ( remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED) )

вам не кажется,чтo строчка page = virt_to_phys((void *)pos) >> PAGE_SHIFT неправильная и совершенно непонятно, что вы пытаетесь remap_pfn_range() в дальнейшем?

anonymous
()
Ответ на: комментарий от anonymous

Взято из дров - а какой вариант Вы предлагаете? Как сделать чтобы работало?

P.S. err - это макрос и к расширениям gcc типа __FUNCTION__ никакого отношения не имеет

anonymous
()


я бы предложил обрабатывать операцию vm_oprations_struct->nopage()
i.e. мапировать свой буффер в ядре в userland по мере необходимости.
по крайней мере, у меня это сделано указанным образом и вполне работает.

// wbr

klalafuda ★☆☆
()
Ответ на: комментарий от anonymous

> Не могли бы Вы привести код, который это делает?

ну щас напишем...

> P.S. А буффер Вы чем выделяете? Он dma-шный?

обычный kmalloc(9). у меня стоит простая задача - сделать буффер, выделенный в ядре, пользовательскому процессу. от железа тут ничего нет.

// wbr

klalafuda ★☆☆
()
Ответ на: комментарий от klalafuda

> у меня стоит простая задача - сделать буффер, выделенный в ядре, доспупным пользовательскому процессу.

// wbr

klalafuda ★☆☆
()
Ответ на: комментарий от klalafuda

Для начала: буффер выделенный kmalloc не обязательно будет начинаться на границе страницы.

смотрю дальше...

anonymous
()
Ответ на: комментарий от anonymous

Вы похоже не понимаете разницы между отображением физической и виртуальной памяти. Советую вам найти книгу "Linux Device Drivers" by Alessandro Rubini и открыть на странице 390 - это будет глава Remapping RAM.

anonymous
()
Ответ на: комментарий от anonymous

собственно, протопип простейшего модуля, который делает следующее:

1. при загрузке выделяет какой-то внутенний буффер и забивает его всяким мусором.
2. регистрирует символьное устройство
3. обрабатывает вызов mmap(2) для чтения этого буффера.

цель - расшарить буффер между модулем ядра и пользовательским процессом. синхронизация доступа к буфферу, проверки, корректность кода, выравнивание буффера на границу страницы и пр. мелочи оставляются на ваше усмотрение.

все это собиралось и прогонялось под RHEL4 на ядре 2.6.9-5.ELsmp. про другие среды ничего не скажу.

--- Makefile ---
KERNELRELEASE ?= $(shell uname -r)
KERNELDIR ?= /lib/modules/$(KERNELRELEASE)/build
MODULEDIR := $(PWD)
MODNAME = foo

obj-m := $(MODNAME).o
$(MODNAME)-objs := module.o

all:
    $(MAKE) -C $(KERNELDIR) M=$(MODULEDIR) modules

clean:
    $(MAKE) -C $(KERNELDIR) M=$(MODULEDIR) clean
--- Makefile ---

--- module.c ---
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <asm/page.h>

static int __init foo_init_module(void);
static void __exit foo_exit_module(void);

static int foo_file_mmap(struct file *file, struct vm_area_struct *vma);
static struct file_operations foo_file_ops = {
    .owner = THIS_MODULE,
    .mmap = foo_file_mmap
};

static struct page *foo_vma_nopage(struct vm_area_struct *vma, unsigned long addr, int *type);
static struct vm_operations_struct foo_vm_ops = {
    .nopage = foo_vma_nopage
};

/* Setup buffer length as needed */
static size_t foo_length = PAGE_SIZE * 10;
static void *foo_buffer;
static dev_t foo_dev;
static struct cdev *foo_cdev;

static int __init
foo_init_module(void)
{
    size_t i;
    int err;

    foo_buffer = kmalloc(foo_length, GFP_KERNEL);
    if (!foo_buffer) {
        printk("failed to allocate data buffer\n");
        err = -ENOMEM;
        goto out;
    }

    err = alloc_chrdev_region(&foo_dev, 0, 1, "foo");
    if (err) {
        printk("failed to allocate device region (error %d)\n", err);
        goto out_buffer;
    }

    foo_cdev = cdev_alloc();
    if (!foo_cdev) {
        printk("failed to allocate device\n");
        goto out_chrdev;
    }
    cdev_init(foo_cdev, &foo_file_ops);
    foo_cdev->owner = THIS_MODULE;

    err = cdev_add(foo_cdev, foo_dev, 1);
    if (err) {
        printk("failed to add chr device (error %d)\n", err);
        goto out_cdev;
    }

    for (i = 0; i < foo_length; i++) {
        ((unsigned char*)foo_buffer)[i] = i;
    }

    return 0;

out_cdev:
    cdev_del(foo_cdev);

out_chrdev:
    unregister_chrdev_region(foo_dev, 1);

out_buffer:
    kfree(foo_buffer);

out:
    return err;
}

static void __exit
foo_exit_module(void)
{
    cdev_del(foo_cdev);
    unregister_chrdev_region(foo_dev, 1);
    kfree(foo_buffer);
}

static int
foo_file_mmap(struct file *file, struct vm_area_struct *vma)
{
    vma->vm_flags |= VM_RESERVED;
    vma->vm_ops = &foo_vm_ops;

    return 0;
}

static struct page *
foo_vma_nopage(struct vm_area_struct *vma, unsigned long addr, int *type)
{
    struct page *page;
    unsigned long pfn;

    printk("%s: vma start 0x%lX end 0x%lX addr 0x%lX\n",
        __FUNCTION__, vma->vm_start, vma->vm_end, addr);

    pfn = __pa(foo_buffer) >> PAGE_SHIFT;
    page = pfn_to_page(pfn);
    get_page(page);
    if (type) *type = VM_FAULT_MINOR;

    return page;
}

module_init(foo_init_module);
module_exit(foo_exit_module);
--- module.c ---

собственно простенькая проверка:

--- module.c ---
root@ip-230$ insmod foo.ko
root@ip-230$ lsmod | grep foo
foo                     7052  0
root@ip-230$ cat /proc/devices | grep foo
254 foo
root@ip-230$ mknod /dev/foo c 254 0
root@ip-230$ ls -l /dev/foo
crw-r--r--  1 root root 254, 0 Jul 14 17:08 /dev/foo
root@ip-230$ mmdump /dev/foo 0 64
Dump file '/dev/foo' offset 0 size 64
Run test number 1..
000: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
001: 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
002: 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
003: 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
root@ip-230$ dmesg | grep foo
foo_vma_nopage: vma start 0xB7FFE000 end 0xB7FFF000 addr 0xB7FFE000
root@ip-230$ rmmod foo
root@ip-230$ rm -f /dev/foo
--- module.c ---

// wbr

klalafuda ★☆☆
()
Ответ на: комментарий от anonymous

> Советую вам найти книгу "Linux Device Drivers" by Alessandro Rubini и открыть на странице 390 - это будет глава Remapping RAM.

книга хорошая, согласен. но страница скорее 412 "Chapter 15. Memory Mapping and DMA".

// wbr

klalafuda ★☆☆
()
Ответ на: комментарий от klalafuda

Может чего-то я недопонял - а как данные копировать/получать userspace проге (особенно интересно последнее)?

BTW - чем no_page лучше mmap() непоср-но?

И не могли бы Вы привести /proc/maps листинг (буду оч благодарен) userspace проц-а ?

P.S. Книгу читал (rev2; rev3 смотрел по этому поводу но не помогло - затык какой-то... :-( )

anonymous
()
Ответ на: комментарий от anonymous

> Может чего-то я недопонял - а как данные копировать/получать userspace проге (особенно интересно последнее)?

man mmap(2) ?:)

1. открываем наш файл. допустим, это какое-то символьное устройство /dev/foo. впрочем, это может быть любой файл.
2. мапируем его в память пользовательского процесса через mmap(2) и получаем указатель void * "на".
3. полученный указатель используем для чтения/записи данных как обычный указатель.
4. по окончанию подчищаем за собой i.e. размапируем память через munmap(2) и закрываем файл.

> И не могли бы Вы привести /proc/maps листинг (буду оч благодарен) userspace проц-а ?

--- cut ---
root@ip-230$ mmdump -s30 /dev/foo 0 100
root@ip-230$ cat /proc/4521/maps
00abd000-00ad2000 r-xp 00000000 fd:00 145642     /lib/ld-2.3.4.so
00ad2000-00ad3000 r--p 00014000 fd:00 145642     /lib/ld-2.3.4.so
00ad3000-00ad4000 rw-p 00015000 fd:00 145642     /lib/ld-2.3.4.so
00ad6000-00bf9000 r-xp 00000000 fd:00 145665     /lib/tls/libc-2.3.4.so
00bf9000-00bfa000 r--p 00123000 fd:00 145665     /lib/tls/libc-2.3.4.so
00bfa000-00bfd000 rw-p 00124000 fd:00 145665     /lib/tls/libc-2.3.4.so
00bfd000-00bff000 rw-p 00bfd000 00:00 0
08048000-0804a000 r-xp 00000000 00:15 2539989    /.automount/timmy/root/mnt/.2/xyz/bin/mmdump
0804a000-0804b000 rw-p 00001000 00:15 2539989    /.automount/timmy/root/mnt/.2/xyz/bin/mmdump
b7ff0000-b7ff1000 rw-p b7ff0000 00:00 0
b7ffe000-b7fff000 r--s 00000000 00:0d 8287       /dev/foo
b7fff000-b8000000 rw-p b7fff000 00:00 0
bfe67000-c0000000 rw-p bfe67000 00:00 0
ffffe000-fffff000 ---p 00000000 00:00 0

root@ip-230$ dmesg | grep foo
foo_vma_nopage: vma start 0xB7FFE000 end 0xB7FFF000 addr 0xB7FFE000
--- cut ---

как видно из maps и того, что попадает в обработчик mmap, область отмапирована в адрессное пространство процесса по адресам b7ffe000-b7fff000.

// wbr

klalafuda ★☆☆
()
Ответ на: комментарий от anonymous

> BTW - чем no_page лучше mmap() непоср-но?

вы имеете ввиду remap_pfn_range() вместо mmap()? да AFAIU особо ничем, кроме того, что он у меня работает :) а вот с remap как-то с первого захода отмапировать буффер не получилось so разбиратсья надо. может как-нить руки и дойдут.

// wbr

klalafuda ★☆☆
()
Ответ на: комментарий от klalafuda

Вообще-то у меня то же самое получалось - но странно другое:

1. b7ffe000-b7fff000 r--s 00000000 00:0d 8287 /dev/foo Почему не указано что отмаппировано по адресу выделенному kmalloc() (в колонке где нули разве не должны быть адреса типа 0xcXXXXXX - ядерные?)

2. При посылке в драйвере memcpy() в адрес выделенный kmalloc() копируется что-то вроде "Hello,world"; затем userspace читает (есно по 0xbXXXXXXX - но получает фигу - может читать не оттуда? или писать не так? или ещё чего не так?

anonymous
()
Ответ на: комментарий от anonymous

>1. b7ffe000-b7fff000 r--s 00000000 00:0d 8287 /dev/foo Почему не
>указано что отмаппировано по адресу выделенному kmalloc() (в колонке
>где нули разве не должны быть адреса типа 0xcXXXXXX - ядерные?)

Третья колонка говорит о том, что виртуально отображена часть файла (устройства) со смещения 0х00000000. Если вы через mmap пытаетесь отобразить первую страницу файла, то что там может быть кроме как 0?

Физических адресов в maps нет.

>2. При посылке в драйвере memcpy() в адрес выделенный kmalloc()
>копируется что-то вроде "Hello,world"; затем userspace читает (есно по
>0xbXXXXXXX - но получает фигу - может читать не оттуда? или писать не
>так? или ещё чего не так?

Либо по-разному отображен кусок памяти, либо одинаково, но в молоко (например, шинный адрес не соответствует ни одному устройству).

Можно ради проверки попробовать тупо сделать на пользовательский адрес из ядра get_user_pages и сверить page_address. (код прочитать не осилил).

Murr ★★
()

и исчо делаете ли вы SetPageReserved после выделения памяти, а?

примерно так:

char *dev_buf;
int dev_open(...) {
    ...
    dev_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
    ...
    SetPageReserved(virt_to_page(dev_buf));
    ...
}
int dev_mmap(...) {
unsigned long start = vma->vm_start;
...
if (remap_pfn_range(vma, start, page_to_pfn(virt_to_page( dev_buf), PAGE_SIZE, PAGE_SHARED))
 ругаццо 
       

anonymous
()
Ответ на: комментарий от anonymous

Браво (по поводу резервирования)!!!

Я до этого уже догадался сам в прошедшие выходные - именно из-за этого всё и не работало.

Всем спасибо за попытки помочь (однако рад что всё сам смог решить).

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.