Где находятся команды NVMe внутри PCIe BAR?

Согласно спецификации NVMe, BAR имеет хвостовое и заголовочное поля для каждой очереди. Например:

  • Submission Queue y Tail Doorbell (SQyTDBL):
    • Start: 1000h + (2y * (4 << CAP.DSTRD))
    • Конец: 1003h + (2y * (4 << CAP.DSTRD))
  • Submission Queue y Head Doorbell (SQyHDBL):
    • Start: 1000h + ((2y + 1) * (4 << CAP.DSTRD))
    • Конец: 1003h + ((2y + 1) * (4 << CAP.DSTRD))

Это сама очередь или просто указатели? Это правильно? Если это очередь, я предполагаю, что DSTRD указывает максимальное длина всех очередей.

Кроме того, в спецификации говорится о двух необязательных областях: буфер памяти хоста (HMB) и буфер памяти контроллера (CMB).

  • HMB: область в DRAM хоста (корень PCIe)
  • CMB: область в DRAM контроллера NVMe (внутри SSD)

Если оба варианта являются необязательными, то где он находится? Поскольку конечная точка PCIe работает только с полосами BAR и заголовками PCI, я не вижу другого места, где они могли бы располагаться, кроме BAR.


person tweak    schedule 20.02.2020    source источник


Ответы (1)


Извините, но я делаю это по памяти, но я реализовал хост FPGA NVMe, поэтому, надеюсь, моей памяти будет достаточно, чтобы ответить на ваши вопросы и многое другое, если я ошибаюсь, хотя вы, по крайней мере, знаете почему. Я предоставлю справочные разделы из спецификации, которые вы можете найти здесь. https://nvmexpress.org/wp-content/uploads/NVM-Express-1_4-2019.06.10-Ratified.pdf Также в качестве примечания, прежде чем я действительно отвечу на ваш вопрос, я хочу прояснить некоторую путаницу, понимание спецификации занимает некоторое время, я честно рекомендую прочитать ее внизу несколько последних разделов помогают дать контекст для первых нескольких, как бы странно это ни звучало.

  1. Это очереди отправки и завершения, в частности хвост подочереди и заголовок очереди завершения соответственно (РАЗДЕЛ 3.1). Подробнее об этом позже. Я просто хотел исправить ошибочное представление о том, что вы обращаетесь к заголовку очереди отправки в качестве хоста, а не только контроллер (традиционно диск). Простое напоминание - это то, что вы просите диск сделать что-то, а завершение - это драйв, рассказывающий вам, как все прошло. Прочтите РАЗДЕЛ 7.2 для получения дополнительной информации.

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

28h 2Fh Базовый адрес очереди передачи администратора ASQ

30h 37h Базовый адрес очереди завершения администратора ACQ

  1. Ваше заявление о DSTRD - это огромная ошибка в понимании. Это поле из регистра возможностей (0x0) Рисунок 3.1.1. Это поле является контроллером (диском), сообщающим вам "шаг дверного звонка", который говорит, сколько байтов находится между каждым дверным звонком, я никогда не видел, чтобы диск сообщал что-либо, кроме 0 для этого значения, так как хорошо, зачем вам оставлять мертвое пространство между регистрами дверного звонка.

  2. Пожалуйста, будьте осторожны с размером ваших записей, по моему опыту, большинство накопителей NVMe требуют, чтобы вы отправляли записи как минимум 2 слова (8 байт), даже если вы собираетесь отправить только 1 слово данных, просто примечание.

  3. Чтобы действительно помочь вам использовать это устройство в качестве хоста, обратитесь к РАЗДЕЛУ 7.6.1, чтобы найти последовательность инициализации. Обратите внимание, как вы должны настроить несколько регистров, прочитать определенные параметры и другие подобные вещи.

  4. Предполагая, что вы или кто-то другой выполнили инициализацию, позвольте мне теперь ответить на суть вашего вопроса, как использовать эти очереди. Дело в том, что этот ответ охватывает МНОГИЕ разделы спецификации и является его ядром. Итак, на этом я собираюсь разбить его как можно лучше для простой команды записи. Обратите внимание, что вы НЕ МОЖЕТЕ писать, пока не создадите очереди, используя очереди администратора, которые используют разные коды операций из другого раздела спецификации, извините, я не могу записать все это.

