Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / 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/Mutex.h"
10 #include <boost/intrusive/list.hpp>
11 #include <boost/intrusive/set.hpp>
12 #include <deque>
13 #include <list>
14 #include "include/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   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)
51     : m_cct(cct), m_lock("librbd::BlockGuard::m_lock") {
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) {
66     Mutex::Locker 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()
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) {
107     Mutex::Locker locker(m_lock);
108
109     assert(cell != nullptr);
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
161   Mutex m_lock;
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