Page 19 of 46

Re: Тестируем поддержку USB

Posted: Fri Aug 26, 2011 11:34 pm
by CleverMouse
Если это критично для драйверной модели, IOCTL можно и реализовать. Мне просто жаль тратить своё время на реализацию функции, которую никто не вызывает и не собирается вызывать.

Блокировки из таймерных функций захватывать можно в том смысле, что никакими deadlock'ами это не угрожает. Если для захвата блокировки потенциально требуется длительное время, тогда это возможно, неправильная блокировка относится к категории длительных операций.

Какой-то поток должен выполнять код таймерных функций. Создавать для каждого вызова специальный поток слишком накладно. Создавать при загрузке специальный поток для всех таймерных функций неудобно в силу того, что при текущей организации потоков/процессов это ещё один элемент таблицы слотов, который в CPU будет уже третьим процессом по имени OS/IDLE.

При чём тут события, я не поняла. Можно набросок того, как это, по-твоему, должно выглядеть? Можно на псевдо-Си.

P.S. Скорее всего, это моё последнее сообщение на этой неделе, так что до понедельника.

Re: Тестируем поддержку USB

Posted: Sat Aug 27, 2011 12:50 am
by Serge
У меня другой подход. Не вызывать таймерную функцию, а активировать сигнализирующее событие.
Собственно событие

Code: Select all

typedef struct
{
    u32_t  code;
    u32_t  data[5];
}kevent_t;
С точки зрения ядра событие - объект ядра и принадлежит создавшему его потоку.

Code: Select all

struc EVENT
{
   .magic       dd ?   ;'EVNT'
   .destroy     dd ?   ;internal destructor
   .fd          dd ?   ;next object in list
   .bk          dd ?   ;prev object in list
   .pid         dd ?   ;owner id

   .id          dd ?   ;event uid
   .state       dd ?   ;internal flags
   .code        dd ?
                rd 5
   .size     =  $ - .magic
   .codesize =  $ - .code
}

Немного про события ядра и не только
Код находится в gui/event.inc
Основные функции: create_event() raise_event() wait_event() get_event_ex()
Используется в звуковой подсистеме (RaiseEvent в mixer.asm и WaitEvent в infinity.asm) видеодрайвере (radeon_fence.c) и в драйвере drivers/usb/uhci (GetEvent в usb.c RaiseEvent и WaitEvent в hcd.inc)
Пример работы в Infinity.
Драйвер создаёт событие для каждого звукового буфера. Функции refill() и refill_ring(), вызываемые из обработчика прерываний, активируют события когда необходимо пополнить звуковой буфер. Приложение ожидает событие через вызов 68.14 get_event_ex() или это делает wave_out() в режиме ядра через вызов WaitEvent.
В драйвере usb.
Для каждого запроса request_t создаётся событие. Обработчик прерываний hc_interrupt() проходит по списку запросов и для каждого завершённого запроса активирует событие (там нет обработки ошибок и всё очень примитивно сделано) Поток драйвера в бесконечном цикле выбирает события GetEvent(), получает указатель на запрос и вызывает его обработчик.

Re: Тестируем поддержку USB

Posted: Sat Aug 27, 2011 2:24 pm
by art_zh
В принципе, события можно активировать аппаратно, с помощью механизма MSI.

При этом не требуется ни отдельный IRQ-канал, ни промежуточная обработка прерывания в драйвере.

Re: Тестируем поддержку USB

Posted: Sat Aug 27, 2011 2:34 pm
by Serge
Это подойдёт только для простых флагов. В данном случае надо перекидывать объект из одного списка в другой (у меня была паранойя в связи с возможной утечкой памяти в ядре). Плюс надо сделать правильную блокировку, как в мьютексах. Последнее и не подходит для аппаратного решения. Или monitor/mwait нам в помощь ?

Re: Тестируем поддержку USB

Posted: Sat Aug 27, 2011 6:07 pm
by art_zh
CleverMouse
Приношу 1000 извинений за очередной оффтопик в Вашей теме.

