Linux. Драйвер. Память.
От: unkn2000  
Дата: 17.12.03 17:56
Оценка:
Как в драйвере (kernel mode) организовать передачу данных из устройства непосредственно в буфер процесса (user mode), минуя буфер в драйвере?

ОС Linux 2.4.18.
PCI устройство в режиме мастеринга (без участия процессора, как в DMA) заливает данные в буфер памяти. Адрес начала буфера и его размер я пишу в порты в\в устройства.
Хотелось бы передавать устройству физический адрес буфера процесса запросившего данные.
Объемы данных (несколько аудио и видео потоков) большие и поэтому лишнее копирование из устройства в буфер драйвера и оттуда в буфер процесса крайне не желательно. В то же время использовать для перекачки данных процессор, когда драйвер читает данные из порта и передаёт их процессу ( что-то типа put_user(inb(PORT), buf++); ) то же достаточно накладно.
Как можно реализовать схему при которой данные из устройства сразу поступают в адресное пространство процесса ждущего данные?
Что-то типа следующего:
Процесс получает буфер (malloc) и передаёт указатель на него и его размер в драйвер (при помощи ioctl). Процесс засыпает в ioctl (при помощи очереди ожидания). В драйвере определяется физический адрес этого буфера и передаётся устройству. Устройство заполняет буфер данными и генерирует прерывание. В обработчике прерывания процесс пробуждается и при выходе из ioctl буфер либо заполнен либо возвращается код ошибки.

Подскажите хоть в каком направлении рыть.
Re: bash + .so
От: Murr Россия  
Дата: 17.12.03 18:34
Оценка:
Здравствуйте, unkn2000, Вы писали:

U>Как в драйвере (kernel mode) организовать передачу данных из устройства непосредственно в буфер процесса (user mode), минуя буфер в драйвере?


U>ОС Linux 2.4.18.

U>PCI устройство в режиме мастеринга (без участия процессора, как в DMA) заливает данные в буфер памяти. Адрес начала буфера и его размер я пишу в порты в\в устройства.
U>Хотелось бы передавать устройству физический адрес буфера процесса запросившего данные.
U>Объемы данных (несколько аудио и видео потоков) большие и поэтому лишнее копирование из устройства в буфер драйвера и оттуда в буфер процесса крайне не желательно. В то же время использовать для перекачки данных процессор, когда драйвер читает данные из порта и передаёт их процессу ( что-то типа put_user(inb(PORT), buf++); ) то же достаточно накладно.
U>Как можно реализовать схему при которой данные из устройства сразу поступают в адресное пространство процесса ждущего данные?
U>Что-то типа следующего:
U>Процесс получает буфер (malloc) и передаёт указатель на него и его размер в драйвер (при помощи ioctl). Процесс засыпает в ioctl (при помощи очереди ожидания). В драйвере определяется физический адрес этого буфера и передаётся устройству. Устройство заполняет буфер данными и генерирует прерывание. В обработчике прерывания процесс пробуждается и при выходе из ioctl буфер либо заполнен либо возвращается код ошибки.

U>Подскажите хоть в каком направлении рыть.


Пользуйтесь kio (direct_IO) интерфейсом.
Re[2]: bash + .so
От: unkn2000  
Дата: 17.12.03 18:44
Оценка:
Здравствуйте, Murr, Вы писали:


M>Пользуйтесь kio (direct_IO) интерфейсом.


А чуть подробней можно?
Что это за интерфейс такой?
Где про него почитать можно?
Хотя бы в какие файлы заглянуть?
Re[3]: bash + .so
От: Murr Россия  
Дата: 17.12.03 18:57
Оценка:
Здравствуйте, unkn2000, Вы писали:

U>Здравствуйте, Murr, Вы писали:



M>>Пользуйтесь kio (direct_IO) интерфейсом.


U>А чуть подробней можно?

U>Что это за интерфейс такой?
U>Где про него почитать можно?
U>Хотя бы в какие файлы заглянуть?

Почитать, честно говоря, не знаю где. Может в LDP что-нибудь и есть...
Посмотреть можно
mm/filemap.c: generic_file_read, generic_file_direct_IO.
Ядро само заполняет kiobuf ссылками на страницы переданного userspace буфера, т.е. не нужно самостоятельно делать им pin и трансляцию.
Re[4]: bash + .so
От: Murr Россия  
Дата: 17.12.03 18:59
Оценка:
Здравствуйте, Murr, Вы писали:

