// -*- 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) 2016 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 "ExtentCache.h" void ExtentCache::extent::_link_pin_state(pin_state &pin_state) { assert(parent_extent_set); assert(!parent_pin_state); parent_pin_state = &pin_state; pin_state.pin_list.push_back(*this); } void ExtentCache::extent::_unlink_pin_state() { assert(parent_extent_set); assert(parent_pin_state); auto liter = pin_state::list::s_iterator_to(*this); parent_pin_state->pin_list.erase(liter); parent_pin_state = nullptr; } void ExtentCache::extent::unlink() { assert(parent_extent_set); assert(parent_pin_state); _unlink_pin_state(); // remove from extent set { auto siter = object_extent_set::set::s_iterator_to(*this); auto &set = object_extent_set::set::container_from_iterator(siter); assert(&set == &(parent_extent_set->extent_set)); set.erase(siter); } parent_extent_set = nullptr; assert(!parent_pin_state); } void ExtentCache::extent::link( object_extent_set &extent_set, pin_state &pin_state) { assert(!parent_extent_set); parent_extent_set = &extent_set; extent_set.extent_set.insert(*this); _link_pin_state(pin_state); } void ExtentCache::extent::move( pin_state &to) { _unlink_pin_state(); _link_pin_state(to); } void ExtentCache::remove_and_destroy_if_empty(object_extent_set &eset) { if (eset.extent_set.empty()) { auto siter = cache_set::s_iterator_to(eset); auto &set = cache_set::container_from_iterator(siter); assert(&set == &per_object_caches); // per_object_caches owns eset per_object_caches.erase(eset); delete &eset; } } ExtentCache::object_extent_set &ExtentCache::get_or_create( const hobject_t &oid) { cache_set::insert_commit_data data; auto p = per_object_caches.insert_check(oid, Cmp(), data); if (p.second) { auto *eset = new object_extent_set(oid); per_object_caches.insert_commit(*eset, data); return *eset; } else { return *(p.first); } } ExtentCache::object_extent_set *ExtentCache::get_if_exists( const hobject_t &oid) { cache_set::insert_commit_data data; auto p = per_object_caches.insert_check(oid, Cmp(), data); if (p.second) { return nullptr; } else { return &*(p.first); } } std::pair< ExtentCache::object_extent_set::set::iterator, ExtentCache::object_extent_set::set::iterator > ExtentCache::object_extent_set::get_containing_range( uint64_t off, uint64_t len) { // fst is first iterator with end after off (may be end) auto fst = extent_set.upper_bound(off, uint_cmp()); if (fst != extent_set.begin()) --fst; if (fst != extent_set.end() && off >= (fst->offset + fst->get_length())) ++fst; // lst is first iterator with start >= off + len (may be end) auto lst = extent_set.lower_bound(off + len, uint_cmp()); return std::make_pair(fst, lst); } extent_set ExtentCache::reserve_extents_for_rmw( const hobject_t &oid, write_pin &pin, const extent_set &to_write, const extent_set &to_read) { if (to_write.empty() && to_read.empty()) { return extent_set(); } extent_set must_read; auto &eset = get_or_create(oid); extent_set missing; for (auto &&res: to_write) { eset.traverse_update( pin, res.first, res.second, [&](uint64_t off, uint64_t len, extent *ext, object_extent_set::update_action *action) { action->action = object_extent_set::update_action::UPDATE_PIN; if (!ext) { missing.insert(off, len); } }); } must_read.intersection_of( to_read, missing); return must_read; } extent_map ExtentCache::get_remaining_extents_for_rmw( const hobject_t &oid, write_pin &pin, const extent_set &to_get) { if (to_get.empty()) { return extent_map(); } extent_map ret; auto &eset = get_or_create(oid); for (auto &&res: to_get) { bufferlist bl; uint64_t cur = res.first; eset.traverse_update( pin, res.first, res.second, [&](uint64_t off, uint64_t len, extent *ext, object_extent_set::update_action *action) { assert(off == cur); cur = off + len; action->action = object_extent_set::update_action::NONE; assert(ext && ext->bl && ext->pinned_by_write()); bl.substr_of( *(ext->bl), off - ext->offset, len); ret.insert(off, len, bl); }); } return ret; } void ExtentCache::present_rmw_update( const hobject_t &oid, write_pin &pin, const extent_map &extents) { if (extents.empty()) { return; } auto &eset = get_or_create(oid); for (auto &&res: extents) { eset.traverse_update( pin, res.get_off(), res.get_len(), [&](uint64_t off, uint64_t len, extent *ext, object_extent_set::update_action *action) { action->action = object_extent_set::update_action::NONE; assert(ext && ext->pinned_by_write()); action->bl = bufferlist(); action->bl->substr_of( res.get_val(), off - res.get_off(), len); }); } } ostream &ExtentCache::print(ostream &out) const { out << "ExtentCache(" << std::endl; for (auto esiter = per_object_caches.begin(); esiter != per_object_caches.end(); ++esiter) { out << " Extents(" << esiter->oid << ")[" << std::endl; for (auto exiter = esiter->extent_set.begin(); exiter != esiter->extent_set.end(); ++exiter) { out << " Extent(" << exiter->offset << "~" << exiter->get_length() << ":" << exiter->pin_tid() << ")" << std::endl; } } return out << ")" << std::endl; } ostream &operator<<(ostream &lhs, const ExtentCache &cache) { return cache.print(lhs); }