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 // [block_start, block_end)
25 uint64_t block_start
= 0;
26 uint64_t block_end
= 0;
30 BlockExtent(uint64_t block_start
, uint64_t block_end
)
31 : block_start(block_start
), block_end(block_end
) {
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
<< "]";
41 struct BlockGuardCell
{
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.
49 template <typename BlockOperation
>
52 struct DetainedBlockExtent
;
55 typedef std::list
<BlockOperation
> BlockOperations
;
57 BlockGuard(CephContext
*cct
)
61 BlockGuard(const BlockGuard
&) = delete;
62 BlockGuard
&operator=(const BlockGuard
&) = delete;
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,
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
76 << m_free_detained_block_extents
.size()
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
));
89 // alert the caller that the IO was detained
91 return detained_block_extent
->block_operations
.size();
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();
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();
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
);
111 * Release any detained IO operations from the provided cell.
113 void release(BlockGuardCell
*cell
, BlockOperations
*block_operations
) {
114 std::lock_guard locker
{m_lock
};
116 ceph_assert(cell
!= nullptr);
117 auto &detained_block_extent
= reinterpret_cast<DetainedBlockExtent
&>(
119 ldout(m_cct
, 20) << detained_block_extent
.block_extent
121 << detained_block_extent
.block_operations
.size()
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
);
130 struct DetainedBlockExtent
: public boost::intrusive::list_base_hook
<>,
131 public boost::intrusive::set_base_hook
<> {
132 BlockExtent block_extent
;
133 BlockOperations block_operations
;
136 struct DetainedBlockExtentKey
{
137 typedef BlockExtent type
;
138 const BlockExtent
&operator()(const DetainedBlockExtent
&value
) {
139 return value
.block_extent
;
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
) {
154 typedef std::deque
<DetainedBlockExtent
> DetainedBlockExtentsPool
;
155 typedef boost::intrusive::list
<DetainedBlockExtent
> DetainedBlockExtents
;
156 typedef boost::intrusive::set
<
158 boost::intrusive::compare
<DetainedBlockExtentCompare
>,
159 boost::intrusive::key_of_value
<DetainedBlockExtentKey
> >
160 BlockExtentToDetainedBlockExtents
;
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
;
171 } // namespace librbd
175 #define dout_prefix *_dout
177 #endif // CEPH_LIBRBD_IO_BLOCK_GUARD_H