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