X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fceph%2Fsrc%2Fos%2Fbluestore%2Fbluestore_types.cc;fp=src%2Fceph%2Fsrc%2Fos%2Fbluestore%2Fbluestore_types.cc;h=06f64d21d4b34b318525f464af2fa29ddec1ef48;hb=812ff6ca9fcd3e629e49d4328905f33eee8ca3f5;hp=0000000000000000000000000000000000000000;hpb=15280273faafb77777eab341909a3f495cf248d9;p=stor4nfv.git diff --git a/src/ceph/src/os/bluestore/bluestore_types.cc b/src/ceph/src/os/bluestore/bluestore_types.cc new file mode 100644 index 0000000..06f64d2 --- /dev/null +++ b/src/ceph/src/os/bluestore/bluestore_types.cc @@ -0,0 +1,1147 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2014 Red Hat + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#include "bluestore_types.h" +#include "common/Formatter.h" +#include "common/Checksummer.h" +#include "include/stringify.h" + +void ExtentList::add_extents(int64_t start, int64_t count) { + AllocExtent *last_extent = NULL; + bool can_merge = false; + + if (!m_extents->empty()) { + last_extent = &(m_extents->back()); + uint64_t last_offset = last_extent->end() / m_block_size; + uint32_t last_length = last_extent->length / m_block_size; + if ((last_offset == (uint64_t) start) && + (!m_max_blocks || (last_length + count) <= m_max_blocks)) { + can_merge = true; + } + } + + if (can_merge) { + last_extent->length += (count * m_block_size); + } else { + m_extents->emplace_back(AllocExtent(start * m_block_size, + count * m_block_size)); + } +} + +// bluestore_bdev_label_t + +void bluestore_bdev_label_t::encode(bufferlist& bl) const +{ + // be slightly friendly to someone who looks at the device + bl.append("bluestore block device\n"); + bl.append(stringify(osd_uuid)); + bl.append("\n"); + ENCODE_START(2, 1, bl); + ::encode(osd_uuid, bl); + ::encode(size, bl); + ::encode(btime, bl); + ::encode(description, bl); + ::encode(meta, bl); + ENCODE_FINISH(bl); +} + +void bluestore_bdev_label_t::decode(bufferlist::iterator& p) +{ + p.advance(60); // see above + DECODE_START(2, p); + ::decode(osd_uuid, p); + ::decode(size, p); + ::decode(btime, p); + ::decode(description, p); + if (struct_v >= 2) { + ::decode(meta, p); + } + DECODE_FINISH(p); +} + +void bluestore_bdev_label_t::dump(Formatter *f) const +{ + f->dump_stream("osd_uuid") << osd_uuid; + f->dump_unsigned("size", size); + f->dump_stream("btime") << btime; + f->dump_string("description", description); + for (auto& i : meta) { + f->dump_string(i.first.c_str(), i.second); + } +} + +void bluestore_bdev_label_t::generate_test_instances( + list& o) +{ + o.push_back(new bluestore_bdev_label_t); + o.push_back(new bluestore_bdev_label_t); + o.back()->size = 123; + o.back()->btime = utime_t(4, 5); + o.back()->description = "fakey"; + o.back()->meta["foo"] = "bar"; +} + +ostream& operator<<(ostream& out, const bluestore_bdev_label_t& l) +{ + return out << "bdev(osd_uuid " << l.osd_uuid + << ", size 0x" << std::hex << l.size << std::dec + << ", btime " << l.btime + << ", desc " << l.description + << ", " << l.meta.size() << " meta" + << ")"; +} + +// cnode_t + +void bluestore_cnode_t::dump(Formatter *f) const +{ + f->dump_unsigned("bits", bits); +} + +void bluestore_cnode_t::generate_test_instances(list& o) +{ + o.push_back(new bluestore_cnode_t()); + o.push_back(new bluestore_cnode_t(0)); + o.push_back(new bluestore_cnode_t(123)); +} + +// bluestore_extent_ref_map_t + +void bluestore_extent_ref_map_t::_check() const +{ + uint64_t pos = 0; + unsigned refs = 0; + for (const auto &p : ref_map) { + if (p.first < pos) + assert(0 == "overlap"); + if (p.first == pos && p.second.refs == refs) + assert(0 == "unmerged"); + pos = p.first + p.second.length; + refs = p.second.refs; + } +} + +void bluestore_extent_ref_map_t::_maybe_merge_left( + map::iterator& p) +{ + if (p == ref_map.begin()) + return; + auto q = p; + --q; + if (q->second.refs == p->second.refs && + q->first + q->second.length == p->first) { + q->second.length += p->second.length; + ref_map.erase(p); + p = q; + } +} + +void bluestore_extent_ref_map_t::get(uint64_t offset, uint32_t length) +{ + auto p = ref_map.lower_bound(offset); + if (p != ref_map.begin()) { + --p; + if (p->first + p->second.length <= offset) { + ++p; + } + } + while (length > 0) { + if (p == ref_map.end()) { + // nothing after offset; add the whole thing. + p = ref_map.insert( + map::value_type(offset, record_t(length, 1))).first; + break; + } + if (p->first > offset) { + // gap + uint64_t newlen = MIN(p->first - offset, length); + p = ref_map.insert( + map::value_type(offset, + record_t(newlen, 1))).first; + offset += newlen; + length -= newlen; + _maybe_merge_left(p); + ++p; + continue; + } + if (p->first < offset) { + // split off the portion before offset + assert(p->first + p->second.length > offset); + uint64_t left = p->first + p->second.length - offset; + p->second.length = offset - p->first; + p = ref_map.insert(map::value_type( + offset, record_t(left, p->second.refs))).first; + // continue below + } + assert(p->first == offset); + if (length < p->second.length) { + ref_map.insert(make_pair(offset + length, + record_t(p->second.length - length, + p->second.refs))); + p->second.length = length; + ++p->second.refs; + break; + } + ++p->second.refs; + offset += p->second.length; + length -= p->second.length; + _maybe_merge_left(p); + ++p; + } + if (p != ref_map.end()) + _maybe_merge_left(p); + //_check(); +} + +void bluestore_extent_ref_map_t::put( + uint64_t offset, uint32_t length, + PExtentVector *release, + bool *maybe_unshared) +{ + //NB: existing entries in 'release' container must be preserved! + bool unshared = true; + auto p = ref_map.lower_bound(offset); + if (p == ref_map.end() || p->first > offset) { + if (p == ref_map.begin()) { + assert(0 == "put on missing extent (nothing before)"); + } + --p; + if (p->first + p->second.length <= offset) { + assert(0 == "put on missing extent (gap)"); + } + } + if (p->first < offset) { + uint64_t left = p->first + p->second.length - offset; + p->second.length = offset - p->first; + if (p->second.refs != 1) { + unshared = false; + } + p = ref_map.insert(map::value_type( + offset, record_t(left, p->second.refs))).first; + } + while (length > 0) { + assert(p->first == offset); + if (length < p->second.length) { + if (p->second.refs != 1) { + unshared = false; + } + ref_map.insert(make_pair(offset + length, + record_t(p->second.length - length, + p->second.refs))); + if (p->second.refs > 1) { + p->second.length = length; + --p->second.refs; + if (p->second.refs != 1) { + unshared = false; + } + _maybe_merge_left(p); + } else { + if (release) + release->push_back(bluestore_pextent_t(p->first, length)); + ref_map.erase(p); + } + goto out; + } + offset += p->second.length; + length -= p->second.length; + if (p->second.refs > 1) { + --p->second.refs; + if (p->second.refs != 1) { + unshared = false; + } + _maybe_merge_left(p); + ++p; + } else { + if (release) + release->push_back(bluestore_pextent_t(p->first, p->second.length)); + ref_map.erase(p++); + } + } + if (p != ref_map.end()) + _maybe_merge_left(p); + //_check(); +out: + if (maybe_unshared) { + if (unshared) { + // we haven't seen a ref != 1 yet; check the whole map. + for (auto& p : ref_map) { + if (p.second.refs != 1) { + unshared = false; + break; + } + } + } + *maybe_unshared = unshared; + } +} + +bool bluestore_extent_ref_map_t::contains(uint64_t offset, uint32_t length) const +{ + auto p = ref_map.lower_bound(offset); + if (p == ref_map.end() || p->first > offset) { + if (p == ref_map.begin()) { + return false; // nothing before + } + --p; + if (p->first + p->second.length <= offset) { + return false; // gap + } + } + while (length > 0) { + if (p == ref_map.end()) + return false; + if (p->first > offset) + return false; + if (p->first + p->second.length >= offset + length) + return true; + uint64_t overlap = p->first + p->second.length - offset; + offset += overlap; + length -= overlap; + ++p; + } + return true; +} + +bool bluestore_extent_ref_map_t::intersects( + uint64_t offset, + uint32_t length) const +{ + auto p = ref_map.lower_bound(offset); + if (p != ref_map.begin()) { + --p; + if (p->first + p->second.length <= offset) { + ++p; + } + } + if (p == ref_map.end()) + return false; + if (p->first >= offset + length) + return false; + return true; // intersects p! +} + +void bluestore_extent_ref_map_t::dump(Formatter *f) const +{ + f->open_array_section("ref_map"); + for (auto& p : ref_map) { + f->open_object_section("ref"); + f->dump_unsigned("offset", p.first); + f->dump_unsigned("length", p.second.length); + f->dump_unsigned("refs", p.second.refs); + f->close_section(); + } + f->close_section(); +} + +void bluestore_extent_ref_map_t::generate_test_instances( + list& o) +{ + o.push_back(new bluestore_extent_ref_map_t); + o.push_back(new bluestore_extent_ref_map_t); + o.back()->get(10, 10); + o.back()->get(18, 22); + o.back()->get(20, 20); + o.back()->get(10, 25); + o.back()->get(15, 20); +} + +ostream& operator<<(ostream& out, const bluestore_extent_ref_map_t& m) +{ + out << "ref_map("; + for (auto p = m.ref_map.begin(); p != m.ref_map.end(); ++p) { + if (p != m.ref_map.begin()) + out << ","; + out << std::hex << "0x" << p->first << "~" << p->second.length << std::dec + << "=" << p->second.refs; + } + out << ")"; + return out; +} + +// bluestore_blob_use_tracker_t + +void bluestore_blob_use_tracker_t::allocate() +{ + assert(num_au != 0); + bytes_per_au = new uint32_t[num_au]; + for (uint32_t i = 0; i < num_au; ++i) { + bytes_per_au[i] = 0; + } +} + +void bluestore_blob_use_tracker_t::init( + uint32_t full_length, uint32_t _au_size) { + assert(!au_size || is_empty()); + assert(_au_size > 0); + assert(full_length > 0); + clear(); + uint32_t _num_au = ROUND_UP_TO(full_length, _au_size) / _au_size; + au_size = _au_size; + if( _num_au > 1 ) { + num_au = _num_au; + allocate(); + } +} + +void bluestore_blob_use_tracker_t::get( + uint32_t offset, uint32_t length) +{ + assert(au_size); + if (!num_au) { + total_bytes += length; + }else { + auto end = offset + length; + + while (offset < end) { + auto phase = offset % au_size; + bytes_per_au[offset / au_size] += + MIN(au_size - phase, end - offset); + offset += (phase ? au_size - phase : au_size); + } + } +} + +bool bluestore_blob_use_tracker_t::put( + uint32_t offset, uint32_t length, + PExtentVector *release_units) +{ + assert(au_size); + if (release_units) { + release_units->clear(); + } + bool maybe_empty = true; + if (!num_au) { + assert(total_bytes >= length); + total_bytes -= length; + } else { + auto end = offset + length; + uint64_t next_offs = 0; + while (offset < end) { + auto phase = offset % au_size; + size_t pos = offset / au_size; + auto diff = MIN(au_size - phase, end - offset); + assert(diff <= bytes_per_au[pos]); + bytes_per_au[pos] -= diff; + offset += (phase ? au_size - phase : au_size); + if (bytes_per_au[pos] == 0) { + if (release_units) { + if (release_units->empty() || next_offs != pos * au_size) { + release_units->emplace_back(pos * au_size, au_size); + } else { + release_units->back().length += au_size; + } + next_offs += au_size; + } + } else { + maybe_empty = false; // micro optimization detecting we aren't empty + // even in the affected extent + } + } + } + bool empty = maybe_empty ? !is_not_empty() : false; + if (empty && release_units) { + release_units->clear(); + } + return empty; +} + +bool bluestore_blob_use_tracker_t::can_split() const +{ + return num_au > 0; +} + +bool bluestore_blob_use_tracker_t::can_split_at(uint32_t blob_offset) const +{ + assert(au_size); + return (blob_offset % au_size) == 0 && + blob_offset < num_au * au_size; +} + +void bluestore_blob_use_tracker_t::split( + uint32_t blob_offset, + bluestore_blob_use_tracker_t* r) +{ + assert(au_size); + assert(can_split()); + assert(can_split_at(blob_offset)); + assert(r->is_empty()); + + uint32_t new_num_au = blob_offset / au_size; + r->init( (num_au - new_num_au) * au_size, au_size); + + for (auto i = new_num_au; i < num_au; i++) { + r->get((i - new_num_au) * au_size, bytes_per_au[i]); + bytes_per_au[i] = 0; + } + if (new_num_au == 0) { + clear(); + } else if (new_num_au == 1) { + uint32_t tmp = bytes_per_au[0]; + uint32_t _au_size = au_size; + clear(); + au_size = _au_size; + total_bytes = tmp; + } else { + num_au = new_num_au; + } +} + +bool bluestore_blob_use_tracker_t::equal( + const bluestore_blob_use_tracker_t& other) const +{ + if (!num_au && !other.num_au) { + return total_bytes == other.total_bytes && au_size == other.au_size; + } else if (num_au && other.num_au) { + if (num_au != other.num_au || au_size != other.au_size) { + return false; + } + for (size_t i = 0; i < num_au; i++) { + if (bytes_per_au[i] != other.bytes_per_au[i]) { + return false; + } + } + return true; + } + + uint32_t n = num_au ? num_au : other.num_au; + uint32_t referenced = + num_au ? other.get_referenced_bytes() : get_referenced_bytes(); + auto bytes_per_au_tmp = num_au ? bytes_per_au : other.bytes_per_au; + uint32_t my_referenced = 0; + for (size_t i = 0; i < n; i++) { + my_referenced += bytes_per_au_tmp[i]; + if (my_referenced > referenced) { + return false; + } + } + return my_referenced == referenced; +} + +void bluestore_blob_use_tracker_t::dump(Formatter *f) const +{ + f->dump_unsigned("num_au", num_au); + f->dump_unsigned("au_size", au_size); + if (!num_au) { + f->dump_unsigned("total_bytes", total_bytes); + } else { + f->open_array_section("bytes_per_au"); + for (size_t i = 0; i < num_au; ++i) { + f->dump_unsigned("", bytes_per_au[i]); + } + f->close_section(); + } +} + +void bluestore_blob_use_tracker_t::generate_test_instances( + list& o) +{ + o.push_back(new bluestore_blob_use_tracker_t()); + o.back()->init(16, 16); + o.back()->get(10, 10); + o.back()->get(10, 5); + o.push_back(new bluestore_blob_use_tracker_t()); + o.back()->init(60, 16); + o.back()->get(18, 22); + o.back()->get(20, 20); + o.back()->get(15, 20); +} + +ostream& operator<<(ostream& out, const bluestore_blob_use_tracker_t& m) +{ + out << "use_tracker(" << std::hex; + if (!m.num_au) { + out << "0x" << m.au_size + << " " + << "0x" << m.total_bytes; + } else { + out << "0x" << m.num_au + << "*0x" << m.au_size + << " 0x["; + for (size_t i = 0; i < m.num_au; ++i) { + if (i != 0) + out << ","; + out << m.bytes_per_au[i]; + } + out << "]"; + } + out << std::dec << ")"; + return out; +} + +// bluestore_pextent_t + +void bluestore_pextent_t::dump(Formatter *f) const +{ + f->dump_unsigned("offset", offset); + f->dump_unsigned("length", length); +} + +ostream& operator<<(ostream& out, const bluestore_pextent_t& o) { + if (o.is_valid()) + return out << "0x" << std::hex << o.offset << "~" << o.length << std::dec; + else + return out << "!~" << std::hex << o.length << std::dec; +} + +void bluestore_pextent_t::generate_test_instances(list& ls) +{ + ls.push_back(new bluestore_pextent_t); + ls.push_back(new bluestore_pextent_t(1, 2)); +} + +// bluestore_blob_t + +string bluestore_blob_t::get_flags_string(unsigned flags) +{ + string s; + if (flags & FLAG_COMPRESSED) { + if (s.length()) + s += '+'; + s += "compressed"; + } + if (flags & FLAG_CSUM) { + if (s.length()) + s += '+'; + s += "csum"; + } + if (flags & FLAG_HAS_UNUSED) { + if (s.length()) + s += '+'; + s += "has_unused"; + } + if (flags & FLAG_SHARED) { + if (s.length()) + s += '+'; + s += "shared"; + } + + return s; +} + +size_t bluestore_blob_t::get_csum_value_size() const +{ + return Checksummer::get_csum_value_size(csum_type); +} + +void bluestore_blob_t::dump(Formatter *f) const +{ + f->open_array_section("extents"); + for (auto& p : extents) { + f->dump_object("extent", p); + } + f->close_section(); + f->dump_unsigned("logical_length", logical_length); + f->dump_unsigned("compressed_length", compressed_length); + f->dump_unsigned("flags", flags); + f->dump_unsigned("csum_type", csum_type); + f->dump_unsigned("csum_chunk_order", csum_chunk_order); + f->open_array_section("csum_data"); + size_t n = get_csum_count(); + for (unsigned i = 0; i < n; ++i) + f->dump_unsigned("csum", get_csum_item(i)); + f->close_section(); + f->dump_unsigned("unused", unused); +} + +void bluestore_blob_t::generate_test_instances(list& ls) +{ + ls.push_back(new bluestore_blob_t); + ls.push_back(new bluestore_blob_t(0)); + ls.push_back(new bluestore_blob_t); + ls.back()->allocated_test(bluestore_pextent_t(111, 222)); + ls.push_back(new bluestore_blob_t); + ls.back()->init_csum(Checksummer::CSUM_XXHASH32, 16, 65536); + ls.back()->csum_data = buffer::claim_malloc(4, strdup("abcd")); + ls.back()->add_unused(0, 3); + ls.back()->add_unused(8, 8); + ls.back()->allocated_test(bluestore_pextent_t(0x40100000, 0x10000)); + ls.back()->allocated_test( + bluestore_pextent_t(bluestore_pextent_t::INVALID_OFFSET, 0x1000)); + ls.back()->allocated_test(bluestore_pextent_t(0x40120000, 0x10000)); +} + +ostream& operator<<(ostream& out, const bluestore_blob_t& o) +{ + out << "blob(" << o.get_extents(); + if (o.is_compressed()) { + out << " clen 0x" << std::hex + << o.get_logical_length() + << " -> 0x" + << o.get_compressed_payload_length() + << std::dec; + } + if (o.flags) { + out << " " << o.get_flags_string(); + } + if (o.has_csum()) { + out << " " << Checksummer::get_csum_type_string(o.csum_type) + << "/0x" << std::hex << (1ull << o.csum_chunk_order) << std::dec; + } + if (o.has_unused()) + out << " unused=0x" << std::hex << o.unused << std::dec; + out << ")"; + return out; +} + +void bluestore_blob_t::calc_csum(uint64_t b_off, const bufferlist& bl) +{ + switch (csum_type) { + case Checksummer::CSUM_XXHASH32: + Checksummer::calculate( + get_csum_chunk_size(), b_off, bl.length(), bl, &csum_data); + break; + case Checksummer::CSUM_XXHASH64: + Checksummer::calculate( + get_csum_chunk_size(), b_off, bl.length(), bl, &csum_data); + break;; + case Checksummer::CSUM_CRC32C: + Checksummer::calculate( + get_csum_chunk_size(), b_off, bl.length(), bl, &csum_data); + break; + case Checksummer::CSUM_CRC32C_16: + Checksummer::calculate( + get_csum_chunk_size(), b_off, bl.length(), bl, &csum_data); + break; + case Checksummer::CSUM_CRC32C_8: + Checksummer::calculate( + get_csum_chunk_size(), b_off, bl.length(), bl, &csum_data); + break; + } +} + +int bluestore_blob_t::verify_csum(uint64_t b_off, const bufferlist& bl, + int* b_bad_off, uint64_t *bad_csum) const +{ + int r = 0; + + *b_bad_off = -1; + switch (csum_type) { + case Checksummer::CSUM_NONE: + break; + case Checksummer::CSUM_XXHASH32: + *b_bad_off = Checksummer::verify( + get_csum_chunk_size(), b_off, bl.length(), bl, csum_data, bad_csum); + break; + case Checksummer::CSUM_XXHASH64: + *b_bad_off = Checksummer::verify( + get_csum_chunk_size(), b_off, bl.length(), bl, csum_data, bad_csum); + break; + case Checksummer::CSUM_CRC32C: + *b_bad_off = Checksummer::verify( + get_csum_chunk_size(), b_off, bl.length(), bl, csum_data, bad_csum); + break; + case Checksummer::CSUM_CRC32C_16: + *b_bad_off = Checksummer::verify( + get_csum_chunk_size(), b_off, bl.length(), bl, csum_data, bad_csum); + break; + case Checksummer::CSUM_CRC32C_8: + *b_bad_off = Checksummer::verify( + get_csum_chunk_size(), b_off, bl.length(), bl, csum_data, bad_csum); + break; + default: + r = -EOPNOTSUPP; + break; + } + + if (r < 0) + return r; + else if (*b_bad_off >= 0) + return -1; // bad checksum + else + return 0; +} + +void bluestore_blob_t::allocated(uint32_t b_off, uint32_t length, const AllocExtentVector& allocs) +{ + if (extents.size() == 0) { + // if blob is compressed then logical length to be already configured + // otherwise - to be unset. + assert((is_compressed() && logical_length != 0) || + (!is_compressed() && logical_length == 0)); + + extents.reserve(allocs.size() + (b_off ? 1 : 0)); + if (b_off) { + extents.emplace_back( + bluestore_pextent_t(bluestore_pextent_t::INVALID_OFFSET, b_off)); + } + uint32_t new_len = b_off; + for (auto& a : allocs) { + extents.emplace_back(a.offset, a.length); + new_len += a.length; + } + if (!is_compressed()) { + logical_length = new_len; + } + } else { + assert(!is_compressed()); // partial allocations are forbidden when + // compressed + assert(b_off < logical_length); + uint32_t cur_offs = 0; + auto start_it = extents.begin(); + size_t pos = 0; + while(true) { + assert(start_it != extents.end()); + if (cur_offs + start_it->length > b_off) { + break; + } + cur_offs += start_it->length; + ++start_it; + ++pos; + } + uint32_t head = b_off - cur_offs; + uint32_t end_off = b_off + length; + auto end_it = start_it; + + while (true) { + assert(end_it != extents.end()); + assert(!end_it->is_valid()); + if (cur_offs + end_it->length >= end_off) { + break; + } + cur_offs += end_it->length; + ++end_it; + } + assert(cur_offs + end_it->length >= end_off); + uint32_t tail = cur_offs + end_it->length - end_off; + + start_it = extents.erase(start_it, end_it + 1); + size_t count = allocs.size(); + count += head ? 1 : 0; + count += tail ? 1 : 0; + extents.insert(start_it, + count, + bluestore_pextent_t( + bluestore_pextent_t::INVALID_OFFSET, 0)); + + // Workaround to resolve lack of proper iterator return in vector::insert + // Looks like some gcc/stl implementations still lack it despite c++11 + // support claim + start_it = extents.begin() + pos; + + if (head) { + start_it->length = head; + ++start_it; + } + for(auto& e : allocs) { + *start_it = e; + ++start_it; + } + if (tail) { + start_it->length = tail; + } + } +} + +// cut it out of extents +struct vecbuilder { + PExtentVector v; + uint64_t invalid = 0; + + void add_invalid(uint64_t length) { + invalid += length; + } + void flush() { + if (invalid) { + v.emplace_back(bluestore_pextent_t(bluestore_pextent_t::INVALID_OFFSET, + invalid)); + invalid = 0; + } + } + void add(uint64_t offset, uint64_t length) { + if (offset == bluestore_pextent_t::INVALID_OFFSET) { + add_invalid(length); + } + else { + flush(); + v.emplace_back(bluestore_pextent_t(offset, length)); + } + } +}; + +void bluestore_blob_t::allocated_test(const bluestore_pextent_t& alloc) +{ + extents.emplace_back(alloc); + if (!is_compressed()) { + logical_length += alloc.length; + } +} + +bool bluestore_blob_t::release_extents(bool all, + const PExtentVector& logical, + PExtentVector* r) +{ + // common case: all of it? + if (all) { + uint64_t pos = 0; + for (auto& e : extents) { + if (e.is_valid()) { + r->push_back(e); + } + pos += e.length; + } + assert(is_compressed() || get_logical_length() == pos); + extents.resize(1); + extents[0].offset = bluestore_pextent_t::INVALID_OFFSET; + extents[0].length = pos; + return true; + } + // remove from pextents according to logical release list + vecbuilder vb; + auto loffs_it = logical.begin(); + auto lend = logical.end(); + uint32_t pext_loffs_start = 0; //starting loffset of the current pextent + uint32_t pext_loffs = 0; //current loffset + auto pext_it = extents.begin(); + auto pext_end = extents.end(); + while (pext_it != pext_end) { + if (loffs_it == lend || + pext_loffs_start + pext_it->length <= loffs_it->offset) { + int delta0 = pext_loffs - pext_loffs_start; + assert(delta0 >= 0); + if ((uint32_t)delta0 < pext_it->length) { + vb.add(pext_it->offset + delta0, pext_it->length - delta0); + } + pext_loffs_start += pext_it->length; + pext_loffs = pext_loffs_start; + ++pext_it; + } + else { + //assert(pext_loffs == pext_loffs_start); + int delta0 = pext_loffs - pext_loffs_start; + assert(delta0 >= 0); + + int delta = loffs_it->offset - pext_loffs; + assert(delta >= 0); + if (delta > 0) { + vb.add(pext_it->offset + delta0, delta); + pext_loffs += delta; + } + + PExtentVector::iterator last_r = r->end(); + if (r->begin() != last_r) { + --last_r; + } + uint32_t to_release = loffs_it->length; + do { + uint32_t to_release_part = + MIN(pext_it->length - delta0 - delta, to_release); + auto o = pext_it->offset + delta0 + delta; + if (last_r != r->end() && last_r->offset + last_r->length == o) { + last_r->length += to_release_part; + } + else { + last_r = r->emplace(r->end(), o, to_release_part); + } + to_release -= to_release_part; + pext_loffs += to_release_part; + if (pext_loffs == pext_loffs_start + pext_it->length) { + pext_loffs_start += pext_it->length; + pext_loffs = pext_loffs_start; + pext_it++; + delta0 = delta = 0; + } + } while (to_release > 0 && pext_it != pext_end); + vb.add_invalid(loffs_it->length - to_release); + ++loffs_it; + } + } + vb.flush(); + extents.swap(vb.v); + return false; +} + +void bluestore_blob_t::split(uint32_t blob_offset, bluestore_blob_t& rb) +{ + size_t left = blob_offset; + uint32_t llen_lb = 0; + uint32_t llen_rb = 0; + unsigned i = 0; + for (auto p = extents.begin(); p != extents.end(); ++p, ++i) { + if (p->length <= left) { + left -= p->length; + llen_lb += p->length; + continue; + } + if (left) { + if (p->is_valid()) { + rb.extents.emplace_back(bluestore_pextent_t(p->offset + left, + p->length - left)); + } + else { + rb.extents.emplace_back(bluestore_pextent_t( + bluestore_pextent_t::INVALID_OFFSET, + p->length - left)); + } + llen_rb += p->length - left; + llen_lb += left; + p->length = left; + ++i; + ++p; + } + while (p != extents.end()) { + llen_rb += p->length; + rb.extents.push_back(*p++); + } + extents.resize(i); + logical_length = llen_lb; + rb.logical_length = llen_rb; + break; + } + rb.flags = flags; + + if (has_csum()) { + rb.csum_type = csum_type; + rb.csum_chunk_order = csum_chunk_order; + size_t csum_order = get_csum_chunk_size(); + assert(blob_offset % csum_order == 0); + size_t pos = (blob_offset / csum_order) * get_csum_value_size(); + // deep copy csum data + bufferptr old; + old.swap(csum_data); + rb.csum_data = bufferptr(old.c_str() + pos, old.length() - pos); + csum_data = bufferptr(old.c_str(), pos); + } +} + +// bluestore_shared_blob_t + +void bluestore_shared_blob_t::dump(Formatter *f) const +{ + f->dump_int("sbid", sbid); + f->dump_object("ref_map", ref_map); +} + +void bluestore_shared_blob_t::generate_test_instances( + list& ls) +{ + ls.push_back(new bluestore_shared_blob_t(1)); +} + +ostream& operator<<(ostream& out, const bluestore_shared_blob_t& sb) +{ + out << "(sbid 0x" << std::hex << sb.sbid << std::dec; + out << " " << sb.ref_map << ")"; + return out; +} + +// bluestore_onode_t + +void bluestore_onode_t::shard_info::dump(Formatter *f) const +{ + f->dump_unsigned("offset", offset); + f->dump_unsigned("bytes", bytes); +} + +ostream& operator<<(ostream& out, const bluestore_onode_t::shard_info& si) +{ + return out << std::hex << "0x" << si.offset << "(0x" << si.bytes << " bytes" + << std::dec << ")"; +} + +void bluestore_onode_t::dump(Formatter *f) const +{ + f->dump_unsigned("nid", nid); + f->dump_unsigned("size", size); + f->open_object_section("attrs"); + for (auto p = attrs.begin(); p != attrs.end(); ++p) { + f->open_object_section("attr"); + f->dump_string("name", p->first.c_str()); // it's not quite std::string + f->dump_unsigned("len", p->second.length()); + f->close_section(); + } + f->close_section(); + f->dump_string("flags", get_flags_string()); + f->open_array_section("extent_map_shards"); + for (auto si : extent_map_shards) { + f->dump_object("shard", si); + } + f->close_section(); + f->dump_unsigned("expected_object_size", expected_object_size); + f->dump_unsigned("expected_write_size", expected_write_size); + f->dump_unsigned("alloc_hint_flags", alloc_hint_flags); +} + +void bluestore_onode_t::generate_test_instances(list& o) +{ + o.push_back(new bluestore_onode_t()); + // FIXME +} + +// bluestore_deferred_op_t + +void bluestore_deferred_op_t::dump(Formatter *f) const +{ + f->dump_unsigned("op", (int)op); + f->dump_unsigned("data_len", data.length()); + f->open_array_section("extents"); + for (auto& e : extents) { + f->dump_object("extent", e); + } + f->close_section(); +} + +void bluestore_deferred_op_t::generate_test_instances(list& o) +{ + o.push_back(new bluestore_deferred_op_t); + o.push_back(new bluestore_deferred_op_t); + o.back()->op = OP_WRITE; + o.back()->extents.push_back(bluestore_pextent_t(1, 2)); + o.back()->extents.push_back(bluestore_pextent_t(100, 5)); + o.back()->data.append("my data"); +} + +void bluestore_deferred_transaction_t::dump(Formatter *f) const +{ + f->dump_unsigned("seq", seq); + f->open_array_section("ops"); + for (list::const_iterator p = ops.begin(); p != ops.end(); ++p) { + f->dump_object("op", *p); + } + f->close_section(); + + f->open_array_section("released extents"); + for (interval_set::const_iterator p = released.begin(); p != released.end(); ++p) { + f->open_object_section("extent"); + f->dump_unsigned("offset", p.get_start()); + f->dump_unsigned("length", p.get_len()); + f->close_section(); + } + f->close_section(); +} + +void bluestore_deferred_transaction_t::generate_test_instances(list& o) +{ + o.push_back(new bluestore_deferred_transaction_t()); + o.push_back(new bluestore_deferred_transaction_t()); + o.back()->seq = 123; + o.back()->ops.push_back(bluestore_deferred_op_t()); + o.back()->ops.push_back(bluestore_deferred_op_t()); + o.back()->ops.back().op = bluestore_deferred_op_t::OP_WRITE; + o.back()->ops.back().extents.push_back(bluestore_pextent_t(1,7)); + o.back()->ops.back().data.append("foodata"); +} + +void bluestore_compression_header_t::dump(Formatter *f) const +{ + f->dump_unsigned("type", type); + f->dump_unsigned("length", length); +} + +void bluestore_compression_header_t::generate_test_instances( + list& o) +{ + o.push_back(new bluestore_compression_header_t); + o.push_back(new bluestore_compression_header_t(1)); + o.back()->length = 1234; +}