Serge
Я всегда думал, что системные события и должны быть по сути простыми флагами.
Перекидыванием объектов должны заниматься соответствующие драйверы и/или системные службы.

Насчет мьютексов -- в них нет никакой необходимости: можно явно размещать мессиджи только в некэшируемых страницах.

А можно и не размещать: MSI-пакет всегда передается со сброшенным атрибутом NoSnoop, так что после передачи кэш будет автоматически объявлен недостоверным.

Re: Тестируем поддержку USB

Posted: Sat Aug 27, 2011 10:13 pm
by Serge
art_zh
Хозяйки нет, гуляем :D
Насчет мьютексов -- в них нет никакой необходимости: можно явно размещать мессиджи только в некэшируемых страницах.
Как синхронизировать доступ к ресурсам без мьютексов ?
Некешируемая страница - замечательный тормоз. А если там ещё опрос в цикле, так вообще ядро встанет. В некоторых ситуациях простота приводит к воровству. Взять наш старый мьютекс
align 4
wait_mutex:
;;Maxis use atomic bts for mutex 4.4.2009
push eax
push ebx
.do_wait:
bts dword [ebx],0
jnc .locked
call change_task
jmp .do_wait
.locked:
pop ebx
pop eax
ret
Всё просто и замечательно работает, если не считать того, что поток получает управление независимо от состояния блокировки. Подумаешь, два лишних переключения контекста. Но каждое переключение контекста это сброс TLB. А сброс TLB даёт пенальти равноценное копированию одной страницы (Это не я придумал, программисты QNX RIM. Если в ядре начнут одновременно крутиться десятки таких блокировок, потеря тактов станет заметна.

Кстати, описанный тобой способ доставки сообщения уже используется в Колибри. Правда без MSI.

Re: Тестируем поддержку USB

Posted: Sat Aug 27, 2011 11:18 pm
by art_zh
Serge
Ты не понял (или это я за 10 лет разучился внятно излагать простые вещи по-русски), я вовсе не против мьютексов, и тем более не против твоего оптимизированного варианта мьютексов.

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

Если состояние события не изменилось - структура EVENT будет выбрана из кэша мгновенно.
Если же в результате MSI-цикла что-то (допустим, EVENT.state) в этой структуре было перезаписано по инициативе внешнего устройства - тогда соответствюущий кэш-блок будет объявлен недостоверным, и контроллер кэша сразу же перезагрузит его. Такой механизм называется Cache Snooping и стандартизирован для всех платформ с PCI 2.2+ и PCI Express.

В самом худшем случае - при следующем опросе процессору придется подождать пару наносекунд, пока не перезагрузится этот кэш-блок.
Но скорее всего - не придется, к этому моменту новое состояние уже будет сидеть в кэше.
Кстати, описанный тобой способ доставки сообщения уже используется в Колибри. Правда без MSI.
???
Железо пишет прямо в список событий, без активации IRQ ?

Re: Тестируем поддержку USB

Posted: Sat Aug 27, 2011 11:33 pm
by Serge
art_zh
Теперь понял. К сожалению при текущей реализации EVENT не взлетит. Там надо не только флаги установить, но ещё и перекинуть из одного списка в другой.

Re: Тестируем поддержку USB

Posted: Sat Aug 27, 2011 11:42 pm
by art_zh
Serge
Теперь и до меня дошло :)
Жаль, было бы красиво.
И с APIC не пришлось бы заморачиваться, и механизм был бы независимый от конкретного чипсета.
Правда, только для новых платформ.

Re: Тестируем поддержку USB

Posted: Sun Aug 28, 2011 12:16 am
by Serge
Железо пишет прямо в список событий, без активации IRQ ?
Ага, заинтриговал :D Не совсем, или совсем нет :D Писать в список событий не получается, почему я уже указывал. У gpu есть блок специальных scratch регистров. Для них установливается базовый адрес и запись в этот регистр приводит к записи в память CPU. Если не ошибаюсь scrath -> scratch_base+i*8. В драйвере механизм используется для подтверждения выполнения командных буферов. Каждому буферу присваивается свой ID (простой монотонный счетчик) и после команд на исполнение буфера идёт команда записи ID в scratch регистр