M>Здравствуйте, unkn2000, Вы писали:


U>>Здравствуйте, Murr, Вы писали:



M>>>Пользуйтесь kio (direct_IO) интерфейсом.


U>>А чуть подробней можно?

U>>Что это за интерфейс такой?
U>>Где про него почитать можно?
U>>Хотя бы в какие файлы заглянуть?

M>Почитать, честно говоря, не знаю где. Может в LDP что-нибудь и есть...

M>Посмотреть можно
M>mm/filemap.c: generic_file_read, generic_file_direct_IO.
M>Ядро само заполняет kiobuf ссылками на страницы переданного userspace буфера, т.е. не нужно самостоятельно делать им pin и трансляцию.

Просто в struct file нужно определить одну доп операцию — direct_IO.
И из user-space открывать dev с флажком O_DIRECT.
Re[5]: Linux. Драйвер. Память.
От: unkn2000  
Дата: 22.12.03 13:51
Оценка:
Здравствуйте, Murr.
Я этот же вопрос задал на соседней ветке ( низкоуровневое программирование ).
http://www.rsdn.ru/Forum/Message.aspx?mid=483272#483272
Автор: unkn2000
Дата: 18.12.03

Мне ответили, но что-то не получается. Не могли бы вы помоч разобраться.
Re[6]: Linux. Драйвер. Память.
От: Murr Россия  
Дата: 23.12.03 09:00
Оценка:
Здравствуйте, unkn2000, Вы писали:

U>Здравствуйте, Murr.

U>Я этот же вопрос задал на соседней ветке ( низкоуровневое программирование ).
U>http://www.rsdn.ru/Forum/Message.aspx?mid=483272#483272
Автор: unkn2000
Дата: 18.12.03

U>Мне ответили, но что-то не получается. Не могли бы вы помоч разобраться.

Как работает драйвер блочного устройства?
Вы регистрируете устройство, при открытии inode передается управление в вашу функцию open.
Вы определяете для переданной inode операции a_ops (inode->i_mapping->a_ops), конкретнее — direct_IO.
Если Ваше устройство открывается в режиме O_DIRECT, то при чтении используется callback direct_IO.

В качестве примера реализации для блочного ввода-вывода просто посмотрите fs/buffer.c:generic_direct_IO.

То есть фактически Вам на вход подается массив pinned страниц — вам только остается посчитать физический адрес каждой страницы и подать на устройство. Чтобы получить физический адрес скорее всего достаточно воспользоваться макросом page_to_phys (вроде бы должно работать даже на NUMA/DISCONTIG).

P.S. Попробуйте написать Если есть какие-то вопросы — я отвечу в меру своих знаний.
Re[7]: Linux. Драйвер. Память.
От: Murr Россия  
Дата: 23.12.03 09:14
Оценка:
Аноним довольно профессионально описал как это сделать с mmap, но там есть ряд своих тонкостей.

Например, если не пишешь свои address_space operations, то нужно объявлять VMA как VM_RESERVED или VM_LOCKED, чтобы vmscan/kswapd не отправил страницы в swap .

В этом отношении direct_IO попроще и именно поэтому я его и посоветовал.

P.S. Правда, в случае с direct_IO могут возникнуть интересные эффекты, если страница будет выделена выше 4 Гб (на PC)... Не знаю могут ли Bus Master устройства так высоко залезать.
Re[6]: Путь к каталогу программы
От: Murr Россия  
Дата: 24.12.03 21:34
Оценка:
Здравствуйте, unkn2000, Вы писали:

U>Здравствуйте, Murr.

U>Я этот же вопрос задал на соседней ветке ( низкоуровневое программирование ).
U>http://www.rsdn.ru/Forum/Message.aspx?mid=483272#483272
Автор: unkn2000
Дата: 18.12.03

U>Мне ответили, но что-то не получается. Не могли бы вы помоч разобраться.
Я наваял пример.
Надеюсь он поможет разобраться.

Makefile
CC := gcc
CFLAGS_K := -c -I/lib/modules/`uname -r`/build/include
CFLAGS_U :=

all: dio.c diotest.c
        $(CC) $(CFLAGS_K) dio.c
        $(CC) $(CFLAGS_U) diotest.c -o diotest
        mknod diomod c 222 0

