From: Yan, Zheng Date: Mon, 9 Oct 2017 10:00:38 +0000 (+0800) Subject: osdc/ObjectCacher: limit memory usage of BufferHead X-Git-Tag: v13.0.1~566^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=27db0255992354e15b4285891a33fede6849cf62;p=ceph.git osdc/ObjectCacher: limit memory usage of BufferHead when doing small size random writes, size of data structures that used for tracking dirty data can be larger than the dirty data size. Fixes: http://tracker.ceph.com/issues/21726 Signed-off-by: "Yan, Zheng" --- diff --git a/src/osdc/ObjectCacher.cc b/src/osdc/ObjectCacher.cc index a77d6b3188347..4afd1de9b6fc1 100644 --- a/src/osdc/ObjectCacher.cc +++ b/src/osdc/ObjectCacher.cc @@ -12,6 +12,7 @@ #include "include/assert.h" #define MAX_FLUSH_UNDER_LOCK 20 ///< max bh's we start writeback on +#define BUFFER_MEMORY_WEIGHT 12 // memory usage of BufferHead, count in (1< 0 && (uint64_t) get_stat_clean() > max_size) { + uint64_t max_clean_bh = max_size >> BUFFER_MEMORY_WEIGHT; + uint64_t nr_clean_bh = bh_lru_rest.lru_get_size() - bh_lru_rest.lru_get_num_pinned(); + while (get_stat_clean() > 0 && + ((uint64_t)get_stat_clean() > max_size || + nr_clean_bh > max_clean_bh)) { BufferHead *bh = static_cast(bh_lru_rest.lru_expire()); if (!bh) break; @@ -1267,6 +1273,8 @@ void ObjectCacher::trim() bh_remove(ob, bh); delete bh; + --nr_clean_bh; + if (ob->complete) { ldout(cct, 10) << "trim clearing complete on " << *ob << dendl; ob->complete = false; @@ -1782,9 +1790,14 @@ void ObjectCacher::maybe_wait_for_writeback(uint64_t len, // - do not wait for bytes other waiters are waiting on. this means that // threads do not wait for each other. this effectively allows the cache // size to balloon proportional to the data that is in flight. + + uint64_t max_dirty_bh = max_dirty >> BUFFER_MEMORY_WEIGHT; while (get_stat_dirty() + get_stat_tx() > 0 && - (uint64_t) (get_stat_dirty() + get_stat_tx()) >= - max_dirty + get_stat_dirty_waiting()) { + (((uint64_t)(get_stat_dirty() + get_stat_tx()) >= + max_dirty + get_stat_dirty_waiting()) || + (dirty_or_tx_bh.size() >= + max_dirty_bh + get_stat_nr_dirty_waiters()))) { + if (blocked == 0) { trace->event("start wait for writeback"); } @@ -1794,8 +1807,10 @@ void ObjectCacher::maybe_wait_for_writeback(uint64_t len, << get_stat_dirty_waiting() << dendl; flusher_cond.Signal(); stat_dirty_waiting += len; + ++stat_nr_dirty_waiters; stat_cond.Wait(lock); stat_dirty_waiting -= len; + --stat_nr_dirty_waiters; ++blocked; ldout(cct, 10) << __func__ << " woke up" << dendl; } diff --git a/src/osdc/ObjectCacher.h b/src/osdc/ObjectCacher.h index 31201a7235422..58b3e7aafeea3 100644 --- a/src/osdc/ObjectCacher.h +++ b/src/osdc/ObjectCacher.h @@ -461,6 +461,8 @@ class ObjectCacher { loff_t stat_error; loff_t stat_dirty_waiting; // bytes that writers are waiting on to write + size_t stat_nr_dirty_waiters; + void verify_stats() const; void bh_stat_add(BufferHead *bh); @@ -468,9 +470,10 @@ class ObjectCacher { loff_t get_stat_tx() const { return stat_tx; } loff_t get_stat_rx() const { return stat_rx; } loff_t get_stat_dirty() const { return stat_dirty; } - loff_t get_stat_dirty_waiting() const { return stat_dirty_waiting; } loff_t get_stat_clean() const { return stat_clean; } loff_t get_stat_zero() const { return stat_zero; } + loff_t get_stat_dirty_waiting() const { return stat_dirty_waiting; } + size_t get_stat_nr_dirty_waiters() const { return stat_nr_dirty_waiters; } void touch_bh(BufferHead *bh) { if (bh->is_dirty())