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