.PHONY: clean

clean:
        rm -f dio.o diotest


dio.c
#define __KERNEL__
#define MODULE

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/iobuf.h>
#include <linux/highmem.h>
#include <asm/errno.h>
#include <asm/page.h>
#include <asm/io.h>

#define DIOMOD_MAJOR 222

#define AS_SIZE (4*1024*1024)

#define MIN(a,b) ((a)>(b))?(b):(a)

static int diodev_direct_IO(int rw, struct inode *inode, struct kiobuf *iobuf,
                         unsigned long blocknr, int blocksize) {
        int i, to_copy;
        int length = iobuf->length, nr_blocks;
        long offset = iobuf->offset;
        struct page * pg;
        char * virt;

        printk ("diomod: in direct_IO rw=%s, blocknr=%d, blocksize=%d offset=%lu length=%d.\n", (rw == READ)?"read":"write", blocknr, blocksize, offset, length);

        if (rw == WRITE) return -ENXIO;

        nr_blocks = length / blocksize;

        for (i = 0; i < nr_blocks; i++, blocknr++) {
            pg = iobuf->maplist[i];

            printk ("Received a page for fulfill: struct page @ %p, physical address @ %p\n", pg, page_to_phys(pg));

            to_copy = MIN(PAGE_SIZE-offset, length);
            virt = kmap(pg);                                            // AP: Just a demo side effect
            printk ("diomod: memsetting %p(%d).\n", virt+offset, to_copy);
            memset (virt + offset, 'Q', to_copy); // AP: Just a demo side effect
            kunmap(pg);                                                 // AP: Just a demo side effect

            length -= to_copy;                    // AP: Just a demo side effect

            offset = 0;                                                 // AP: Just a demo side effect
        }

        return length;
}


static struct address_space_operations diomod_aops = {
    direct_IO : diodev_direct_IO
};

static int diomod_open(struct inode *inode, struct file *file) {
    printk ("diomod: in open.\n");

    inode->i_size = AS_SIZE;
    inode->i_blkbits = PAGE_SHIFT;
    inode->i_mapping->a_ops = &diomod_aops;

    return 0;
}


static struct file_operations diomod_fops = {
    open : diomod_open,
    read : generic_file_read
};

int init_module () {

    if (register_chrdev (DIOMOD_MAJOR, "diodev", &diomod_fops) != 0) {
        printk ("diomod: failed to register diodev device.\n");
        return -EBUSY;
    }

    printk ("diomod module loaded.\n");

    return 0;
}

void cleanup_module() {

    if (unregister_chrdev (DIOMOD_MAJOR, "diodev") != 0) {
            printk ("DIOMOD: failed to unregister diodev device! Please contact author!\n");
    }

    printk ("diomod module unloaded.\n");

}


diotest.c
#define _GNU_SOURCE
#include <stdio.h>
#include <fcntl.h>

#define ARRAY_SIZE (getpagesize()*10)

int main(int argc, char*argv[]) {
    int fd = open("diomod", O_RDWR|O_DIRECT);
    char *arr = memalign(getpagesize(), ARRAY_SIZE);

    memset (arr, 'A', ARRAY_SIZE);

    pread (fd, arr+getpagesize(), getpagesize(), 0);

    close (fd);

    write (1, arr, ARRAY_SIZE);

    return 0;

}


Успехов.

P.S. Пример с балды, поэтому могут быть неточности... в принципе было достаточно выводить физический адрес страницы, но я для пример заполнял передаваемый буфер буквами Q.
Re[7]: Путь к каталогу программы
От: Murr Россия  
Дата: 24.12.03 21:41
Оценка:
Ну да, после memset надо бы еще flush_dcache_page делать

Ну в общем, есть там неточности
но в целом вроде пример почти рабочий

Во всяком случае из него видно что и как )
Re[8]: Путь к каталогу программы
От: unkn2000  
Дата: 25.12.03 18:46
Оценка:
Здравствуйте, Murr, Вы писали:

M>Ну да, после memset надо бы еще flush_dcache_page делать


M>Ну в общем, есть там неточности

M>но в целом вроде пример почти рабочий

M>Во всяком случае из него видно что и как )




Так работает
    memset (arr, 'A', ARRAY_SIZE);
    pread (fd, arr, getpagesize(), 0);
    //в место pread (fd, arr+getpagesize(), getpagesize(), 0);
    close (fd);
    write (1, arr, ARRAY_SIZE);

