NVMeQueue.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. /*
  2. * Copyright (c) 2021, Pankaj R <pankydev8@gmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "NVMeQueue.h"
  7. #include "Kernel/StdLib.h"
  8. #include <Kernel/Arch/x86/IO.h>
  9. #include <Kernel/Storage/NVMe/NVMeController.h>
  10. #include <Kernel/Storage/NVMe/NVMeQueue.h>
  11. #include <Kernel/WorkQueue.h>
  12. namespace Kernel {
  13. ErrorOr<NonnullRefPtr<NVMeQueue>> NVMeQueue::try_create(u16 qid, u8 irq, u32 q_depth, OwnPtr<Memory::Region> cq_dma_region, NonnullRefPtrVector<Memory::PhysicalPage> cq_dma_page, OwnPtr<Memory::Region> sq_dma_region, NonnullRefPtrVector<Memory::PhysicalPage> sq_dma_page, Memory::TypedMapping<volatile DoorbellRegister> db_regs)
  14. {
  15. auto queue = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) NVMeQueue(qid, irq, q_depth, move(cq_dma_region), cq_dma_page, move(sq_dma_region), sq_dma_page, move(db_regs))));
  16. TRY(queue->create());
  17. return queue;
  18. }
  19. NVMeQueue::NVMeQueue(u16 qid, u8 irq, u32 q_depth, OwnPtr<Memory::Region> cq_dma_region, NonnullRefPtrVector<Memory::PhysicalPage> cq_dma_page, OwnPtr<Memory::Region> sq_dma_region, NonnullRefPtrVector<Memory::PhysicalPage> sq_dma_page, Memory::TypedMapping<volatile DoorbellRegister> db_regs)
  20. : IRQHandler(irq)
  21. , m_qid(qid)
  22. , m_admin_queue(qid == 0)
  23. , m_irq(irq)
  24. , m_qdepth(q_depth)
  25. , m_cq_dma_region(move(cq_dma_region))
  26. , m_cq_dma_page(cq_dma_page)
  27. , m_sq_dma_region(move(sq_dma_region))
  28. , m_sq_dma_page(sq_dma_page)
  29. , m_db_regs(move(db_regs))
  30. , m_current_request(nullptr)
  31. {
  32. m_sqe_array = { reinterpret_cast<NVMeSubmission*>(m_sq_dma_region->vaddr().as_ptr()), m_qdepth };
  33. m_cqe_array = { reinterpret_cast<NVMeCompletion*>(m_cq_dma_region->vaddr().as_ptr()), m_qdepth };
  34. }
  35. ErrorOr<void> NVMeQueue::create()
  36. {
  37. // DMA region for RW operation. For now the requests don't exceed more than 4096 bytes(Storage device takes of it)
  38. auto buffer = TRY(MM.allocate_dma_buffer_page("Admin CQ queue", Memory::Region::Access::ReadWrite, m_rw_dma_page));
  39. m_rw_dma_region = move(buffer);
  40. return {};
  41. }
  42. bool NVMeQueue::cqe_available()
  43. {
  44. return PHASE_TAG(m_cqe_array[m_cq_head].status) == m_cq_valid_phase;
  45. }
  46. void NVMeQueue::update_cqe_head()
  47. {
  48. // To prevent overflow, use a temp variable
  49. u32 temp_cq_head = m_cq_head + 1;
  50. if (temp_cq_head == m_qdepth) {
  51. m_cq_head = 0;
  52. m_cq_valid_phase ^= 1;
  53. } else {
  54. m_cq_head = temp_cq_head;
  55. }
  56. }
  57. bool NVMeQueue::handle_irq(const RegisterState&)
  58. {
  59. u32 nr_of_processed_cqes = 0;
  60. while (cqe_available()) {
  61. u16 status;
  62. u16 cmdid;
  63. ++nr_of_processed_cqes;
  64. status = CQ_STATUS_FIELD(m_cqe_array[m_cq_head].status);
  65. cmdid = m_cqe_array[m_cq_head].command_id;
  66. dbgln_if(NVME_DEBUG, "NVMe: Completion with status {:x} and command identifier {}. CQ_HEAD: {}", status, cmdid, m_cq_head);
  67. // TODO: We don't use AsyncBlockDevice requests for admin queue as it is only applicable for a block device (NVMe namespace)
  68. // But admin commands precedes namespace creation. Unify requests to avoid special conditions
  69. if (m_admin_queue == false) {
  70. // As the block layer calls are now sync (as we wait on each requests),
  71. // everything is operated on a single request similar to BMIDE driver.
  72. // TODO: Remove this constraint eventually.
  73. VERIFY(cmdid == m_prev_sq_tail);
  74. SpinlockLocker lock(m_request_lock);
  75. if (m_current_request) {
  76. complete_current_request(status);
  77. }
  78. }
  79. update_cqe_head();
  80. }
  81. if (nr_of_processed_cqes) {
  82. update_cq_doorbell();
  83. }
  84. return nr_of_processed_cqes ? true : false;
  85. }
  86. void NVMeQueue::submit_sqe(struct NVMeSubmission& sub)
  87. {
  88. SpinlockLocker lock(m_sq_lock);
  89. // For now let's use sq tail as a unique command id.
  90. sub.cmdid = m_sq_tail;
  91. m_prev_sq_tail = m_sq_tail;
  92. memcpy(&m_sqe_array[m_sq_tail], &sub, sizeof(NVMeSubmission));
  93. {
  94. u32 temp_sq_tail = m_sq_tail + 1;
  95. if (temp_sq_tail == m_qdepth)
  96. m_sq_tail = 0;
  97. else
  98. m_sq_tail = temp_sq_tail;
  99. }
  100. dbgln_if(NVME_DEBUG, "NVMe: Submission with command identifier {}. SQ_TAIL: {}", sub.cmdid, m_sq_tail);
  101. full_memory_barrier();
  102. update_sq_doorbell();
  103. }
  104. u16 NVMeQueue::submit_sync_sqe(NVMeSubmission& sub)
  105. {
  106. // For now let's use sq tail as a unique command id.
  107. u16 cqe_cid;
  108. u16 cid = m_sq_tail;
  109. submit_sqe(sub);
  110. do {
  111. int index;
  112. {
  113. SpinlockLocker lock(m_cq_lock);
  114. index = m_cq_head - 1;
  115. if (index < 0)
  116. index = IO_QUEUE_SIZE - 1;
  117. }
  118. cqe_cid = m_cqe_array[index].command_id;
  119. Scheduler::yield();
  120. } while (cid != cqe_cid);
  121. auto status = CQ_STATUS_FIELD(m_cqe_array[m_cq_head].status);
  122. return status;
  123. }
  124. void NVMeQueue::read(AsyncBlockDeviceRequest& request, u16 nsid, u64 index, u32 count)
  125. {
  126. NVMeSubmission sub {};
  127. SpinlockLocker m_lock(m_request_lock);
  128. m_current_request = request;
  129. sub.op = OP_NVME_READ;
  130. sub.nsid = nsid;
  131. sub.cdw10 = AK::convert_between_host_and_little_endian(index & 0xFFFFFFFF);
  132. sub.cdw11 = AK::convert_between_host_and_little_endian(index >> 32);
  133. // No. of lbas is 0 based
  134. sub.cdw12 = AK::convert_between_host_and_little_endian((count - 1) & 0xFFFF);
  135. sub.data_ptr.prp1 = reinterpret_cast<u64>(AK::convert_between_host_and_little_endian(m_rw_dma_page->paddr().as_ptr()));
  136. full_memory_barrier();
  137. submit_sqe(sub);
  138. }
  139. void NVMeQueue::write(AsyncBlockDeviceRequest& request, u16 nsid, u64 index, u32 count)
  140. {
  141. NVMeSubmission sub {};
  142. SpinlockLocker m_lock(m_request_lock);
  143. m_current_request = request;
  144. if (auto result = m_current_request->read_from_buffer(m_current_request->buffer(), m_rw_dma_region->vaddr().as_ptr(), 512 * m_current_request->block_count()); result.is_error()) {
  145. complete_current_request(AsyncDeviceRequest::MemoryFault);
  146. return;
  147. }
  148. sub.op = OP_NVME_WRITE;
  149. sub.nsid = nsid;
  150. sub.cdw10 = AK::convert_between_host_and_little_endian(index & 0xFFFFFFFF);
  151. sub.cdw11 = AK::convert_between_host_and_little_endian(index >> 32);
  152. // No. of lbas is 0 based
  153. sub.cdw12 = AK::convert_between_host_and_little_endian((count - 1) & 0xFFFF);
  154. sub.data_ptr.prp1 = reinterpret_cast<u64>(AK::convert_between_host_and_little_endian(m_rw_dma_page->paddr().as_ptr()));
  155. full_memory_barrier();
  156. submit_sqe(sub);
  157. }
  158. void NVMeQueue::complete_current_request(u16 status)
  159. {
  160. VERIFY(m_request_lock.is_locked());
  161. g_io_work->queue([this, status]() {
  162. SpinlockLocker lock(m_request_lock);
  163. auto current_request = m_current_request;
  164. m_current_request.clear();
  165. if (status) {
  166. lock.unlock();
  167. current_request->complete(AsyncBlockDeviceRequest::Failure);
  168. return;
  169. }
  170. if (current_request->request_type() == AsyncBlockDeviceRequest::RequestType::Read) {
  171. if (auto result = current_request->write_to_buffer(current_request->buffer(), m_rw_dma_region->vaddr().as_ptr(), 512 * current_request->block_count()); result.is_error()) {
  172. lock.unlock();
  173. current_request->complete(AsyncDeviceRequest::MemoryFault);
  174. return;
  175. }
  176. }
  177. lock.unlock();
  178. current_request->complete(AsyncDeviceRequest::Success);
  179. return;
  180. });
  181. }
  182. }