Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Использование GetScatterGatherList  (Прочитано 10614 раз)
0 Пользователей и 3 Гостей смотрят эту тему.
chaika_sv
Участник

ru
Offline Offline
Пол: Мужской

« : 12-08-2010 12:56 » 

Всем привет!

Тема создаётся в продолжение этой темы.
Здесь я хочу попытаться выяснить, что же всё-таки делает функция GetScatterGatherList().

Есть у меня в драйвере обработчик запроса чтения IRP_MJ_READ.
В этом обработчике среди прочего я вычисляю необходимое кол-во регистров отображения:
Код:
	mapRegsNeeded = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(pIrp->MdlAddress), 
  pDevExt->ReadTotalLength - pDevExt->ReadOffset);

Затем определяю виртуальный адрес пользовательского буфера
Код:
	baseVa = MmGetMdlVirtualAddress(pIrp->MdlAddress + pDevExt->ReadOffset);

И запускаю непосредственно GetScatterGatherList()
Код:
status = pDevExt->pReadAdapter->DmaOperations->GetScatterGatherList(pDevExt->pReadAdapter, 
pDeviceObject,
pIrp->MdlAddress,
baseVa,
pDevExt->ReadLength,
ReadAdapterControl,
pIrp,
FALSE)// transfer to buffer from device

Вот создала эта функция Scatter-Gather список. Дальше автоматом вызывается функция ReadAdapterControl().

Вопросы.

Нужно ли в функции ReadAdapterControl() делать явное копирование данных в MDL-буфер?
Если не нужно, то в какой момент данные попадают в MDL-буфер?

Примечание. Вообще-то я думал, что функция GetScatterGatherList() как-то сама перемещает данные из устройства в MDL-буфер, а функция ReadAdapterControl() нужна только для того, чтобы сохранить указатель на S/G-список и перейти к следующему элементу S/G-списка. Но попробовал на практике и само собой после выполнения GetScatterGatherList() исходный MDL-буфер остался нетронутым. Поэтому и спрашиваю.


P.S. Очкарик, я прошу прощения, потому что наверно достал тут уже всех своими темами, но разобраться очень надо Скромно так...
« Последнее редактирование: 12-08-2010 12:58 от chaika_sv » Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #1 : 12-08-2010 15:34 » new

вы когда вызываете GetScatterGatherList - то в качестве третьего параметра передаете MDL пользовательского буфера. именно для этого буфера ищутся физически-непрерывные куски памяти и строится S/G-список. фактически он является описателем того же буфера но только для физической адресации страниц, из которых он и состоит. то есть физически это он же, просто для другого типа адресации.

PS я сегодня выдохся полностью, потом гляну еще раз код...
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
resource
Молодой специалист

ru
Offline Offline
Пол: Мужской

« Ответ #2 : 13-08-2010 06:33 » 

Цитата: chaika_sv
Код:
baseVa = MmGetMdlVirtualAddress(pIrp->MdlAddress + pDevExt->ReadOffset);

Что-то не очень понимаю вот это pIrp->MdlAddress + pDevExt->ReadOffset. Как это так? MdlAddress это ведь PMDL.

Сам по себе, MmGetMdlVirtualAddress - это макрос, который всего то и делает, что

Код:
(PCHAR)(Mdl->StartVa) + Mdl->ByteOffset
« Последнее редактирование: 13-08-2010 07:16 от resource » Записан
chaika_sv
Участник

ru
Offline Offline
Пол: Мужской

« Ответ #3 : 13-08-2010 09:25 » 

Цитата
Что-то не очень понимаю вот это pIrp->MdlAddress + pDevExt->ReadOffset. Как это так? MdlAddress это ведь PMDL.
Согласен. Видимо бага. Кстати, код это взят из примера... не я писал

Наверно правильно будет так:
Код:
	baseVa = MmGetMdlVirtualAddress(pIrp->MdlAddress) + pDevExt->ReadOffset;
?
Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #4 : 15-08-2010 17:21 » 

вот  еще MapTransfer() кажется посмотреть надо.... но точно не уверен
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
P_Egor
Новенький

