]> git.proxmox.com Git - ceph.git/blob - ceph/src/librbd/BlockGuard.h
import quincy beta 17.1.0
[ceph.git] / ceph / src / librbd / BlockGuard.h
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"
9 #include "common/ceph_mutex.h"
10 #include <boost/intrusive/list.hpp>
11 #include <boost/intrusive/set.hpp>
12 #include <deque>
13 #include <list>
14 #include "include/ceph_assert.h"
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 // [block_start, block_end)
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 }
33
34 friend std::ostream& operator<< (std::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 }
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)
58 : m_cct(cct) {
59 }
60
61 BlockGuard(const BlockGuard&) = delete;
62 BlockGuard &operator=(const BlockGuard&) = delete;
63
64 /**
65 * Detain future IO for a range of blocks. the guard will keep
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) {
73 std::lock_guard locker{m_lock};
74 ldout(m_cct, 20) << block_extent
75 << ", free_slots="
76 << m_free_detained_block_extents.size()
77 << dendl;
78
79 DetainedBlockExtent *detained_block_extent;
80 auto it = m_detained_block_extents.find(block_extent);
81 if (it != m_detained_block_extents.end()) {
82 // request against an already detained block
83 detained_block_extent = &(*it);
84 if (block_operation != nullptr) {
85 detained_block_extent->block_operations.emplace_back(
86 std::move(*block_operation));
87 }
88
89 // alert the caller that the IO was detained
90 *cell = nullptr;
91 return detained_block_extent->block_operations.size();
92 } else {
93 if (!m_free_detained_block_extents.empty()) {
94 detained_block_extent = &m_free_detained_block_extents.front();
95 detained_block_extent->block_operations.clear();
96 m_free_detained_block_extents.pop_front();
97 } else {
98 ldout(m_cct, 20) << "no free detained block cells" << dendl;
99 m_detained_block_extent_pool.emplace_back();
100 detained_block_extent = &m_detained_block_extent_pool.back();
101 }
102
103 detained_block_extent->block_extent = block_extent;
104 m_detained_block_extents.insert(*detained_block_extent);
105 *cell = reinterpret_cast<BlockGuardCell*>(detained_block_extent);
106 return 0;
107 }
108 }
109
110 /**
111 * Release any detained IO operations from the provided cell.
112 */
113 void release(BlockGuardCell *cell, BlockOperations *block_operations) {
114 std::lock_guard locker{m_lock};
115
116 ceph_assert(cell != nullptr);
117 auto &detained_block_extent = reinterpret_cast<DetainedBlockExtent &>(
118 *cell);
119 ldout(m_cct, 20) << detained_block_extent.block_extent
120 << ", pending_ops="
121 << detained_block_extent.block_operations.size()
122 << dendl;
123
124 *block_operations = std::move(detained_block_extent.block_operations);
125 m_detained_block_extents.erase(detained_block_extent.block_extent);
126 m_free_detained_block_extents.push_back(detained_block_extent);
127 }
128
129 private:
130 struct DetainedBlockExtent : public boost::intrusive::list_base_hook<>,
131 public boost::intrusive::set_base_hook<> {
132 BlockExtent block_extent;
133 BlockOperations block_operations;
134 };
135
136 struct DetainedBlockExtentKey {
137 typedef BlockExtent type;
138 const BlockExtent &operator()(const DetainedBlockExtent &value) {
139 return value.block_extent;
140 }
141 };
142
143 struct DetainedBlockExtentCompare {
144 bool operator()(const BlockExtent &lhs,
145 const BlockExtent &rhs) const {
146 // check for range overlap (lhs < rhs)
147 if (lhs.block_end <= rhs.block_start) {
148 return true;
149 }
150 return false;
151 }
152 };
153
154 typedef std::deque<DetainedBlockExtent> DetainedBlockExtentsPool;
155 typedef boost::intrusive::list<DetainedBlockExtent> DetainedBlockExtents;
156 typedef boost::intrusive::set<
157 DetainedBlockExtent,
158 boost::intrusive::compare<DetainedBlockExtentCompare>,
159 boost::intrusive::key_of_value<DetainedBlockExtentKey> >
160 BlockExtentToDetainedBlockExtents;
161
162 CephContext *m_cct;
163
164 ceph::mutex m_lock = ceph::make_mutex("librbd::BlockGuard::m_lock");
165 DetainedBlockExtentsPool m_detained_block_extent_pool;
166 DetainedBlockExtents m_free_detained_block_extents;
167 BlockExtentToDetainedBlockExtents m_detained_block_extents;
168
169 };
170
171 } // namespace librbd
172
173 #undef dout_subsys
174 #undef dout_prefix
175 #define dout_prefix *_dout
176
177 #endif // CEPH_LIBRBD_IO_BLOCK_GUARD_H