Но только первая страница выводиться с ‘Q’, затем ‘A’.
Маловато будет.
А фун. flush_dcache_page – на результат не влияет.

Ещё Вы писали:
>P.S. Правда, в случае с direct_IO могут возникнуть интересные эффекты, если >страница будет выделена выше 4 Гб (на PC)... Не знаю могут ли Bus Master устройства >так высоко залезать.

Вы правы выше 4 гиг не адресуют.

В любом случае спасибо за помощь.
Re[9]: Путь к каталогу программы
От: Murr Россия  
Дата: 25.12.03 19:06
Оценка:
Здравствуйте, unkn2000, Вы писали:

U>Но только первая страница выводиться с ‘Q’, затем ‘A’.

U>Маловато будет.
Да нет... все эти memset — это просто так...
Дальше printk, который выводит физический адрес страницы, смысла смотреть нет.
Это так — народное творчество

U>А фун. flush_dcache_page – на результат не влияет.


На PC — да.

U>В любом случае спасибо за помощь.


Не за что... просто имейте в виду этот вариант.
Re[10]: Linux. Драйвер. Память.
От: unkn2000  
Дата: 26.12.03 16:50
Оценка:
Здравствуйте, Murr, Вы писали:

M>Здравствуйте, unkn2000, Вы писали:


U>>Но только первая страница выводиться с ‘Q’, затем ‘A’.

U>>Маловато будет.
M>Да нет... все эти memset — это просто так...
M>Дальше printk, который выводит физический адрес страницы, смысла смотреть нет.
M>Это так — народное творчество

U>>А фун. flush_dcache_page – на результат не влияет.


M>На PC — да.


U>>В любом случае спасибо за помощь.


M>Не за что... просто имейте в виду этот вариант.


Я понимаю, но всё же, что б закончить с примером надо исправить.
memset (arr, 'A', ARRAY_SIZE);
pread (fd, arr, ARRAY_SIZE, 0);
//в место pread (fd, arr+getpagesize(), getpagesize(), 0);
//или того, что я написал в начале pread (fd, arr, getpagesize(), 0);
//и в первом и во втором случае отображалось только первая страница
close (fd);
write (1, arr, ARRAY_SIZE);

Правильно ли я понял смысл этого метода?
В user mode выделяем память кратную размеру страницы памяти (для Intel это обычно 4КБ)
Кстати, а память выделяется не прерывным куском?
При открытии устройства сообщаем ядру, что используем O_DIRECT.
Драйвер совместно с ядром подцепляют fun_direct_IO.
Далее все запросы на чтение \ запмись передаются этой функции.
В fun_direct_IO мы последовательно отображаем (kmap) и инициализируем каждую страницу.

А где эти страницы располагаются физически?
Я так понял они не свопируются и реально сидят в памяти. А значит, устройство может к ним адресоваться.
Или я чего-то не понимаю?
Murr растолкуй пожалуйста.

В принципе если бы выполнялось два условия:
1. Страницы располагагись не прерывно в памяти.
2. И фун. page_to_phys(первая страница) возвращала бы физический адрес до 4GB.

Или хотя бы второе то этот вариант можно было бы попробовать использовать в моём случае.
Я бы
1. в fun_direct_IO отображал страницу, передовал адрес устройству,
2. усыплял процесс в fun_direct_IO,
2. устройство писало данные,
3. по прерыванию будил процесс, kunmap
4. У процесса свободный не кем не занитый буфер.
и т.д. в цикле в принцепе если сами буфферы обрабатывать в другом потоке,
то первый поток в полне бы успевал снабжать устройство новыми буфферами.
Re[11]: Linux. Драйвер. Память.
От: Murr Россия  
Дата: 26.12.03 18:04
Оценка:
Здравствуйте, unkn2000, Вы писали:

U>Я понимаю, но всё же, что б закончить с примером надо исправить.

U> memset (arr, 'A', ARRAY_SIZE);
U> pread (fd, arr, ARRAY_SIZE, 0);
U> //в место pread (fd, arr+getpagesize(), getpagesize(), 0);
U> //или того, что я написал в начале pread (fd, arr, getpagesize(), 0);
U> //и в первом и во втором случае отображалось только первая страница
U> close (fd);
U> write (1, arr, ARRAY_SIZE);