ru
Offline Offline

« Ответ #5 : 07-10-2010 02:59 » 

Добрового всем времени суток!

Решил поднять эту тему, так как тоже "хочу попытаться выяснить, что же всё-таки делает функция GetScatterGatherList()"(с), но немного с другой стороны
В других, к сожалению, не было близких обсуждений. По рекомендации модераторов может создадим новую ветку.

Небольшое вступление.
В описании работы DMA очень часто упомянают, что для устройств не поддерживающих 64-битные трансферы при использовании 64-битных Windows или 32-битных Windows с PAE
scatter-gather список дробится на куски, при чём размер каждого куска списка ограничен количеством MAP-регистров системы, выделенных для DMA-адаптера функцией IoGetDmaAdapter.
Т.е. если функция IoGetDmaAdapter вернёт 16 буферов (у меня Windows 2008 Server так делает, а 64битная 7ка выдаёт 256 буферов), то вероятнее всего максимальный кусок scatter-gather списка
обрабатываемый зараз должен быть 64Кб (меньше для 1го и последнго трансфера и больше если вдруг Windows замапит подряд 2 страницы памяти, чего я ещё не встречал). При больших объёмах данных
(у меня может быть 12Мб каждые 16мс для одной железки)  надо обрабатывать очень много прерываний (в моём случае получается >10000 в секунду), что не очень красиво.
К тому же ограничение на 64-битную фдресацию памячти может быть не только у устройств, но и чипсет может не поддерживать полноценные 64-битные трансферы с шины PCI.
Если я не прав - поправьте.
Возращаясь к GetScatterGatherList:
У меня есть рабочий пример использования GetScatterGatherList, который в любых условиях генерит полный scatter-gather список:
1. Всё инициализируется без отхождений от рекомендаций использования функций в WDDK
Код:
static NTSTATUS StartDevice(
        PDEVICE_OBJECT               DeviceObject,
        PCM_FULL_RESOURCE_DESCRIPTOR FullResourceDescriptor,
        PCM_FULL_RESOURCE_DESCRIPTOR FullResourceDescriptorTranslated
    )
{
......................
......................
    RtlZeroMemory(&DeviceDescription, sizeof(DeviceDescription));
    DeviceDescription.Version             = DEVICE_DESCRIPTION_VERSION;
    DeviceDescription.Master              = TRUE;
    DeviceDescription.ScatterGather       = TRUE;
    DeviceDescription.DemandMode          = TRUE;
    DeviceDescription.AutoInitialize      = FALSE;
    DeviceDescription.Dma32BitAddresses   = TRUE;
    DeviceDescription.IgnoreCount         = FALSE;
    DeviceDescription.DmaChannel          = 0;
    DeviceDescription.InterfaceType       = PCIBus;
    DeviceDescription.DmaWidth            = Width32Bits;
    DeviceDescription.DmaSpeed            = Compatible;
    DeviceDescription.MaximumLength       = (ULONG)-1; // 0x100000; // number of bytes

    DeviceExtension->DmaAdapter = IoGetDmaAdapter(
            DeviceExtension->PhysicalDeviceObject,
            &DeviceDescription,
            &NumberOfMapRegisters
        ) ;
......................
......................
}
......................
static VOID StartIo(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
......................
        switch ( IrpStack->MajorFunction )
        {
            ................
            case IRP_MJ_WRITE:          ntStatus = ReadWrite(DeviceObject, Irp, TRUE);              break;
            case IRP_MJ_READ:           ntStatus = ReadWrite(DeviceObject, Irp, FALSE);             break;
            ................
        }
......................
}
......................
static NTSTATUS ReadWrite(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, BOOLEAN Write)
{
......................
......................
    ntStatus = DeviceExtension->DmaAdapter->DmaOperations->GetScatterGatherList(
            DeviceExtension->DmaAdapter,
            DeviceObject,
            Mdl,
            VirtualAddress,
            Length,
            (PDRIVER_CONTROL)GetScatterGatherListControl,
            MappedBuffer,
            Write
        );
2.  В фунции
Код:
static VOID GetScatterGatherListControl(
    IN PDEVICE_OBJECT       DeviceObject,
    IN PIRP                 Irp,
    IN PSCATTER_GATHER_LIST ScatterGather,
    IN PVOID                Context)
Собираем список scatter-gather, добавляя кусочки трансферов из полученных замапленных областей, но если вызов функции для конкретного трасфера не первый, то список не перезатираем, а добавлям в конец новые элементы. В конце функции не посылаем, получившийся список нашему устройсву, а просто выходим из функции.
3. Если не возникли проблемы, то в функции ReadWrite  ставим стасут занято и
Код:
.............
    if ( ntStatus == STATUS_PENDING )
    {
        KIRQL OldIrql;

        IoAcquireCancelSpinLock(&OldIrql);
        IoSetCancelRoutine(Irp, CancelRoutine);
        IoReleaseCancelSpinLock(OldIrql);

        IoMarkIrpPending(Irp);
        return ntStatus;
    }
.................
}
А теперь самое интересное и непонятное: функция ReadWrite с одним и тем же IRP будет вызываться столько раз, насколько кусочков у нас разбит трансфер. Каждый кусочек в GetScatterGatherListControl будем маппироваться в новую область, т.е. не перезатирая старые map-регистры!
4. Когда в список соберётся весь трансфер - запускаем DMA.
5. После окончания DMA освобождаем память через PutScatterGatherList.

