]> git.proxmox.com Git - ceph.git/blame - ceph/src/librbd/BlockGuard.h
import 15.2.0 Octopus source
[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 {
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
34struct 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 */
42template <typename BlockOperation>
43class BlockGuard {
44private:
45 struct DetainedBlockExtent;
46
47public:
48 typedef std::list<BlockOperation> BlockOperations;
49
50 BlockGuard(CephContext *cct)
9f95a23c 51 : m_cct(cct) {
7c673cae
FG
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) {
9f95a23c 66 std::lock_guard locker{m_lock};
7c673cae
FG
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) {
9f95a23c 107 std::lock_guard locker{m_lock};
7c673cae 108
11fdf7f2 109 ceph_assert(cell != nullptr);
7c673cae
FG
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
126private:
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
9f95a23c 161 ceph::mutex m_lock = ceph::make_mutex("librbd::BlockGuard::m_lock");
7c673cae
FG
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