1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "test/librados_test_stub/TestMemIoCtxImpl.h"
5 #include "test/librados_test_stub/TestMemRadosClient.h"
6 #include "common/Clock.h"
7 #include "common/RWLock.h"
8 #include "include/err.h"
9 #include <boost/algorithm/string/predicate.hpp>
10 #include <boost/bind.hpp>
12 #include <include/compat.h>
14 static void to_vector(const interval_set
<uint64_t> &set
,
15 std::vector
<std::pair
<uint64_t, uint64_t> > *vec
) {
17 for (interval_set
<uint64_t>::const_iterator it
= set
.begin();
18 it
!= set
.end(); ++it
) {
25 TestMemIoCtxImpl::TestMemIoCtxImpl() {
28 TestMemIoCtxImpl::TestMemIoCtxImpl(const TestMemIoCtxImpl
& rhs
)
29 : TestIoCtxImpl(rhs
), m_client(rhs
.m_client
), m_pool(rhs
.m_pool
) {
33 TestMemIoCtxImpl::TestMemIoCtxImpl(TestMemRadosClient
*client
, int64_t pool_id
,
34 const std::string
& pool_name
,
35 TestMemCluster::Pool
*pool
)
36 : TestIoCtxImpl(client
, pool_id
, pool_name
), m_client(client
),
41 TestMemIoCtxImpl::~TestMemIoCtxImpl() {
45 TestIoCtxImpl
*TestMemIoCtxImpl::clone() {
46 return new TestMemIoCtxImpl(*this);
49 int TestMemIoCtxImpl::aio_remove(const std::string
& oid
, AioCompletionImpl
*c
) {
50 m_client
->add_aio_operation(oid
, true,
51 boost::bind(&TestMemIoCtxImpl::remove
, this, oid
,
57 int TestMemIoCtxImpl::append(const std::string
& oid
, const bufferlist
&bl
,
58 const SnapContext
&snapc
) {
59 if (get_snap_read() != CEPH_NOSNAP
) {
61 } else if (m_client
->is_blacklisted()) {
65 TestMemCluster::SharedFile file
;
67 RWLock::WLocker
l(m_pool
->file_lock
);
68 file
= get_file(oid
, true, snapc
);
71 RWLock::WLocker
l(file
->lock
);
72 file
->data
.append(bl
);
76 int TestMemIoCtxImpl::assert_exists(const std::string
&oid
) {
77 if (m_client
->is_blacklisted()) {
81 RWLock::RLocker
l(m_pool
->file_lock
);
82 TestMemCluster::SharedFile file
= get_file(oid
, false, get_snap_context());
89 int TestMemIoCtxImpl::create(const std::string
& oid
, bool exclusive
) {
90 if (get_snap_read() != CEPH_NOSNAP
) {
92 } else if (m_client
->is_blacklisted()) {
96 RWLock::WLocker
l(m_pool
->file_lock
);
97 get_file(oid
, true, get_snap_context());
101 int TestMemIoCtxImpl::list_snaps(const std::string
& oid
, snap_set_t
*out_snaps
) {
102 if (m_client
->is_blacklisted()) {
103 return -EBLACKLISTED
;
107 out_snaps
->clones
.clear();
109 RWLock::RLocker
l(m_pool
->file_lock
);
110 TestMemCluster::Files::iterator it
= m_pool
->files
.find(oid
);
111 if (it
== m_pool
->files
.end()) {
115 bool include_head
= false;
116 TestMemCluster::FileSnapshots
&file_snaps
= it
->second
;
117 for (TestMemCluster::FileSnapshots::iterator s_it
= file_snaps
.begin();
118 s_it
!= file_snaps
.end(); ++s_it
) {
119 TestMemCluster::File
&file
= *s_it
->get();
121 if (file_snaps
.size() > 1) {
122 out_snaps
->seq
= file
.snap_id
;
123 TestMemCluster::FileSnapshots::iterator
next_it(s_it
);
125 if (next_it
== file_snaps
.end()) {
135 // update the overlap with the next version's overlap metadata
136 TestMemCluster::File
&next_file
= *next_it
->get();
137 interval_set
<uint64_t> overlap
;
138 if (next_file
.exists
) {
139 overlap
= next_file
.snap_overlap
;
143 clone
.cloneid
= file
.snap_id
;
144 clone
.snaps
= file
.snaps
;
145 to_vector(overlap
, &clone
.overlap
);
146 clone
.size
= file
.data
.length();
147 out_snaps
->clones
.push_back(clone
);
151 if ((file_snaps
.size() == 1 && file_snaps
.back()->data
.length() > 0) ||
154 // Include the SNAP_HEAD
155 TestMemCluster::File
&file
= *file_snaps
.back();
157 RWLock::RLocker
l2(file
.lock
);
158 if (out_snaps
->seq
== 0 && !include_head
) {
159 out_snaps
->seq
= file
.snap_id
;
161 clone_info_t head_clone
;
162 head_clone
.cloneid
= librados::SNAP_HEAD
;
163 head_clone
.size
= file
.data
.length();
164 out_snaps
->clones
.push_back(head_clone
);
171 int TestMemIoCtxImpl::omap_get_vals2(const std::string
& oid
,
172 const std::string
& start_after
,
173 const std::string
&filter_prefix
,
175 std::map
<std::string
, bufferlist
> *out_vals
,
177 if (out_vals
== NULL
) {
179 } else if (m_client
->is_blacklisted()) {
180 return -EBLACKLISTED
;
183 TestMemCluster::SharedFile file
;
185 RWLock::RLocker
l(m_pool
->file_lock
);
186 file
= get_file(oid
, false, get_snap_context());
194 RWLock::RLocker
l(file
->lock
);
195 TestMemCluster::FileOMaps::iterator o_it
= m_pool
->file_omaps
.find(oid
);
196 if (o_it
== m_pool
->file_omaps
.end()) {
203 TestMemCluster::OMap
&omap
= o_it
->second
;
204 TestMemCluster::OMap::iterator it
= omap
.begin();
205 if (!start_after
.empty()) {
206 it
= omap
.upper_bound(start_after
);
209 while (it
!= omap
.end() && max_return
> 0) {
210 if (filter_prefix
.empty() ||
211 boost::algorithm::starts_with(it
->first
, filter_prefix
)) {
212 (*out_vals
)[it
->first
] = it
->second
;
218 *pmore
= (it
!= omap
.end());
223 int TestMemIoCtxImpl::omap_get_vals(const std::string
& oid
,
224 const std::string
& start_after
,
225 const std::string
&filter_prefix
,
227 std::map
<std::string
, bufferlist
> *out_vals
) {
228 return omap_get_vals2(oid
, start_after
, filter_prefix
, max_return
, out_vals
, nullptr);
231 int TestMemIoCtxImpl::omap_rm_keys(const std::string
& oid
,
232 const std::set
<std::string
>& keys
) {
233 if (get_snap_read() != CEPH_NOSNAP
) {
235 } else if (m_client
->is_blacklisted()) {
236 return -EBLACKLISTED
;
239 TestMemCluster::SharedFile file
;
241 RWLock::WLocker
l(m_pool
->file_lock
);
242 file
= get_file(oid
, true, get_snap_context());
248 RWLock::WLocker
l(file
->lock
);
249 for (std::set
<std::string
>::iterator it
= keys
.begin();
250 it
!= keys
.end(); ++it
) {
251 m_pool
->file_omaps
[oid
].erase(*it
);
256 int TestMemIoCtxImpl::omap_set(const std::string
& oid
,
257 const std::map
<std::string
, bufferlist
> &map
) {
258 if (get_snap_read() != CEPH_NOSNAP
) {
260 } else if (m_client
->is_blacklisted()) {
261 return -EBLACKLISTED
;
264 TestMemCluster::SharedFile file
;
266 RWLock::WLocker
l(m_pool
->file_lock
);
267 file
= get_file(oid
, true, get_snap_context());
273 RWLock::WLocker
l(file
->lock
);
274 for (std::map
<std::string
, bufferlist
>::const_iterator it
= map
.begin();
275 it
!= map
.end(); ++it
) {
277 bl
.append(it
->second
);
278 m_pool
->file_omaps
[oid
][it
->first
] = bl
;
284 int TestMemIoCtxImpl::read(const std::string
& oid
, size_t len
, uint64_t off
,
286 if (m_client
->is_blacklisted()) {
287 return -EBLACKLISTED
;
290 TestMemCluster::SharedFile file
;
292 RWLock::RLocker
l(m_pool
->file_lock
);
293 file
= get_file(oid
, false, get_snap_context());
299 RWLock::RLocker
l(file
->lock
);
301 len
= file
->data
.length();
303 len
= clip_io(off
, len
, file
->data
.length());
304 if (bl
!= NULL
&& len
> 0) {
306 bit
.substr_of(file
->data
, off
, len
);
307 append_clone(bit
, bl
);
312 int TestMemIoCtxImpl::remove(const std::string
& oid
, const SnapContext
&snapc
) {
313 if (get_snap_read() != CEPH_NOSNAP
) {
315 } else if (m_client
->is_blacklisted()) {
316 return -EBLACKLISTED
;
319 RWLock::WLocker
l(m_pool
->file_lock
);
320 TestMemCluster::SharedFile file
= get_file(oid
, false, snapc
);
324 file
= get_file(oid
, true, snapc
);
326 RWLock::WLocker
l2(file
->lock
);
327 file
->exists
= false;
329 TestMemCluster::Files::iterator it
= m_pool
->files
.find(oid
);
330 assert(it
!= m_pool
->files
.end());
331 if (it
->second
.size() == 1) {
332 m_pool
->files
.erase(it
);
333 m_pool
->file_omaps
.erase(oid
);
338 int TestMemIoCtxImpl::selfmanaged_snap_create(uint64_t *snapid
) {
339 if (m_client
->is_blacklisted()) {
340 return -EBLACKLISTED
;
343 RWLock::WLocker
l(m_pool
->file_lock
);
344 *snapid
= ++m_pool
->snap_id
;
345 m_pool
->snap_seqs
.insert(*snapid
);
349 int TestMemIoCtxImpl::selfmanaged_snap_remove(uint64_t snapid
) {
350 if (m_client
->is_blacklisted()) {
351 return -EBLACKLISTED
;
354 RWLock::WLocker
l(m_pool
->file_lock
);
355 TestMemCluster::SnapSeqs::iterator it
=
356 m_pool
->snap_seqs
.find(snapid
);
357 if (it
== m_pool
->snap_seqs
.end()) {
361 // TODO clean up all file snapshots
362 m_pool
->snap_seqs
.erase(it
);
366 int TestMemIoCtxImpl::selfmanaged_snap_rollback(const std::string
& oid
,
368 if (m_client
->is_blacklisted()) {
369 return -EBLACKLISTED
;
372 RWLock::WLocker
l(m_pool
->file_lock
);
374 TestMemCluster::SharedFile file
;
375 TestMemCluster::Files::iterator f_it
= m_pool
->files
.find(oid
);
376 if (f_it
== m_pool
->files
.end()) {
380 TestMemCluster::FileSnapshots
&snaps
= f_it
->second
;
384 for (TestMemCluster::FileSnapshots::reverse_iterator it
= snaps
.rbegin();
385 it
!= snaps
.rend(); ++it
) {
386 TestMemCluster::SharedFile file
= *it
;
387 if (file
->snap_id
< get_snap_read()) {
389 // already at the snapshot version
391 } else if (file
->snap_id
== CEPH_NOSNAP
) {
393 // delete it current HEAD, next one is correct version
394 snaps
.erase(it
.base());
396 // overwrite contents of current HEAD
397 file
= TestMemCluster::SharedFile (new TestMemCluster::File(**it
));
398 file
->snap_id
= CEPH_NOSNAP
;
402 // create new head version
403 file
= TestMemCluster::SharedFile (new TestMemCluster::File(**it
));
404 file
->snap_id
= m_pool
->snap_id
;
405 snaps
.push_back(file
);
414 int TestMemIoCtxImpl::set_alloc_hint(const std::string
& oid
,
415 uint64_t expected_object_size
,
416 uint64_t expected_write_size
,
417 const SnapContext
&snapc
) {
418 if (get_snap_read() != CEPH_NOSNAP
) {
420 } else if (m_client
->is_blacklisted()) {
421 return -EBLACKLISTED
;
425 RWLock::WLocker
l(m_pool
->file_lock
);
426 get_file(oid
, true, snapc
);
432 int TestMemIoCtxImpl::sparse_read(const std::string
& oid
, uint64_t off
,
434 std::map
<uint64_t,uint64_t> *m
,
435 bufferlist
*data_bl
) {
436 if (m_client
->is_blacklisted()) {
437 return -EBLACKLISTED
;
440 // TODO verify correctness
441 TestMemCluster::SharedFile file
;
443 RWLock::RLocker
l(m_pool
->file_lock
);
444 file
= get_file(oid
, false, get_snap_context());
450 RWLock::RLocker
l(file
->lock
);
451 len
= clip_io(off
, len
, file
->data
.length());
458 if (data_bl
!= NULL
&& len
> 0) {
460 bit
.substr_of(file
->data
, off
, len
);
461 append_clone(bit
, data_bl
);
466 int TestMemIoCtxImpl::stat(const std::string
& oid
, uint64_t *psize
,
468 if (m_client
->is_blacklisted()) {
469 return -EBLACKLISTED
;
472 TestMemCluster::SharedFile file
;
474 RWLock::RLocker
l(m_pool
->file_lock
);
475 file
= get_file(oid
, false, get_snap_context());
481 RWLock::RLocker
l(file
->lock
);
483 *psize
= file
->data
.length();
485 if (pmtime
!= NULL
) {
486 *pmtime
= file
->mtime
;
491 int TestMemIoCtxImpl::truncate(const std::string
& oid
, uint64_t size
,
492 const SnapContext
&snapc
) {
493 if (get_snap_read() != CEPH_NOSNAP
) {
495 } else if (m_client
->is_blacklisted()) {
496 return -EBLACKLISTED
;
499 TestMemCluster::SharedFile file
;
501 RWLock::WLocker
l(m_pool
->file_lock
);
502 file
= get_file(oid
, true, snapc
);
505 RWLock::WLocker
l(file
->lock
);
508 interval_set
<uint64_t> is
;
509 if (file
->data
.length() > size
) {
510 is
.insert(size
, file
->data
.length() - size
);
512 bl
.substr_of(file
->data
, 0, size
);
514 } else if (file
->data
.length() != size
) {
520 bl
.append_zero(size
- file
->data
.length());
521 file
->data
.append(bl
);
524 is
.intersection_of(file
->snap_overlap
);
525 file
->snap_overlap
.subtract(is
);
529 int TestMemIoCtxImpl::write(const std::string
& oid
, bufferlist
& bl
, size_t len
,
530 uint64_t off
, const SnapContext
&snapc
) {
531 if (get_snap_read() != CEPH_NOSNAP
) {
533 } else if (m_client
->is_blacklisted()) {
534 return -EBLACKLISTED
;
537 TestMemCluster::SharedFile file
;
539 RWLock::WLocker
l(m_pool
->file_lock
);
540 file
= get_file(oid
, true, snapc
);
543 RWLock::WLocker
l(file
->lock
);
545 interval_set
<uint64_t> is
;
547 is
.intersection_of(file
->snap_overlap
);
548 file
->snap_overlap
.subtract(is
);
551 ensure_minimum_length(off
+ len
, &file
->data
);
552 file
->data
.copy_in(off
, len
, bl
);
556 int TestMemIoCtxImpl::write_full(const std::string
& oid
, bufferlist
& bl
,
557 const SnapContext
&snapc
) {
558 if (get_snap_read() != CEPH_NOSNAP
) {
560 } else if (m_client
->is_blacklisted()) {
561 return -EBLACKLISTED
;
564 TestMemCluster::SharedFile file
;
566 RWLock::WLocker
l(m_pool
->file_lock
);
567 file
= get_file(oid
, true, snapc
);
573 RWLock::WLocker
l(file
->lock
);
574 if (bl
.length() > 0) {
575 interval_set
<uint64_t> is
;
576 is
.insert(0, bl
.length());
577 is
.intersection_of(file
->snap_overlap
);
578 file
->snap_overlap
.subtract(is
);
582 file
->data
.append(bl
);
586 int TestMemIoCtxImpl::writesame(const std::string
& oid
, bufferlist
& bl
, size_t len
,
587 uint64_t off
, const SnapContext
&snapc
) {
588 if (get_snap_read() != CEPH_NOSNAP
) {
590 } else if (m_client
->is_blacklisted()) {
591 return -EBLACKLISTED
;
594 if (len
== 0 || (len
% bl
.length())) {
598 TestMemCluster::SharedFile file
;
600 RWLock::WLocker
l(m_pool
->file_lock
);
601 file
= get_file(oid
, true, snapc
);
604 RWLock::WLocker
l(file
->lock
);
606 interval_set
<uint64_t> is
;
608 is
.intersection_of(file
->snap_overlap
);
609 file
->snap_overlap
.subtract(is
);
612 ensure_minimum_length(off
+ len
, &file
->data
);
614 file
->data
.copy_in(off
, bl
.length(), bl
);
621 int TestMemIoCtxImpl::cmpext(const std::string
& oid
, uint64_t off
,
622 bufferlist
& cmp_bl
) {
623 if (get_snap_read() != CEPH_NOSNAP
) {
625 } else if (m_client
->is_blacklisted()) {
626 return -EBLACKLISTED
;
629 if (cmp_bl
.length() == 0) {
633 TestMemCluster::SharedFile file
;
635 RWLock::WLocker
l(m_pool
->file_lock
);
636 file
= get_file(oid
, true, get_snap_context());
639 RWLock::RLocker
l(file
->lock
);
640 size_t len
= cmp_bl
.length();
641 ensure_minimum_length(off
+ len
, &file
->data
);
642 if (len
> 0 && off
<= len
) {
643 for (uint64_t p
= off
; p
< len
; p
++) {
644 if (file
->data
[p
] != cmp_bl
[p
])
645 return -MAX_ERRNO
- p
;
651 int TestMemIoCtxImpl::xattr_get(const std::string
& oid
,
652 std::map
<std::string
, bufferlist
>* attrset
) {
653 if (m_client
->is_blacklisted()) {
654 return -EBLACKLISTED
;
657 TestMemCluster::SharedFile file
;
658 RWLock::RLocker
l(m_pool
->file_lock
);
659 TestMemCluster::FileXAttrs::iterator it
= m_pool
->file_xattrs
.find(oid
);
660 if (it
== m_pool
->file_xattrs
.end()) {
663 *attrset
= it
->second
;
667 int TestMemIoCtxImpl::xattr_set(const std::string
& oid
, const std::string
&name
,
669 if (m_client
->is_blacklisted()) {
670 return -EBLACKLISTED
;
673 RWLock::WLocker
l(m_pool
->file_lock
);
674 m_pool
->file_xattrs
[oid
][name
] = bl
;
678 int TestMemIoCtxImpl::zero(const std::string
& oid
, uint64_t off
, uint64_t len
,
679 const SnapContext
&snapc
) {
680 if (m_client
->is_blacklisted()) {
681 return -EBLACKLISTED
;
684 bool truncate_redirect
= false;
685 TestMemCluster::SharedFile file
;
687 RWLock::WLocker
l(m_pool
->file_lock
);
688 file
= get_file(oid
, false, snapc
);
692 file
= get_file(oid
, true, snapc
);
694 RWLock::RLocker
l2(file
->lock
);
695 if (len
> 0 && off
+ len
>= file
->data
.length()) {
696 // Zero -> Truncate logic embedded in OSD
697 truncate_redirect
= true;
700 if (truncate_redirect
) {
701 return truncate(oid
, off
, snapc
);
706 return write(oid
, bl
, len
, off
, snapc
);
709 void TestMemIoCtxImpl::append_clone(bufferlist
& src
, bufferlist
* dest
) {
710 // deep-copy the src to ensure our memory-based mock RADOS data cannot
711 // be modified by callers
712 if (src
.length() > 0) {
713 bufferlist::iterator iter
= src
.begin();
715 iter
.copy_deep(src
.length(), ptr
);
720 size_t TestMemIoCtxImpl::clip_io(size_t off
, size_t len
, size_t bl_len
) {
723 } else if (off
+ len
> bl_len
) {
729 void TestMemIoCtxImpl::ensure_minimum_length(size_t len
, bufferlist
*bl
) {
730 if (len
> bl
->length()) {
731 bufferptr
ptr(buffer::create(len
- bl
->length()));
737 TestMemCluster::SharedFile
TestMemIoCtxImpl::get_file(
738 const std::string
&oid
, bool write
, const SnapContext
&snapc
) {
739 assert(m_pool
->file_lock
.is_locked() || m_pool
->file_lock
.is_wlocked());
740 assert(!write
|| m_pool
->file_lock
.is_wlocked());
742 TestMemCluster::SharedFile file
;
743 TestMemCluster::Files::iterator it
= m_pool
->files
.find(oid
);
744 if (it
!= m_pool
->files
.end()) {
745 file
= it
->second
.back();
747 return TestMemCluster::SharedFile();
751 bool new_version
= false;
752 if (!file
|| !file
->exists
) {
753 file
= TestMemCluster::SharedFile(new TestMemCluster::File());
756 if (!snapc
.snaps
.empty() && file
->snap_id
< snapc
.seq
) {
757 for (std::vector
<snapid_t
>::const_reverse_iterator seq_it
=
758 snapc
.snaps
.rbegin();
759 seq_it
!= snapc
.snaps
.rend(); ++seq_it
) {
760 if (*seq_it
> file
->snap_id
&& *seq_it
<= snapc
.seq
) {
761 file
->snaps
.push_back(*seq_it
);
765 bufferlist prev_data
= file
->data
;
766 file
= TestMemCluster::SharedFile(
767 new TestMemCluster::File(*file
));
769 append_clone(prev_data
, &file
->data
);
770 if (prev_data
.length() > 0) {
771 file
->snap_overlap
.insert(0, prev_data
.length());
778 file
->snap_id
= snapc
.seq
;
779 file
->mtime
= ceph_clock_now().sec();
780 m_pool
->files
[oid
].push_back(file
);
785 if (get_snap_read() == CEPH_NOSNAP
) {
787 assert(it
->second
.size() > 1);
788 return TestMemCluster::SharedFile();
793 TestMemCluster::FileSnapshots
&snaps
= it
->second
;
794 for (TestMemCluster::FileSnapshots::reverse_iterator it
= snaps
.rbegin();
795 it
!= snaps
.rend(); ++it
) {
796 TestMemCluster::SharedFile file
= *it
;
797 if (file
->snap_id
< get_snap_read()) {
799 return TestMemCluster::SharedFile();
804 return TestMemCluster::SharedFile();
807 } // namespace librados