1 // Copyright 2016 The Fuchsia Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 #ifndef ZIRCON_SYSTEM_DEV_BUS_VIRTIO_BLOCK_H_ 5 #define ZIRCON_SYSTEM_DEV_BUS_VIRTIO_BLOCK_H_ 6 7 #include "device.h" 8 #include "ring.h" 9 10 #include <atomic> 11 #include <stdlib.h> 12 #include <zircon/compiler.h> 13 14 #include "backends/backend.h" 15 #include <ddk/protocol/block.h> 16 #include <virtio/block.h> 17 #include <zircon/device/block.h> 18 19 #include <lib/sync/completion.h> 20 21 namespace virtio { 22 23 struct block_txn_t { 24 block_op_t op; 25 block_impl_queue_callback completion_cb; 26 void* cookie; 27 struct vring_desc* desc; 28 size_t index; 29 list_node_t node; 30 zx_handle_t pmt; 31 }; 32 33 class Ring; 34 35 class BlockDevice : public Device { 36 public: 37 BlockDevice(zx_device_t* device, zx::bti bti, fbl::unique_ptr<Backend> backend); 38 39 virtual zx_status_t Init() override; 40 virtual void Release() override; 41 virtual void Unbind() override; 42 43 virtual void IrqRingUpdate() override; 44 virtual void IrqConfigChange() override; 45 GetSize()46 uint64_t GetSize() const { return config_.capacity * config_.blk_size; } GetBlockSize()47 uint32_t GetBlockSize() const { return config_.blk_size; } GetBlockCount()48 uint64_t GetBlockCount() const { return config_.capacity; } tag()49 const char* tag() const override { return "virtio-blk"; } 50 51 private: 52 // DDK driver hooks 53 static zx_off_t virtio_block_get_size(void* ctx); 54 static zx_status_t virtio_block_ioctl(void* ctx, uint32_t op, const void* in_buf, size_t in_len, 55 void* out_buf, size_t out_len, size_t* out_actual); 56 57 static void virtio_block_query(void* ctx, block_info_t* bi, size_t* bopsz); 58 static void virtio_block_queue(void* ctx, block_op_t* bop, 59 block_impl_queue_callback completion_cb, void* cookie); 60 61 static void virtio_block_unbind(void* ctx); 62 static void virtio_block_release(void* ctx); 63 64 void GetInfo(block_info_t* info); 65 66 void SignalWorker(block_txn_t* txn); 67 void WorkerThread(); 68 void FlushPendingTxns(); 69 void CleanupPendingTxns(); 70 71 zx_status_t QueueTxn(block_txn_t* txn, uint32_t type, size_t bytes, uint64_t* pages, 72 size_t pagecount, uint16_t* idx); 73 74 void txn_complete(block_txn_t* txn, zx_status_t status); 75 76 // The main virtio ring. 77 Ring vring_ = {this}; 78 79 // Lock to be used around Ring::AllocDescChain and FreeDesc. 80 // TODO: Move this into Ring class once it's certain that other users of the class are okay with 81 // it. 82 fbl::Mutex ring_lock_; 83 84 static const uint16_t ring_size = 128; // 128 matches legacy pci. 85 86 // Saved block device configuration out of the pci config BAR. 87 virtio_blk_config_t config_ = {}; 88 89 // A queue of block request/responses. 90 static const size_t blk_req_count = 32; 91 92 io_buffer_t blk_req_buf_; 93 virtio_blk_req_t* blk_req_ = nullptr; 94 95 zx_paddr_t blk_res_pa_ = 0; 96 uint8_t* blk_res_ = nullptr; 97 98 uint32_t blk_req_bitmap_ = 0; 99 static_assert(blk_req_count <= sizeof(blk_req_bitmap_) * CHAR_BIT, ""); 100 alloc_blk_req()101 size_t alloc_blk_req() { 102 size_t i = 0; 103 if (blk_req_bitmap_ != 0) 104 i = sizeof(blk_req_bitmap_) * CHAR_BIT - __builtin_clz(blk_req_bitmap_); 105 blk_req_bitmap_ |= (1 << i); 106 return i; 107 } 108 free_blk_req(size_t i)109 void free_blk_req(size_t i) { blk_req_bitmap_ &= ~(1 << i); } 110 111 // Pending txns and completion signal. 112 fbl::Mutex txn_lock_; 113 list_node pending_txn_list_ = LIST_INITIAL_VALUE(pending_txn_list_); 114 sync_completion_t txn_signal_; 115 116 // Worker state. 117 thrd_t worker_thread_; 118 list_node worker_txn_list_ = LIST_INITIAL_VALUE(worker_txn_list_); 119 sync_completion_t worker_signal_; 120 std::atomic_bool worker_shutdown_ = false; 121 122 block_impl_protocol_ops_t block_ops_ = {}; 123 }; 124 125 } // namespace virtio 126 127 #endif // ZIRCON_SYSTEM_DEV_BUS_VIRTIO_BLOCK_H_ 128