NVMeQueue.cpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /*
  2. * Copyright (c) 2021, Pankaj R <pankydev8@gmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <Kernel/Arch/Delay.h>
  7. #include <Kernel/Devices/Storage/NVMe/NVMeController.h>
  8. #include <Kernel/Devices/Storage/NVMe/NVMeInterruptQueue.h>
  9. #include <Kernel/Devices/Storage/NVMe/NVMePollQueue.h>
  10. #include <Kernel/Devices/Storage/NVMe/NVMeQueue.h>
  11. #include <Kernel/Library/StdLib.h>
  12. namespace Kernel {
  13. ErrorOr<NonnullLockRefPtr<NVMeQueue>> NVMeQueue::try_create(NVMeController& device, u16 qid, Optional<u8> irq, u32 q_depth, OwnPtr<Memory::Region> cq_dma_region, OwnPtr<Memory::Region> sq_dma_region, Doorbell db_regs, QueueType queue_type)
  14. {
  15. // Note: Allocate DMA region for RW operation. For now the requests don't exceed more than 4096 bytes (Storage device takes care of it)
  16. RefPtr<Memory::PhysicalPage> rw_dma_page;
  17. auto rw_dma_region = TRY(MM.allocate_dma_buffer_page("NVMe Queue Read/Write DMA"sv, Memory::Region::Access::ReadWrite, rw_dma_page));
  18. if (rw_dma_page.is_null())
  19. return ENOMEM;
  20. if (queue_type == QueueType::Polled) {
  21. auto queue = NVMePollQueue::try_create(move(rw_dma_region), rw_dma_page.release_nonnull(), qid, q_depth, move(cq_dma_region), move(sq_dma_region), move(db_regs));
  22. return queue;
  23. }
  24. auto queue = NVMeInterruptQueue::try_create(device, move(rw_dma_region), rw_dma_page.release_nonnull(), qid, irq.release_value(), q_depth, move(cq_dma_region), move(sq_dma_region), move(db_regs));
  25. return queue;
  26. }
  27. UNMAP_AFTER_INIT NVMeQueue::NVMeQueue(NonnullOwnPtr<Memory::Region> rw_dma_region, Memory::PhysicalPage const& rw_dma_page, u16 qid, u32 q_depth, OwnPtr<Memory::Region> cq_dma_region, OwnPtr<Memory::Region> sq_dma_region, Doorbell db_regs)
  28. : m_rw_dma_region(move(rw_dma_region))
  29. , m_qid(qid)
  30. , m_admin_queue(qid == 0)
  31. , m_qdepth(q_depth)
  32. , m_cq_dma_region(move(cq_dma_region))
  33. , m_sq_dma_region(move(sq_dma_region))
  34. , m_db_regs(move(db_regs))
  35. , m_rw_dma_page(rw_dma_page)
  36. {
  37. m_requests.with([q_depth](auto& requests) {
  38. requests.try_ensure_capacity(q_depth).release_value_but_fixme_should_propagate_errors();
  39. });
  40. m_sqe_array = { reinterpret_cast<NVMeSubmission*>(m_sq_dma_region->vaddr().as_ptr()), m_qdepth };
  41. m_cqe_array = { reinterpret_cast<NVMeCompletion*>(m_cq_dma_region->vaddr().as_ptr()), m_qdepth };
  42. }
  43. bool NVMeQueue::cqe_available()
  44. {
  45. return PHASE_TAG(m_cqe_array[m_cq_head].status) == m_cq_valid_phase;
  46. }
  47. void NVMeQueue::update_cqe_head()
  48. {
  49. // To prevent overflow, use a temp variable
  50. u32 temp_cq_head = m_cq_head + 1;
  51. if (temp_cq_head == m_qdepth) {
  52. m_cq_head = 0;
  53. m_cq_valid_phase ^= 1;
  54. } else {
  55. m_cq_head = temp_cq_head;
  56. }
  57. }
  58. u32 NVMeQueue::process_cq()
  59. {
  60. u32 nr_of_processed_cqes = 0;
  61. m_requests.with([this, &nr_of_processed_cqes](auto& requests) {
  62. while (cqe_available()) {
  63. u16 status;
  64. u16 cmdid;
  65. ++nr_of_processed_cqes;
  66. status = CQ_STATUS_FIELD(m_cqe_array[m_cq_head].status);
  67. cmdid = m_cqe_array[m_cq_head].command_id;
  68. dbgln_if(NVME_DEBUG, "NVMe: Completion with status {:x} and command identifier {}. CQ_HEAD: {}", status, cmdid, m_cq_head);
  69. if (!requests.contains(cmdid)) {
  70. dmesgln("Bogus cmd id: {}", cmdid);
  71. VERIFY_NOT_REACHED();
  72. }
  73. complete_current_request(cmdid, status);
  74. update_cqe_head();
  75. }
  76. });
  77. if (nr_of_processed_cqes) {
  78. update_cq_doorbell();
  79. }
  80. return nr_of_processed_cqes;
  81. }
  82. void NVMeQueue::submit_sqe(NVMeSubmission& sub)
  83. {
  84. SpinlockLocker lock(m_sq_lock);
  85. memcpy(&m_sqe_array[m_sq_tail], &sub, sizeof(NVMeSubmission));
  86. {
  87. u32 temp_sq_tail = m_sq_tail + 1;
  88. if (temp_sq_tail == m_qdepth)
  89. m_sq_tail = 0;
  90. else
  91. m_sq_tail = temp_sq_tail;
  92. }
  93. dbgln_if(NVME_DEBUG, "NVMe: Submission with command identifier {}. SQ_TAIL: {}", sub.cmdid, m_sq_tail);
  94. update_sq_doorbell();
  95. }
  96. void NVMeQueue::complete_current_request(u16 cmdid, u16 status)
  97. {
  98. m_requests.with([this, cmdid, status](auto& requests) {
  99. auto& request_pdu = requests.get(cmdid).release_value();
  100. auto current_request = request_pdu.request;
  101. AsyncDeviceRequest::RequestResult req_result = AsyncDeviceRequest::Success;
  102. ScopeGuard guard = [&req_result, status, &request_pdu] {
  103. if (request_pdu.request)
  104. request_pdu.request->complete(req_result);
  105. if (request_pdu.end_io_handler)
  106. request_pdu.end_io_handler(status);
  107. request_pdu.clear();
  108. };
  109. // There can be submission without any request associated with it such as with
  110. // admin queue commands during init. If there is no request, we are done
  111. if (!current_request)
  112. return;
  113. if (status) {
  114. req_result = AsyncBlockDeviceRequest::Failure;
  115. return;
  116. }
  117. if (current_request->request_type() == AsyncBlockDeviceRequest::RequestType::Read) {
  118. if (auto result = current_request->write_to_buffer(current_request->buffer(), m_rw_dma_region->vaddr().as_ptr(), current_request->buffer_size()); result.is_error()) {
  119. req_result = AsyncBlockDeviceRequest::MemoryFault;
  120. return;
  121. }
  122. }
  123. });
  124. }
  125. u16 NVMeQueue::submit_sync_sqe(NVMeSubmission& sub)
  126. {
  127. // For now let's use sq tail as a unique command id.
  128. u16 cmd_status;
  129. u16 cid = get_request_cid();
  130. sub.cmdid = cid;
  131. m_requests.with([this, &sub, &cmd_status](auto& requests) {
  132. requests.set(sub.cmdid, { nullptr, [this, &cmd_status](u16 status) mutable { cmd_status = status; m_sync_wait_queue.wake_all(); } });
  133. });
  134. submit_sqe(sub);
  135. // FIXME: Only sync submissions (usually used for admin commands) use a WaitQueue based IO. Eventually we need to
  136. // move this logic into the block layer instead of sprinkling them in the driver code.
  137. m_sync_wait_queue.wait_forever("NVMe sync submit"sv);
  138. return cmd_status;
  139. }
  140. void NVMeQueue::read(AsyncBlockDeviceRequest& request, u16 nsid, u64 index, u32 count)
  141. {
  142. NVMeSubmission sub {};
  143. sub.op = OP_NVME_READ;
  144. sub.rw.nsid = nsid;
  145. sub.rw.slba = AK::convert_between_host_and_little_endian(index);
  146. // No. of lbas is 0 based
  147. sub.rw.length = AK::convert_between_host_and_little_endian((count - 1) & 0xFFFF);
  148. sub.rw.data_ptr.prp1 = reinterpret_cast<u64>(AK::convert_between_host_and_little_endian(m_rw_dma_page->paddr().as_ptr()));
  149. sub.cmdid = get_request_cid();
  150. m_requests.with([&sub, &request](auto& requests) {
  151. requests.set(sub.cmdid, { request, nullptr });
  152. });
  153. full_memory_barrier();
  154. submit_sqe(sub);
  155. }
  156. void NVMeQueue::write(AsyncBlockDeviceRequest& request, u16 nsid, u64 index, u32 count)
  157. {
  158. NVMeSubmission sub {};
  159. sub.op = OP_NVME_WRITE;
  160. sub.rw.nsid = nsid;
  161. sub.rw.slba = AK::convert_between_host_and_little_endian(index);
  162. // No. of lbas is 0 based
  163. sub.rw.length = AK::convert_between_host_and_little_endian((count - 1) & 0xFFFF);
  164. sub.rw.data_ptr.prp1 = reinterpret_cast<u64>(AK::convert_between_host_and_little_endian(m_rw_dma_page->paddr().as_ptr()));
  165. sub.cmdid = get_request_cid();
  166. m_requests.with([&sub, &request](auto& requests) {
  167. requests.set(sub.cmdid, { request, nullptr });
  168. });
  169. if (auto result = request.read_from_buffer(request.buffer(), m_rw_dma_region->vaddr().as_ptr(), request.buffer_size()); result.is_error()) {
  170. complete_current_request(sub.cmdid, AsyncDeviceRequest::MemoryFault);
  171. return;
  172. }
  173. full_memory_barrier();
  174. submit_sqe(sub);
  175. }
  176. UNMAP_AFTER_INIT NVMeQueue::~NVMeQueue() = default;
  177. }