/* * Copyright (c) 2021, Pankaj R * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include #include #include #include #include namespace Kernel { struct DoorbellRegister { u32 sq_tail; u32 cq_head; }; class AsyncBlockDeviceRequest; class NVMeQueue : public IRQHandler , public RefCounted { public: static ErrorOr> try_create(u16 qid, u8 irq, u32 q_depth, OwnPtr cq_dma_region, NonnullRefPtrVector cq_dma_page, OwnPtr sq_dma_region, NonnullRefPtrVector sq_dma_page, Memory::TypedMapping db_regs); ErrorOr create(); explicit NVMeQueue(u16 qid, u8 irq, u32 q_depth, OwnPtr cq_dma_region, NonnullRefPtrVector cq_dma_page, OwnPtr sq_dma_region, NonnullRefPtrVector sq_dma_page, Memory::TypedMapping db_regs); bool is_admin_queue() { return m_admin_queue; }; bool handle_irq(const RegisterState&) override; void submit_sqe(struct NVMeSubmission&); u16 submit_sync_sqe(struct NVMeSubmission&); void read(AsyncBlockDeviceRequest& request, u16 nsid, u64 index, u32 count); void write(AsyncBlockDeviceRequest& request, u16 nsid, u64 index, u32 count); void enable_interrupts() { enable_irq(); }; void disable_interrupts() { disable_irq(); }; private: bool cqe_available(); void update_cqe_head(); void complete_current_request(u16 status); void update_cq_doorbell() { m_db_regs->cq_head = m_cq_head; } void update_sq_doorbell() { m_db_regs->sq_tail = m_sq_tail; } private: u16 m_qid {}; u8 m_cq_valid_phase { 1 }; u16 m_sq_tail {}; u16 m_prev_sq_tail {}; u16 m_cq_head {}; bool m_admin_queue { false }; u8 m_irq {}; u32 m_qdepth {}; Spinlock m_cq_lock { LockRank::Interrupts }; Spinlock m_sq_lock { LockRank::Interrupts }; OwnPtr m_cq_dma_region; NonnullRefPtrVector m_cq_dma_page; Span m_sqe_array; OwnPtr m_sq_dma_region; NonnullRefPtrVector m_sq_dma_page; Span m_cqe_array; OwnPtr m_rw_dma_region; Memory::TypedMapping m_db_regs; RefPtr m_rw_dma_page; Spinlock m_request_lock; RefPtr m_current_request; }; }