_base_build_nvme_prp(9)

πŸ“¦ version 7.3.1, in nixpkgs unstable

✨ _base_build_nvme_prp(9) ✨

okay so bestie, this one is a Kernel API Manual situation and it's lowkey kind of iconic. It's all about NVMe end devices building a native SGL (aka an NVMe PRP). We're in the _base_build_nvme_prp function and honestly the whole thing is giving "I speak fluent hardware" energy. πŸ’…


πŸ¦‹ SYNOPSIS πŸ¦‹

void _base_build_nvme_prp (struct MPT3SAS_ADAPTER *ioc , u16 smid ,
Mpi26NVMeEncapsulatedRequest_t *nvme_encap_request , dma_addr_t
data_out_dma , size_t data_out_sz , dma_addr_t data_in_dma , size_t
data_in_sz );

Bestie this signature is long but it's fine, we're gonna go through every single parameter together, no skipping, no cap. πŸ’–


πŸŽ€ ARGUMENTS πŸŽ€

  • ioc β€” the per adapter object, like the main character of this whole operation tbh
  • smid β€” the system request message index, used for getting the associated SGL
  • nvme_encap_request β€” the NVMe request msg frame pointer, aka where the encapsulated request lives
  • data_out_dma β€” physical address for WRITES, periodt πŸ’…
  • data_out_sz β€” the data xfer size for WRITES
  • data_in_dma β€” physical address for READS, yes queen
  • data_in_sz β€” the data xfer size for READS

🌸 DESCRIPTION 🌸

Alright so here's the tea. The native SGL gets built starting in the very first PRP entry of the NVMe message β€” that's PRP1 in case you were wondering β€” and the whole vibe depends on how much data we're describing. If the data buffer is small enough to be entirely described by PRP1, then PRP2 doesn't even get used, she gets to rest. If needed though, PRP2 steps in to describe a larger data buffer. And if the data buffer is too large to describe using the two PRP entries inside the NVMe message?? Then PRP1 describes the first data memory segment, and PRP2 contains a pointer to a PRP list located elsewhere in memory to describe the remaining data memory segments. And yes, that PRP list will be contiguous. Slay. ✨

So for NVMe devices, the native SGL is a Physical Region Page (PRP). A PRP is literally a list of PRP entries that describe a number of noncontiguous physical memory segments as a single memory buffer, just like a SGL does. BUT β€” and this is important bestie β€” this function is only used by the IOCTL call, so the memory given will be guaranteed to be contiguous. There is no need to translate non-contiguous SGL into a PRP in this case. All PRPs will describe contiguous space that is one page size each. No drama, no fragmentation, just vibes. 🫢

Every NVMe message contains two PRP entries, fyi:

  • The first one, PRP1, either contains a PRP list pointer or a PRP element β€” it depends on the command, main character energy honestly.
  • PRP2 contains the second PRP element if the memory being described fits within 2 PRP entries, or a PRP list pointer if the PRP spans more than two entries.

A PRP list pointer contains the address of a PRP list, structured as a linear array of PRP entries. Each PRP entry in this list describes a segment of physical memory. It's giving organized queen. πŸ‘‘

Now, each 64-bit PRP entry comprises an address and an offset field. The address always points at the beginning of a 4KB physical memory page, and the offset describes where within that 4KB page the memory segment begins. Only the first element in a PRP list may contain a non-zero offset, implying that all memory segments following the first begin at the start of a 4KB page. Rules are rules. 🌈

Each PRP element normally describes 4KB of physical memory, with exceptions for the first and last elements in the list. If the memory being described by the list begins at a non-zero offset within the first 4KB page, then the first PRP element will contain a non-zero offset indicating where the region begins within the 4KB page. The last memory segment may end before the end of the 4KB segment, depending upon the overall size of the memory being described by the PRP list. It's giving "it depends" but make it hardware. πŸ’€

And since PRP entries lack any indication of size, the overall data buffer length is used to determine where the end of the data memory buffer is located, and how many PRP entries are required to describe it. So basically the length does all the heavy lifting. Ngl that's kind of a slay design choice. πŸ”₯


πŸ’œ SEE ALSO πŸ’œ

Okay so this function has so many friends, it's literally the most connected function in the driver. Here's the whole group chat:

Kernel file ./drivers/scsi/mpt3sas/mpt3sas_base.c

And the extended fam:

mpt3sas_base_check_cmd_timeout(9), _scsih_set_fwfault_debug(9), _base_readl_aero(9), _base_clone_reply_to_sys_mem(9), _base_clone_mpi_to_sys_mem(9), _base_clone_to_sys_mem(9), _base_get_chain(9), _base_get_chain_phys(9), _base_get_buffer_bar0(9), _base_get_buffer_phys_bar0(9), _base_get_chain_buffer_dma_to_chain_buffer(9), _clone_sg_entries(9), mpt3sas_remove_dead_ioc_func(9), _base_sync_drv_fw_timestamp(9), _base_fault_reset_work(9), mpt3sas_base_start_watchdog(9), mpt3sas_base_stop_watchdog(9), mpt3sas_base_fault_info(9), mpt3sas_base_coredump_info(9), mpt3sas_base_wait_for_coredump_completion(9), mpt3sas_halt_firmware(9), _base_sas_ioc_info(9), _base_display_event_data(9), _base_sas_log_info(9), _base_display_reply_info(9), mpt3sas_base_done(9), _base_async_event(9), _base_get_cb_idx(9), mpt3sas_base_pause_mq_polling(9), mpt3sas_base_resume_mq_polling(9), mpt3sas_base_mask_interrupts(9), mpt3sas_base_unmask_interrupts(9), _base_process_reply_queue(9), mpt3sas_blk_mq_poll(9), _base_interrupt(9), _base_irqpoll(9), _base_init_irqpolls(9), _base_is_controller_msix_enabled(9), mpt3sas_base_sync_reply_irqs(9), mpt3sas_base_release_callback_handler(9), mpt3sas_base_register_callback_handler(9), mpt3sas_base_initialize_callback_handler(9), _base_build_zero_len_sge(9), _base_add_sg_single_32(9), _base_add_sg_single_64(9), _base_get_chain_buffer_tracker(9), _base_build_sg(9), base_make_prp_nvme(9), _base_check_pcie_native_sgl(9), _base_add_sg_single_ieee(9), _base_build_zero_len_sge_ieee(9), _base_build_sg_scmd(9), _base_build_sg_scmd_ieee(9), _base_build_sg_ieee(9), _base_config_dma_addressing(9), _base_check_enable_msix(9), mpt3sas_base_free_irq(9), _base_request_irq(9), _base_assign_reply_queues(9), _base_check_and_enable_high_iops_queues(9), mpt3sas_base_disable_msix(9), _base_alloc_irq_vectors(9), _base_enable_msix(9), mpt3sas_base_unmap_resources(9), mpt3sas_base_check_for_fault_and_issue_reset(9), mpt3sas_base_map_resources(9), mpt3sas_base_get_msg_frame(9), mpt3sas_base_get_sense_buffer(9), mpt3sas_base_get_sense_buffer_dma(9), mpt3sas_base_get_pcie_sgl(9), mpt3sas_base_get_pcie_sgl_dma(9), mpt3sas_base_get_reply_virt_addr(9), _base_get_msix_index(9), _base_get_high_iops_msix_index(9), mpt3sas_base_get_smid(9), mpt3sas_base_get_smid_scsiio(9), mpt3sas_base_get_smid_hpr(9), mpt3sas_base_free_smid(9), _base_mpi_ep_writeq(9), _base_writeq(9), _base_set_and_get_msix_index(9), _base_put_smid_mpi_ep_scsi_io(9), _base_put_smid_scsi_io(9), _base_put_smid_fast_path(9), _base_put_smid_hi_priority(9), mpt3sas_base_put_smid_nvme_encap(9), _base_put_smid_default(9), _base_put_smid_scsi_io_atomic(9), _base_put_smid_fast_path_atomic(9), _base_put_smid_hi_priority_atomic(9), _base_put_smid_default_atomic(9), _base_display_OEMs_branding(9), _base_display_fwpkg_version(9), _base_display_ioc_capabilities(9), mpt3sas_base_update_missing_delay(9), _base_update_ioc_page1_inlinewith_perf_mode(9), _base_get_event_diag_triggers(9), _base_get_scsi_diag_triggers(9), _base_get_mpi_diag_triggers(9), _base_get_master_diag_triggers(9), _base_check_for_trigger_pages_support(9), _base_get_diag_triggers(9), _base_update_diag_trigger_pages(9), _base_assign_fw_reported_qd(9), mpt3sas_atto_validate_nvram(9), mpt3sas_atto_get_sas_addr(9), mpt3sas_atto_init(9), _base_static_config_pages(9), mpt3sas_free_enclosure_list(9), _base_release_memory_pools(9), mpt3sas_check_same_4gb_region(9), _base_reduce_hba_queue_depth(9), _base_allocate_pcie_sgl_pool(9), _base_allocate_chain_dma_pool(9), _base_allocate_sense_dma_pool(9), _base_allocate_reply_pool(9), _base_allocate_reply_free_dma_pool(9), _base_allocate_reply_post_free_array(9), base_alloc_rdpq_dma_pool(9), _base_allocate_memory_pools(9), mpt3sas_base_get_iocstate(9), _base_wait_on_iocstate(9), _base_dump_reg_set(9), _base_wait_for_doorbell_int(9), _base_wait_for_doorbell_ack(9), _base_wait_for_doorbell_not_used(9), _base_send_ioc_reset(9), mpt3sas_wait_for_ioc(9), _base_handshake_req_reply_wait(9), mpt3sas_base_sas_iounit_control(9), mpt3sas_base_scsi_enclosure_processor(9), _base_get_port_facts(9), _base_wait_for_iocstate(9), _base_get_ioc_facts(9), _base_send_ioc_init(9), mpt3sas_port_enable_done(9), _base_send_port_enable(9), mpt3sas_port_enable(9), _base_determine_wait_on_discovery(9), _base_unmask_events(9), _base_event_notification(9), mpt3sas_base_validate_event_type(9), mpt3sas_base_unlock_and_get_host_diagnostic(9), mpt3sas_base_lock_host_diagnostic(9), _base_diag_reset(9), mpt3sas_base_make_ioc_ready(9), _base_make_ioc_operational(9), mpt3sas_base_free_resources(9), mpt3sas_base_attach(9), mpt3sas_base_detach(9), _base_pre_reset_handler(9), _base_clear_outstanding_mpt_commands(9), _base_clear_outstanding_commands(9), _base_reset_done_handler(9), mpt3sas_wait_for_commands_to_complete(9), _base_check_ioc_facts_changes(9), mpt3sas_base_hard_reset_handler(9)


πŸ’‘ tiny note bestie: this whole page is from ./drivers/scsi/mpt3sas and it's dated October 2026 β€” so like, future vibes but also very real kernel documentation. The mpt3sas squad is huge and they all live in this one driver. Respect. 🫑

Stay slay, stay contiguous, and remember: PRP1 is the first entry, PRP2 is the backup dancer, and the PRP list is the whole ensemble. Periodt. πŸ’–βœ¨πŸŽ€

πŸ“¦ where this page comes from