Кто-нибудь разбирался с таким поведением Windows?
Если это не баг, то зачем было мучить людей с map-регистрами?

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

Добавлено через 1 час, 31 минуту и 32 секунды:
а теперь самое интересное и непонятное: функция ReadWrite с одним и тем же IRP будет вызываться столько раз, насколько кусочков у нас разбит трансфер. Каждый кусочек в GetScatterGatherListControl будем маппироваться в новую область, т.е. не перезатирая старые map-регистры!
4. Когда в список соберётся весь трансфер - запускаем DMA.
5. После окончания DMA освобождаем память через PutScatterGatherList.
Извиняюсь за ошибку. Недосмотрел весь код. Схема работы немного другая:

1а. Делаем команду ReadFile (WriteFile) для куска данных не превышающего размер максимальный размер трансфера, т.е. 60КБ для 16 буфферов.

3а. Пока трансфер не окончен делаем переход на 1а.
4. Когда в список соберётся весь трансфер - запускаем DMA.
5. После окончания DMA освобождаем память через PutScatterGatherList.

Путь своеобразный, но! получается map-буфера выделяются для IRP трансфера, а не для канала.
« Последнее редактирование: 07-10-2010 04:30 от P_Egor » Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #6 : 07-10-2010 10:08 » 

P_Egor, доброго!
тема интересная, хотя сам с DMA почти не работал... хотя подготовится никогда не помешает)
пока не совсем понял логику происходящего - цель этого алгоритма какая?  
min/max размер запросов от приложения - какой и почему?
12Мбайт - логически, это связанные данные или  набор каких то пакетов, которые могут быть запрошены в различном порядке?
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
P_Egor
Новенький

ru
Offline Offline

« Ответ #7 : 07-10-2010 15:04 » 

Цитата
ока не совсем понял логику происходящего - цель этого алгоритма какая? 
Это просто одна интересная реализация ДМА трансферов. Я использую wdf и приходится использовать прерывания, и рассматриваю альтернативу.
Цитата
min/max размер запросов от приложения - какой и почему?
один трансфер обычно от 100КБ до 8МБ, зависит от типа данных в потоке. Одновременно может идти несколько потоков разного типа данных. В худшем случае суммарно приблизительно 12Мб на одну итерацию.
Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #8 : 07-10-2010 15:13 » 

все равно ничего не понял)
Делаем команду ReadFile (WriteFile) для куска данных не превышающего размер максимальный размер трансфера,
...
4. Когда в список соберётся весь трансфер - запускаем DMA. 
что значит пока соберется? почему бы сразу не дать полный?
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines