]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
4 | #ifndef CEPH_LIBRBD_IO_BLOCK_GUARD_H | |
5 | #define CEPH_LIBRBD_IO_BLOCK_GUARD_H | |
6 | ||
7 | #include "include/int_types.h" | |
8 | #include "common/dout.h" | |
9f95a23c | 9 | #include "common/ceph_mutex.h" |
7c673cae FG |
10 | #include <boost/intrusive/list.hpp> |
11 | #include <boost/intrusive/set.hpp> | |
12 | #include <deque> | |
13 | #include <list> | |
11fdf7f2 | 14 | #include "include/ceph_assert.h" |
7c673cae FG |
15 | |
16 | #define dout_subsys ceph_subsys_rbd | |
17 | #undef dout_prefix | |
18 | #define dout_prefix *_dout << "librbd::BlockGuard: " << this << " " \ | |
19 | << __func__ << ": " | |
20 | ||
21 | namespace librbd { | |
22 | ||
23 | struct BlockExtent { | |
f67539c2 | 24 | // [block_start, block_end) |
7c673cae FG |
25 | uint64_t block_start = 0; |
26 | uint64_t block_end = 0; | |
27 | ||
28 | BlockExtent() { | |
29 | } | |
30 | BlockExtent(uint64_t block_start, uint64_t block_end) | |
31 | : block_start(block_start), block_end(block_end) { | |
32 | } | |
f67539c2 TL |
33 | |
34 | friend ostream& operator<< (ostream& os, const BlockExtent& block_extent) { | |
35 | os << "[block_start = " << block_extent.block_start << ", " | |
36 | << "block_end = " << block_extent.block_end << ")"; | |
37 | return os; | |
38 | } | |
7c673cae FG |
39 | }; |
40 | ||
41 | struct BlockGuardCell { | |
42 | }; | |
43 | ||
44 | /** | |
45 | * Helper class to restrict and order concurrent IO to the same block. The | |
46 | * definition of a block is dependent upon the user of this class. It might | |
47 | * represent a backing object, 512 byte sectors, etc. | |
48 | */ | |
49 | template <typename BlockOperation> | |
50 | class BlockGuard { | |
51 | private: | |
52 | struct DetainedBlockExtent; | |
53 | ||
54 | public: | |
55 | typedef std::list<BlockOperation> BlockOperations; | |
56 | ||
57 | BlockGuard(CephContext *cct) | |
9f95a23c | 58 | : m_cct(cct) { |
7c673cae FG |
59 | } |
60 | ||
61 | BlockGuard(const BlockGuard&) = delete; | |
62 | BlockGuard &operator=(const BlockGuard&) = delete; | |
63 | ||
64 | /** | |
f67539c2 | 65 | * Detain future IO for a range of blocks. the guard will keep |
7c673cae FG |
66 | * ownership of the provided operation if the operation is blocked. |
67 | * @return 0 upon success and IO can be issued | |
68 | * >0 if the IO is blocked, | |
69 | * <0 upon error | |
70 | */ | |
71 | int detain(const BlockExtent &block_extent, BlockOperation *block_operation, | |
72 | BlockGuardCell **cell) { | |
9f95a23c | 73 | std::lock_guard locker{m_lock}; |
f67539c2 | 74 | ldout(m_cct, 20) << block_extent << ", " |
7c673cae FG |
75 | << "free_slots=" << m_free_detained_block_extents.size() |
76 | << dendl; | |
77 | ||
78 | DetainedBlockExtent *detained_block_extent; | |
79 | auto it = m_detained_block_extents.find(block_extent); | |
80 | if (it != m_detained_block_extents.end()) { | |
81 | // request against an already detained block | |
82 | detained_block_extent = &(*it); | |
83 | if (block_operation != nullptr) { | |
84 | detained_block_extent->block_operations.emplace_back( | |
85 | std::move(*block_operation)); | |
86 | } | |
87 | ||
88 | // alert the caller that the IO was detained | |
89 | *cell = nullptr; | |
90 | return detained_block_extent->block_operations.size(); | |
91 | } else { | |
92 | if (!m_free_detained_block_extents.empty()) { | |
93 | detained_block_extent = &m_free_detained_block_extents.front(); | |
94 | detained_block_extent->block_operations.clear(); | |
95 | m_free_detained_block_extents.pop_front(); | |
96 | } else { | |
97 | ldout(m_cct, 20) << "no free detained block cells" << dendl; | |
98 | m_detained_block_extent_pool.emplace_back(); | |
99 | detained_block_extent = &m_detained_block_extent_pool.back(); | |
100 | } | |
101 | ||
102 | detained_block_extent->block_extent = block_extent; | |
103 | m_detained_block_extents.insert(*detained_block_extent); | |
104 | *cell = reinterpret_cast<BlockGuardCell*>(detained_block_extent); | |
105 | return 0; | |
106 | } | |
107 | } | |
108 | ||
109 | /** | |
110 | * Release any detained IO operations from the provided cell. | |
111 | */ | |
112 | void release(BlockGuardCell *cell, BlockOperations *block_operations) { | |
9f95a23c | 113 | std::lock_guard locker{m_lock}; |
7c673cae | 114 | |
11fdf7f2 | 115 | ceph_assert(cell != nullptr); |
7c673cae FG |
116 | auto &detained_block_extent = reinterpret_cast<DetainedBlockExtent &>( |
117 | *cell); | |
f67539c2 | 118 | ldout(m_cct, 20) << detained_block_extent.block_extent << ", " |
7c673cae | 119 | << "pending_ops=" |
f67539c2 | 120 | << detained_block_extent.block_operations.size() |
7c673cae FG |
121 | << dendl; |
122 | ||
123 | *block_operations = std::move(detained_block_extent.block_operations); | |
124 | m_detained_block_extents.erase(detained_block_extent.block_extent); | |
125 | m_free_detained_block_extents.push_back(detained_block_extent); | |
126 | } | |
127 | ||
128 | private: | |
129 | struct DetainedBlockExtent : public boost::intrusive::list_base_hook<>, | |
130 | public boost::intrusive::set_base_hook<> { | |
131 | BlockExtent block_extent; | |
132 | BlockOperations block_operations; | |
133 | }; | |
134 | ||
135 | struct DetainedBlockExtentKey { | |
136 | typedef BlockExtent type; | |
137 | const BlockExtent &operator()(const DetainedBlockExtent &value) { | |
138 | return value.block_extent; | |
139 | } | |
140 | }; | |
141 | ||
142 | struct DetainedBlockExtentCompare { | |
143 | bool operator()(const BlockExtent &lhs, | |
144 | const BlockExtent &rhs) const { | |
145 | // check for range overlap (lhs < rhs) | |
146 | if (lhs.block_end <= rhs.block_start) { | |
147 | return true; | |
148 | } | |
149 | return false; | |
150 | } | |
151 | }; | |
152 | ||
153 | typedef std::deque<DetainedBlockExtent> DetainedBlockExtentsPool; | |
154 | typedef boost::intrusive::list<DetainedBlockExtent> DetainedBlockExtents; | |
155 | typedef boost::intrusive::set< | |
156 | DetainedBlockExtent, | |
157 | boost::intrusive::compare<DetainedBlockExtentCompare>, | |
158 | boost::intrusive::key_of_value<DetainedBlockExtentKey> > | |
159 | BlockExtentToDetainedBlockExtents; | |
160 | ||
161 | CephContext *m_cct; | |
162 | ||
9f95a23c | 163 | ceph::mutex m_lock = ceph::make_mutex("librbd::BlockGuard::m_lock"); |
7c673cae FG |
164 | DetainedBlockExtentsPool m_detained_block_extent_pool; |
165 | DetainedBlockExtents m_free_detained_block_extents; | |
166 | BlockExtentToDetainedBlockExtents m_detained_block_extents; | |
167 | ||
168 | }; | |
169 | ||
170 | } // namespace librbd | |
171 | ||
172 | #undef dout_subsys | |
173 | #undef dout_prefix | |
174 | #define dout_prefix *_dout | |
175 | ||
176 | #endif // CEPH_LIBRBD_IO_BLOCK_GUARD_H |