1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #ifndef CEPH_LIBRBD_IO_BLOCK_GUARD_H
5 #define CEPH_LIBRBD_IO_BLOCK_GUARD_H
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>
14 #include "include/ceph_assert.h"
16 #define dout_subsys ceph_subsys_rbd
18 #define dout_prefix *_dout << "librbd::BlockGuard: " << this << " " \
24 uint64_t block_start
= 0;
25 uint64_t block_end
= 0;
29 BlockExtent(uint64_t block_start
, uint64_t block_end
)
30 : block_start(block_start
), block_end(block_end
) {
34 struct BlockGuardCell
{
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.
42 template <typename BlockOperation
>
45 struct DetainedBlockExtent
;
48 typedef std::list
<BlockOperation
> BlockOperations
;
50 BlockGuard(CephContext
*cct
)
54 BlockGuard(const BlockGuard
&) = delete;
55 BlockGuard
&operator=(const BlockGuard
&) = delete;
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,
64 int detain(const BlockExtent
&block_extent
, BlockOperation
*block_operation
,
65 BlockGuardCell
**cell
) {
66 std::lock_guard locker
{m_lock
};
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()
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
));
82 // alert the caller that the IO was detained
84 return detained_block_extent
->block_operations
.size();
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();
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();
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
);
104 * Release any detained IO operations from the provided cell.
106 void release(BlockGuardCell
*cell
, BlockOperations
*block_operations
) {
107 std::lock_guard locker
{m_lock
};
109 ceph_assert(cell
!= nullptr);
110 auto &detained_block_extent
= reinterpret_cast<DetainedBlockExtent
&>(
112 ldout(m_cct
, 20) << "block_start="
113 << detained_block_extent
.block_extent
.block_start
<< ", "
115 << detained_block_extent
.block_extent
.block_end
<< ", "
117 << (detained_block_extent
.block_operations
.empty() ?
118 0 : detained_block_extent
.block_operations
.size() - 1)
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
);
127 struct DetainedBlockExtent
: public boost::intrusive::list_base_hook
<>,
128 public boost::intrusive::set_base_hook
<> {
129 BlockExtent block_extent
;
130 BlockOperations block_operations
;
133 struct DetainedBlockExtentKey
{
134 typedef BlockExtent type
;
135 const BlockExtent
&operator()(const DetainedBlockExtent
&value
) {
136 return value
.block_extent
;
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
) {
151 typedef std::deque
<DetainedBlockExtent
> DetainedBlockExtentsPool
;
152 typedef boost::intrusive::list
<DetainedBlockExtent
> DetainedBlockExtents
;
153 typedef boost::intrusive::set
<
155 boost::intrusive::compare
<DetainedBlockExtentCompare
>,
156 boost::intrusive::key_of_value
<DetainedBlockExtentKey
> >
157 BlockExtentToDetainedBlockExtents
;
161 ceph::mutex m_lock
= ceph::make_mutex("librbd::BlockGuard::m_lock");
162 DetainedBlockExtentsPool m_detained_block_extent_pool
;
163 DetainedBlockExtents m_free_detained_block_extents
;
164 BlockExtentToDetainedBlockExtents m_detained_block_extents
;
168 } // namespace librbd
172 #define dout_prefix *_dout
174 #endif // CEPH_LIBRBD_IO_BLOCK_GUARD_H