ШАГИ ПО ЗАПИСИ ДАННЫХ НА ДИСК NVMe.

  1. При создании очереди отправки вы указываете размер этой конкретной очереди. Это количество команд, которые могут быть одновременно помещены в очередь для обработки. Наряду с этим вы укажете базовый адрес очереди. Итак, для этого примера предположим, что вы установили базовый адрес на 0x1000_0000 и размер 16 (0x10). Рисунок 105 позволяет нам узнать, что каждая запись очереди отправки имеет размер 64 байта (0x40), поэтому запись 0 очереди находится на 0x1000_0000, запись 1 находится на 0x1000_0040 2 0x1000_0080 и так далее для наших 16 записей, затем он возвращается назад .

  2. Сначала вы сохраните данные для записи, скажем, вам было дано 512 байтов (0x200) данных для записи. Поэтому для простоты вы помещаете эти данные в адреса 0x2000_0000 - 0x2000_0200.

  3. Вы создаете команду очереди отправки. Это непростой процесс. Я не собираюсь документировать все это для вас, но понимаю, что вы должны ссылаться на рисунок 104, рисунок 346 и раздел 6.15. Однако этого недостаточно. Вам также нужно будет понять, что PRP и SGL вы используете (с PRP легче начать). NLB (количество логических блоков), которые определяют ваш размер записи, с NVMe вы не указываете записи в байтах, но в терминах NLB, размер которых определяется контроллером (диском), он может реализовывать несколько размеров NLB, но это до диск, а не вы в качестве хоста, вам просто нужно выбрать то, что он поддерживает Раздел 5.15.2.1, Рисунок 245. Вы хотите посмотреть на идентификационное пространство имен, чтобы узнать размер LBA (адреса логического блока), это приведет вас к кроличьей норе, чтобы определить фактический размер, но ничего страшного, информация там есть.

  4. Итак, вы закончили этот беспорядок и создали команду отправки. Предположим, хост уже выполнил 2 команды в этой очереди (при запуске это будет 0, я выбираю 2, чтобы было понятнее в моем примере). Теперь вам нужно разместить эту команду по адресу 0x1000_0080.

  5. Теперь предположим, что это очередь 1 (из приведенного вами уравнения номер очереди - это значение y. Обратите внимание, что очередь 0 - это очередь администратора). Что вам нужно сделать, так это нажать на дверной звонок очереди отправки контроллеров, чтобы сказать, сколько команд сейчас загружено (таким образом, вы можете поставить в очередь сразу несколько и сообщить диску только тогда, когда вы готовы). В данном случае это число 2. Значит, вам нужно записать значение 2 в регистр 0x1008.

  6. На этом этапе привод уйдет. ага, хозяин сказал мне, что нужно получить новые команды. Таким образом, контроллер перейдет на базовый адрес очереди + размер команд * 2 и получит 64 байта данных, также известных как 1 команда (адрес 0x1000_0080). Контроллер декодирует эту команду как запись, что означает, что контроллер (диск) должен прочитать данные с некоторого адреса и поместить их в память, где ему было сказано. Это означает, что ваша команда записи должна указать диску перейти по адресу 0x2000_0000 и прочитать 512 байт данных, и это произойдет, если вы включите шину PCIe. На этом этапе накопитель заполнит запись очереди завершения (16 байтов, указанных в Разделе 4.6) и поместит ее в адрес очереди завершения, который вы указали при создании очереди (плюс 0x20, поскольку это 2-е завершение). . Затем контроллер сгенерирует прерывание MSI-X.

  7. На этом этапе вы должны перейти к месту, где была размещена очередь завершения, и прочитать ответ, чтобы проверить статус, а также, если вы поставили в очередь несколько отправок, проверьте SQID, чтобы узнать, что было завершено, поскольку задания могут завершаться не по порядку. Затем вы должны написать в заголовок очереди завершения (0x100C), чтобы указать, что вы получили очередь завершения (успешно или неудачно). Обратите внимание, что здесь вы никогда не взаимодействуете с заголовком очереди отправки (это зависит от контроллера, поскольку только он знает, когда была обработана запись очереди отправки), и только контроллер помещает вещи в хвост очереди завершения, поскольку только он может создавать новые записи.

Мне жаль, что это так долго и плохо отформатировано, но, надеюсь, теперь вы немного лучше понимаете NVMe, сначала это немного беспорядок, но как только вы его получите, все это станет понятным. Просто помните, что в моем примере предполагалось, что вы создали очередь, базовой линии которой не существует. Сначала вам нужно настроить очереди отправки и завершения администратора (0x28 и 0x30), которые имеют идентификатор очереди 0, поэтому дверной звонок в хвостовой / головной части имеет адрес 0x1000,0x1004 соответственно. Затем вы должны обратиться к разделу 5, чтобы найти коды операций, чтобы что-то произошло, но я верю, что вы сможете понять это из того, что я вам дал. Если у вас есть еще вопросы, оставьте комментарий, и я посмотрю, что я могу сделать.

person arduic    schedule 01.03.2020