Се-то я не понимаю.

Я специально заполняю только маленький кусочек и в середине выделенного буфера, чтобы показать, что корректно обрабатываю передаваемый в kernel-space буфер, т.е. то, что нет переполнения ни вперед ни назад. Зачем мне заполнять от начала или до конца?

U>Правильно ли я понял смысл этого метода?

U>В user mode выделяем память кратную размеру страницы памяти (для Intel это обычно 4КБ)
U>Кстати, а память выделяется не прерывным куском?
Нет.

U>При открытии устройства сообщаем ядру, что используем O_DIRECT.

U>Драйвер совместно с ядром подцепляют fun_direct_IO.
U>Далее все запросы на чтение \ запмись передаются этой функции.
Да.

U>В fun_direct_IO мы последовательно отображаем (kmap) и инициализируем каждую страницу.

Я прямо начинаю сожалеть, что вообще сделал побочный эффект в direct_IO.
kmap и подобная хрень для Ваших целей вообще не нужна! Всё, что помечено "side effect" — это просто побочный эффект. kmap нужен чтобы из ядра обращаться к соответствующей странице (чтобы она получила в нем трансляцию), но Вам для ввода-вывода это нафиг не нужно. Просто я не хотел писать драйвер устройства, который осуществляет реальный ввод-вывод, поэтому ограничился простым заполнением буфера (а для этого потребовалось спроецировать страницу на ядро) как демонстрацией побочного эффекта.

U>А где эти страницы располагаются физически?

U>Я так понял они не свопируются и реально сидят в памяти. А значит, устройство может к ним адресоваться.
Располагаются где угодно. Верно.

U>В принципе если бы выполнялось два условия:

U>1. Страницы располагагись не прерывно в памяти.
Довольно странное требование для I/O, IMHO. Это, как я понимаю, было бы удобно, но вообще-то необязательно.

U>2. И фун. page_to_phys(первая страница) возвращала бы физический адрес до 4GB.

Вот это можно гарантировать только на машинах с < 4 Гб, поскольку буфера пользователя могут быть выделены где угодно (GFP_USER).

U>Я бы

U>1. в fun_direct_IO отображал страницу, передовал адрес устройству,
Не нужно отображать страницу! Она уже pinned, а физический адрес Вы можете получить с помощью макроса.

U>2. усыплял процесс в fun_direct_IO,

Угу.

U>2. устройство писало данные,

Угу.

U>3. по прерыванию будил процесс, kunmap

kunmap не нужен.

U>4. У процесса свободный не кем не занитый буфер.

U>и т.д. в цикле в принцепе если сами буфферы обрабатывать в другом потоке,
U>то первый поток в полне бы успевал снабжать устройство новыми буфферами.
Ничего не понял насчет "потоков".

Зачем нужны какие-то "потоки"?

Я это так представляю: в direct_IO Вы программируете устройство на передачу данных с гранулярностью PAGE_SIZE, для каждого запроса создаете некую структуру (скажем, в хэшированном списке) со своей очередью ожидания, в прерывании выставляете в этой структуре флажок (успешность/неуспешность операции) и будите всех кто на этой структуре спит, в direct_IO делаете следующий запрос и засыпаете на нем.
Re[12]: Linux. Драйвер. Память.
От: Murr Россия  
Дата: 26.12.03 18:09
Оценка:
Если устройство не поддерживает вложенные транзакции, то схема вообще тривиальная.

Драйвер просто синхронно делает запросы и ожидает ввода/вывода на одной единственной структуре, сериализуя доступ с помощью семафора. И семафор и структура глобальны для драйвера.
Re: как написать драйвер
От: Signed  
Дата: 28.12.03 13:32
Оценка:
Можешь подкинуть простой пример как драйверы пишутся под Linux....
меня интересует когда используется одновременно и бинарник (сам драйвер) и библиотека *.so
(сорри если не совсем корректно изложился)
Спасибо

almaz-l@mail.ru
Re[12]: Linux. Драйвер. Память.
От: unkn2000  
Дата: 30.12.03 12:11
Оценка:
Здравствуйте, Murr, Вы писали:

M>Се-то я не понимаю.

M>Я специально заполняю только маленький кусочек и в середине выделенного буфера, чтобы показать, что корректно обрабатываю передаваемый в kernel-space буфер, т.е. то, что нет переполнения ни вперед ни назад. Зачем мне заполнять от начала или до конца?

