From 155ee28d98b832e5414aa594094c86cb6bfee45e Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Thu, 29 Apr 2021 15:03:46 +0200 Subject: [PATCH] librbd/cache/pwl/ssd/WriteLog: don't crash on split log entries write_log_entries() will split a log entry at the end of the log, the remainder is written to the beginning at DATA_RING_BUFFER_OFFSET. On the read side aio_read_data_block() doesn't handle this case and just crashes. Unless the workload in use is <= 4K, the image is rendered unusable sooner or later. Fixes: https://tracker.ceph.com/issues/50589 Signed-off-by: Ilya Dryomov --- src/librbd/cache/pwl/ssd/WriteLog.cc | 44 ++++++++++++++++------------ src/librbd/cache/pwl/ssd/WriteLog.h | 1 - 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/librbd/cache/pwl/ssd/WriteLog.cc b/src/librbd/cache/pwl/ssd/WriteLog.cc index 14b813abedbac..864c8f6653dbc 100644 --- a/src/librbd/cache/pwl/ssd/WriteLog.cc +++ b/src/librbd/cache/pwl/ssd/WriteLog.cc @@ -954,18 +954,6 @@ int WriteLog::update_pool_root_sync( return bdev->write(0, bl, false); } -template -void WriteLog::pre_io_check(WriteLogCacheEntry *log_entry, - uint64_t &length) { - assert(log_entry->is_write() || log_entry->is_writesame()); - ceph_assert(log_entry->write_data_pos <= pool_size); - - length = log_entry->is_write() ? log_entry->write_bytes : - log_entry->ws_datalen; - length = round_up_to(length, MIN_WRITE_ALLOC_SSD_SIZE); - ceph_assert(length != 0 && log_entry->write_data_pos + length <= pool_size); -} - template void WriteLog::aio_read_data_block(WriteLogCacheEntry *log_entry, bufferlist *bl, Context *ctx) { @@ -999,12 +987,32 @@ void WriteLog::aio_read_data_block( for (unsigned int i = 0; i < log_entries.size(); i++) { auto log_entry = log_entries[i]; - uint64_t length; - pre_io_check(log_entry, length); - ldout(cct, 20) << "Read at " << log_entry->write_data_pos - << ", length " << length << dendl; - - bdev->aio_read(log_entry->write_data_pos, length, bls[i], &aio->ioc); + ceph_assert(log_entry->is_write() || log_entry->is_writesame()); + uint64_t len = log_entry->is_write() ? log_entry->write_bytes : + log_entry->ws_datalen; + uint64_t align_len = round_up_to(len, MIN_WRITE_ALLOC_SSD_SIZE); + + ldout(cct, 20) << "entry i=" << i << " " << log_entry->write_data_pos + << "~" << len << dendl; + ceph_assert(log_entry->write_data_pos <= pool_root.pool_size); + ceph_assert(align_len != 0); + if (log_entry->write_data_pos + align_len > pool_root.pool_size) { + // spans boundary, need to split + uint64_t len1 = pool_root.pool_size - log_entry->write_data_pos; + uint64_t len2 = align_len - len1; + + ldout(cct, 20) << "read " << log_entry->write_data_pos << "~" + << align_len << " spans boundary, split into " + << log_entry->write_data_pos << "~" << len1 + << " and " << DATA_RING_BUFFER_OFFSET << "~" + << len2 << dendl; + bdev->aio_read(log_entry->write_data_pos, len1, bls[i], &aio->ioc); + bdev->aio_read(DATA_RING_BUFFER_OFFSET, len2, bls[i], &aio->ioc); + } else { + ldout(cct, 20) << "read " << log_entry->write_data_pos << "~" + << align_len << dendl; + bdev->aio_read(log_entry->write_data_pos, align_len, bls[i], &aio->ioc); + } } bdev->aio_submit(&aio->ioc); } diff --git a/src/librbd/cache/pwl/ssd/WriteLog.h b/src/librbd/cache/pwl/ssd/WriteLog.h index df45164f7826c..ca3b90c0b3680 100644 --- a/src/librbd/cache/pwl/ssd/WriteLog.h +++ b/src/librbd/cache/pwl/ssd/WriteLog.h @@ -134,7 +134,6 @@ private: int update_pool_root_sync(std::shared_ptr root); void update_pool_root(std::shared_ptr root, AioTransContext *aio); - void pre_io_check(WriteLogCacheEntry *log_entry, uint64_t &length); void aio_read_data_block(WriteLogCacheEntry *log_entry, bufferlist *bl, Context *ctx); void aio_read_data_block(std::vector &log_entries, -- 2.39.5