]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
os/bluestore: Fix BlueFS::truncate() 60240/head
authorAdam Kupczyk <akupczyk@ibm.com>
Fri, 10 Jan 2025 08:26:54 +0000 (08:26 +0000)
committerAdam Kupczyk <akupczyk@ibm.com>
Tue, 14 Jan 2025 13:27:56 +0000 (13:27 +0000)
In `struct bluefs_fnode_t` there is a vector `extents` and
the vector `extents_index` that is a log2 seek cache.

Until modifications to truncate() we never removed extents from files.
Modified truncate() did not update extents_index.

For example 10 extents long files when truncated to 0 will have:
0 extents, 10 extents_index.
After writing some data to file:
1 extents, 11 extents_index.

Now, `bluefs_fnode_t::seek` will binary search extents_index,
lets say it located seek at item #3.
It will then jump up from #0 extent (that exists) to #3 extent which
does not exist at.
The worst part is that code is now broken, as #3 != extent.end().

There are 3 parts of the fix:
1) assert in `bluefs_fnode_t::seek` to protect against
   jumping outside extents
2) code in BlueFS::truncate to sync up `extents_index` with `extents`
3) dampening down assert in _replay to give a way out of cases
   where incorrect "offset 12345" (12345 is file size) instead of
   "offset 20000" (allocations occupied) was written to log.

Fixes: https://tracker.ceph.com/issues/69481
Signed-off-by: Adam Kupczyk <akupczyk@ibm.com>
(cherry picked from commit 7f3601089d41bfc23f530c7bf3fb7efad2d055ec)

src/os/bluestore/BlueFS.cc
src/os/bluestore/bluefs_types.cc
src/os/bluestore/bluefs_types.h

index ff798ba7ec13fc13d7d3d982f530e38bf734aa1e..96936c0d73b8b441f858e1374b835e73088b84d8 100644 (file)
@@ -1655,7 +1655,8 @@ int BlueFS::_replay(bool noop, bool to_stdout)
                   << " fnode=" << fnode
                   << " delta=" << delta
                   << dendl;
-             ceph_assert(delta.offset == fnode.allocated);
+             // be leanient, if there is no extents just produce error message
+             ceph_assert(delta.offset == fnode.allocated || delta.extents.empty());
            }
            if (cct->_conf->bluefs_log_replay_check_allocations) {
               int r = _check_allocations(fnode,
@@ -3779,6 +3780,7 @@ int BlueFS::truncate(FileWriter *h, uint64_t offset)/*_WF_L*/
     if (changed_extents) {
       fnode.size = offset;
       fnode.reset_delta();
+      fnode.recalc_allocated();
       log.t.op_file_update(fnode);
       // sad, but is_dirty must be set to signal flushing of the log
       h->file->is_dirty = true;
index ff9c96ab660ff27541bb46b372d35a6c91ed2c10..ad53f8011161b7fc2b876b289b5ea279b857dff9 100644 (file)
@@ -141,7 +141,9 @@ mempool::bluefs::vector<bluefs_extent_t>::iterator bluefs_fnode_t::seek(
     assert(it != extents_index.begin());
     --it;
     assert(offset >= *it);
-    p += it - extents_index.begin();
+    uint32_t skip = it - extents_index.begin();
+    ceph_assert(skip <= extents.size());
+    p += skip;
     offset -= *it;
   }
 
index 6516f404e12bcd7cd011960d8c80f9d77956e47f..2323e5ed6a35867ec674cc98e3b924a6a5733e4c 100644 (file)
@@ -89,6 +89,7 @@ struct bluefs_fnode_t {
   void recalc_allocated() {
     allocated = 0;
     extents_index.reserve(extents.size());
+    extents_index.clear();
     for (auto& p : extents) {
       extents_index.emplace_back(allocated);
       allocated += p.length;