Code: Select all

Псевдокод командного процессора
execute buffer_1
wait idle
write scratch[], 1
generate interrupt ; совсем не обязательно, но тогда придётся крутиться в режиме опроса. 

execute buffer_2
wait idle
write scratch[], 2
generate interrupt
...
execute buffer N
wait idle[
write scratch[], N 
generate interrupt
...
Драйвер читает значение id из памяти, проходит по списку запросов, и завершает все запросы у которых id меньше. Они уже выполнены. Конечно таким образом в EVENT не запишешь, но простой мьютекс разблокировать можно. Хотя это будет изврат.
И с APIC не пришлось бы заморачиваться, и механизм был бы независимый от конкретного чипсета.
Без прерываний и настоящей блокировки ожидающих потоков будут очень большие накладные расходы на пустой опрос. Для системы с одним - двумя потоками нормально. Но при большем числе потоков будет страшное бутылочное горлышко. Заткнутое пробкой. Несколько лет назад попались тесты разных вариантов мьютексов. В случае опроса с переключением задач маштабируемость системы падает очень быстро.

Re: Тестируем поддержку USB

Posted: Sun Aug 28, 2011 2:24 am
by art_zh
Serge wrote:Писать в список событий не получается, почему я уже указывал.
....
К сожалению при текущей реализации EVENT не взлетит. Там надо не только флаги установить, но ещё и перекинуть из одного списка в другой
Это в текущей реализации. Любую реализацию можно при необходимости изменить. Или дополнить.
Например, если приложение ожидает событий с номером... ну, скажем, 16-31 :wink: , тогда ничего никуда перекидывать не нужно, достаточно просто проверить состояние таких аппаратно-устанавливаемых флажков, и если там не ноль - передать их приложению.
А можно и что-нибудь поумнее придумать, тут широкий простор для полета фантазии.
Serge wrote:У gpu есть блок специальных scratch регистров. Для них установливается базовый адрес и запись в этот регистр приводит к записи в память CPU. Если не ошибаюсь scrath -> scratch_base+i*8.
...
Конечно таким образом в EVENT не запишешь, но простой мьютекс разблокировать можно. Хотя это будет изврат.

У меня контроллер камеры может ввинтить каждый 100-й кадр прямо во фреймбуфер Колибри, в правый верхний угол.
Подобным образом можно и в EVENT записать, и перекинуть его в другой список.
Так вообще можно все ядро на уши поставить,- ты ведь говоришь про режим, в котором Bus Master в системе - царь и бог. Но что это за scratch-регистры и какой беспредел они могут замутить - зависит не столько от драйвера, сколько от конкретного железа.

А функционал MSI-передач на 100% на 90% определяется именно драйвером. Потому что формат MSI-запроса строго стандартизирован, а его регистры (и адресный, и регистр данных) расположены в конфигспейсе и могут единообразно контролироваться системой. И зная этот формат, можно переадресовать MSI из APIC туда, где его обработка будет более эффективной.

Без прерываний и настоящей блокировки ожидающих потоков будут очень большие накладные расходы на пустой опрос. Для системы с одним - двумя потоками нормально. Но при большем числе потоков будет страшное бутылочное горлышко. Заткнутое пробкой. Несколько лет назад попались тесты разных вариантов мьютексов. В случае опроса с переключением задач маштабируемость системы падает очень быстро.
На каких задачах тестировались эти "разные варианты" мьютексов?
Ведь есть же совершенно разные классы событий - для событий со среднебыстрым временем ожидания (LPT, HDD, USB-диски, видео, звук, сеть) имеет смысл задействовать IRQ, это очевидно.
Зато устройства с длительным временем ожидания отклика (HID, USB-камеры, низкободный RS232), так же как и RT-устройства (которых по-уму в системе должно быть не более одной штуки) только выиграют от замены прерывания на опрос аппаратно-устанавливаемого флага.

Я говорил не про абстрактный "случай опроса с переключением задач", а про конкретный вариант опроса перед переключением в ожидающую задачу.
Нет события - не будет и переключения. Случилось "редкое" событие (нажали кнопку) - лишнее прерывание не генерится, TSS не перегружается, процессор вообще ничего не заметит. Всего лишь установится флажок, который будет опрошен при следующем прерывании таймера. Вторую кнопку за это время нажать невозможно.

Re: Тестируем поддержку USB

Posted: Mon Aug 29, 2011 1:27 pm
by CleverMouse
Serge, и какое отношение всё это имеет к задачам "каждые 50мс генерировать нажатие на клавишу" и "каждую секунду опрашивать контроллер, не извлёк ли пользователь болванку"?

Re: Тестируем поддержку USB

Posted: Mon Aug 29, 2011 2:42 pm
by Serge
CleverMouse
Такое, что даже простая задача "каждую секунду опрашивать контроллер" решается разными способами.
Можно в цикле опрашивать таймер, проверять контроллер раз в секунду, а остальное время переключать задачу.
Или делегировать опрос таймера osloop. Всё равно ему нечего делать :roll:
Или отсортировать таймеры в списке по времени срабатывания и проверять только когда подойдёт время.
Наконец, проверять таймеры когда меняется счётчик тиков. То есть в обработчике прерываний от таймера и доставлять сообщение в поток через систему событий.

У меня вот вопрос, каким образом поток usb получает сообщение о прерывании от обработчика прерываний ? Через опрос глобальной переменной ?

Re: Тестируем поддержку USB

Posted: Mon Aug 29, 2011 3:09 pm
by CleverMouse
Возмущение по поводу core/timers.inc предполагает, что текущее решение этой задачи тебя чем-то не устраивает. Текущее решение - делегировать опрос osloop, точнее, через косвенный вызов функции таймера, при этом нет лишних переключений задач. Вещи типа сортировки таймеров по времени срабатывания или создания специальной таблицы таймеров, адресуемых несколькими младшими битами текущего времени, - это оптимизации, которые не затрагивают ни общей схемы работы, ни внешнего API и потому могут быть выполнены в любой момент; прямо сейчас, когда число таймеров мало, вряд ли имеет смысл ими заниматься. Если сигналить событие из обработчика таймера, мы возвращаемся к вопросу, какой поток должен это обрабатывать, - если главный, то непонятно, в чём преимущество перед текущей схемой, если специальный, то в CPU это будет уже третий поток по имени "OS/IDLE".

Поток usb спит на событии - одном, выделенном при создании потока, чтобы не задаваться вопросами "а что будет, если обработчик прерывания не сможет выделить новое событие" и "не требует ли выделение события захвата какой-нибудь блокировки, которую мог захватить поток как раз в тот момент, когда пришло прерывание" - с таймаутом. Таймаут обычно равен бесконечности в OHCI и интервалу опроса событий подключения/отключения в UHCI, иногда меняясь на 100мс и 10мс.

Re: Тестируем поддержку USB

Posted: Mon Aug 29, 2011 4:58 pm
by Serge
CleverMouse
Мне не нравится нагрузка таймерами osloop. Первоначально цикл использовался для обработки пользовательского ввода. Потом к нему добавили сеть. Теперь его можно загрузить вообще чем угодно.
Раз поток usb спит на событии то он может активироваться любым событием, в том числе и от таймера (через get_event_ex. Конечно не помешает некоторая доработка).
Моя позиция такая: если драйверу требуется периодически выполнять некоторые действия он должен создавать для этого отдельный поток. Часто таким драйверам уже необходима работа в собственном потоке, поэтому добавление туда обработки таймера не создаёт проблем.