А-а-а.… Понял. Я просто не знал, что у Вас на выходе должно быть от этого и мои предложения.

U>>Кстати, а память выделяется не прерывным куском?

M>Нет.

Жаль. Просто наш разработчик этого не любит. Хотя с ним мы уже договорились.
Я ему буду передавать адрес массива указателей на начало страниц.
А он мне прерываниями сообщать, когда исчерпает память.
Ну, это примерно.

U>>В fun_direct_IO мы последовательно отображаем (kmap) и инициализируем каждую страницу.

M>Я прямо начинаю сожалеть, что вообще сделал побочный эффект в direct_IO.
M>kmap и подобная хрень для Ваших целей вообще не нужна! Всё, что помечено "side effect" — это просто побочный эффект. kmap нужен чтобы из ядра обращаться к соответствующей странице (чтобы она получила в нем трансляцию), но Вам для ввода-вывода это нафиг не нужно. Просто я не хотел писать драйвер устройства, который осуществляет реальный ввод-вывод, поэтому ограничился простым заполнением буфера (а для этого потребовалось спроецировать страницу на ядро) как демонстрацией побочного эффекта.

Всё я, кажется, понял мне достаточно сделать что-то типа:
Обвить void *g_addr = NULL;
В функции fun_direct_io(…) {… g_addr = page_to_phys(pg);
g_addr отдаём устройству.
Кажется, теперь правильно?

U>>А где эти страницы располагаются физически?

U>>Я так понял они не свопируются и реально сидят в памяти. А значит, устройство может к ним адресоваться.
M>Располагаются где угодно. Верно.

Где угодно в физической памяти (на борту в RAM)?
Как я понял у интела страничная (4KB) организация памяти. И есть там ещё какой-то механизм копирование при чтении\записи. На третьем кольце процессам выделяется виртуальная память. Реально ей физическая память выделяется только при обращении и то только той странице, которой принадлежит адрес. НО когда память (список страничек) оказывается драйвере в fun_direct_io она уже реально отображена в RAMе.
Т.е. если у меня на борту 256MB то адреса этих страничек должны быть в диапазоне от 0x0 до 0x100000000 (256MB). И устройство запросто может к ним адресоваться. Всё ли верно?
А что будет, если нет свободных страничек в системе и нет возможности отобразить их в RAM? Я предполагаю, что в этом случае система заблокирует процесс пока память не появиться? Правильно ли я предполагаю?
Еще я не понимаю. Когда память находится в ядре в фун. fun_direct_io (и тогда да же пока процесс в ней спит), память отображена на физические адреса и не свопируется.
Но когда система её снова может свопировать? Мне это нужно, что бы, когда память передаётся процессу, она могла при необходимости менеджером памяти отправляться в своп и не висеть мёртвым грузом в RAM. Нужны ли какие-то дополнительно манипуляции с памятью (например, munlock(…) )? Или только освобождать память?

U>>1. Страницы располагались ни прерывно в памяти.

M>Довольно странное требование для I/O, IMHO. Это, как я понимаю, было бы удобно, но вообще-то необязательно.

Согласен. И даже убедил в этом нашего разработчика железа.

U>>2. И фун. page_to_phys(первая страница) возвращала бы физический адрес до 4GB.

M>Вот это можно гарантировать только на машинах с < 4 Гб, поскольку буфера пользователя могут быть выделены где угодно (GFP_USER).

Честно говоря, я таких машин даже не видел. Ходя в будущем всё может быть (Хотя там 64 разрядные процессоры и шины на подходе). Ну да буду решать проблемы по мере поступления.

Murr спасибо за помощь.
Приятно было пообщаться.
Видно, что в Линухе ты силён.
Не поделишься источниками информации?
Здравствуйте, Murr, Вы писали:

M>Се-то я не понимаю.

M>Я специально заполняю только маленький кусочек и в середине выделенного буфера, чтобы показать, что корректно обрабатываю передаваемый в kernel-space буфер, т.е. то, что нет переполнения ни вперед ни назад. Зачем мне заполнять от начала или до конца?

А-а-а.… Понял. Я просто не знал, что у Вас на выходе должно быть от этого и мои предложения.

U>>Кстати, а память выделяется не прерывным куском?

M>Нет.

Жаль. Просто наш разработчик этого не любит. Хотя с ним мы уже договорились.
Я ему буду передавать адрес массива указателей на начало страниц.
А он мне прерываниями сообщать, когда исчерпает память.
Ну, это примерно.

U>>В fun_direct_IO мы последовательно отображаем (kmap) и инициализируем каждую страницу.

M>Я прямо начинаю сожалеть, что вообще сделал побочный эффект в direct_IO.
M>kmap и подобная хрень для Ваших целей вообще не нужна! Всё, что помечено "side effect" — это просто побочный эффект. kmap нужен чтобы из ядра обращаться к соответствующей странице (чтобы она получила в нем трансляцию), но Вам для ввода-вывода это нафиг не нужно. Просто я не хотел писать драйвер устройства, который осуществляет реальный ввод-вывод, поэтому ограничился простым заполнением буфера (а для этого потребовалось спроецировать страницу на ядро) как демонстрацией побочного эффекта.

Всё я, кажется, понял мне достаточно сделать что-то типа:
Обвить void *g_addr = NULL;
В функции fun_direct_io(…) {… g_addr = page_to_phys(pg);
g_addr отдаём устройству.
Кажется, теперь правильно?

U>>А где эти страницы располагаются физически?

U>>Я так понял они не свопируются и реально сидят в памяти. А значит, устройство может к ним адресоваться.
M>Располагаются где угодно. Верно.

Где угодно в физической памяти (на борту в RAM)?
Как я понял у интела страничная (4KB) организация памяти. И есть там ещё какой-то механизм копирование при чтении\записи. На третьем кольце процессам выделяется виртуальная память. Реально ей физическая память выделяется только при обращении и то только той странице, которой принадлежит адрес. НО когда память (список страничек) оказывается драйвере в fun_direct_io она уже реально отображена в RAMе.
Т.е. если у меня на борту 256MB то адреса этих страничек должны быть в диапазоне от 0x0 до 0x100000000 (256MB). И устройство запросто может к ним адресоваться. Всё ли верно?
А что будет, если нет свободных страничек в системе и нет возможности отобразить их в RAM? Я предполагаю, что в этом случае система заблокирует процесс пока память не появиться? Правильно ли я предполагаю?
Еще я не понимаю. Когда память находится в ядре в фун. fun_direct_io (и тогда да же пока процесс в ней спит), память отображена на физические адреса и не свопируется.
Но когда система её снова может свопировать? Мне это нужно, что бы, когда память передаётся процессу, она могла при необходимости менеджером памяти отправляться в своп и не висеть мёртвым грузом в RAM. Нужны ли какие-то дополнительно манипуляции с памятью (например, munlock(…) )? Или только освобождать память?

U>>1. Страницы располагались ни прерывно в памяти.

M>Довольно странное требование для I/O, IMHO. Это, как я понимаю, было бы удобно, но вообще-то необязательно.

Согласен. И даже убедил в этом нашего разработчика железа.

U>>2. И фун. page_to_phys(первая страница) возвращала бы физический адрес до 4GB.

M>Вот это можно гарантировать только на машинах с < 4 Гб, поскольку буфера пользователя могут быть выделены где угодно (GFP_USER).

Честно говоря, я таких машин даже не видел. Ходя в будущем всё может быть (Хотя там 64 разрядные процессоры и шины на подходе). Ну да буду решать проблемы по мере поступления.

Murr спасибо за помощь.
Приятно было пообщаться.
Видно, что в Линухе Вы сильны.
Не поделишься источниками информации?
Re[13]: Linux. Драйвер. Память.
От: Murr Россия  
Дата: 30.12.03 21:21
Оценка: 3 (1)
Здравствуйте, unkn2000, Вы писали:

U>Всё я, кажется, понял мне достаточно сделать что-то типа:

U>Обвить void *g_addr = NULL;
U>В функции fun_direct_io(…) {… g_addr = page_to_phys(pg);
U>g_addr отдаём устройству.
U>Кажется, теперь правильно?

Ну насколько я понял задачу — да.

U>На третьем кольце процессам выделяется виртуальная память. Реально ей физическая память выделяется только при обращении и то только той странице, которой принадлежит адрес. НО когда память (список страничек) оказывается драйвере в fun_direct_io она уже реально отображена в RAMе.


Да, Вы абсолютно правы. Все именно так. При вызове direct_IO (в отличие от write) VFS делает много полезных вещей (в принципе их можно делать и самому во write, но удобнее положиться на VFS): для всего диапазона буфера выделяет память(как Вы заметили — она может быть не не выделена) и закрепляет страницы в памяти на время выполнения Вашей функции direct_IO.

U>Т.е. если у меня на борту 256MB то адреса этих страничек должны быть в диапазоне от 0x0 до 0x100000000 (256MB). И устройство запросто может к ним адресоваться. Всё ли верно?


Немного не так. Процессор для запросов на чтение/запись памяти выставляет на своей адресной шине некий адрес — это шинный адрес или физический адрес. Далее некое устройство на этой же шине может порулить запрос еще дальше. На PC этим обычно занимается чипсет для которого определены таблицы "маршрутизации" — каждому шинному адресу соответствует свое устройство, в частности это может быть контроллер памяти. Обычно вся физическая память отображается чипсетом на несколько "окон" шинных адресов. Как именно — можно увидеть из вывода cat /proc/iomem.

Так исторически получилось, что на PC/XT в адресное пр-во размером 1 Мб должны были влезть и ОЗУ и видео-память и ПЗУ, поэтому в целях совместимости сейчас так получается, что физическая память "порезана" на окошки, окно в 640 кб — в начале физических адресов, потом часть пропущена, потом еще окна с физической памятью и т.д.

U>А что будет, если нет свободных страничек в системе и нет возможности отобразить их в RAM? Я предполагаю, что в этом случае система заблокирует процесс пока память не появиться? Правильно ли я предполагаю?


write выйдет с ошибкой ENOMEM, насколько я понимаю.

U>Еще я не понимаю. Когда память находится в ядре в фун. fun_direct_io (и тогда да же пока процесс в ней спит), память отображена на физические адреса и не свопируется.

U>Но когда система её снова может свопировать? Мне это нужно, что бы, когда память передаётся процессу, она могла при необходимости менеджером памяти отправляться в своп и не висеть мёртвым грузом в RAM. Нужны ли какие-то дополнительно манипуляции с памятью (например, munlock(…) )? Или только освобождать память?

Со страницами, переданными в direct_IO, ничего особенного делать не надо.
До вызова direct_IO VFS закрепляет (pin) страницы в памяти, по возвращению — делает unpin, т.е. делает возможным выгрузку в swap.

U>Не поделишься источниками информации?


Только исходный код, если честно.
С непривычки его, наверное, читать тяжело, но это проходит — было бы желание.
Ценность — в разы и десятки раз больше, чем чтение каких-либо книжек.

С Новым Годом!
Re[14]: Linux. Драйвер. Память.
От: unkn2000  
Дата: 05.01.04 13:40
Оценка:
Здравствуйте, Murr, Вы писали:

U>>Т.е. если у меня на борту 256MB то адреса этих страничек должны быть в диапазоне от 0x0 до 0x100000000 (256MB). И устройство запросто может к ним адресоваться. Всё ли верно?


M>Немного не так. Процессор для запросов на чтение/запись памяти выставляет на своей адресной шине некий адрес — это шинный адрес или физический адрес. Далее некое устройство на этой же шине может порулить запрос еще дальше. На PC этим обычно занимается чипсет для которого определены таблицы "маршрутизации" — каждому шинному адресу соответствует свое устройство, в частности это может быть контроллер памяти. Обычно вся физическая память отображается чипсетом на несколько "окон" шинных адресов. Как именно — можно увидеть из вывода cat /proc/iomem.


M>Так исторически получилось, что на PC/XT в адресное пр-во размером 1 Мб должны были влезть и ОЗУ и видео-память и ПЗУ, поэтому в целях совместимости сейчас так получается, что физическая память "порезана" на окошки, окно в 640 кб — в начале физических адресов, потом часть пропущена, потом еще окна с физической памятью и т.д.


А-а-а ну да. Понял. Согласен.
Но все же да же если все устройства (ПЗУ, видеопамять, ...) получат физические адреса перед RAM (но в принципе они всё равно отображаются (накладываются) на RAM), то всё равно адреса RAM не выйдут на моей системе(256MB) за пределы 1 ГB.
Но проблему я понял. В будущем буду иметь ввиду.

M>С Новым Годом!


С рождеством.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.