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