[no tcmalloc found (use --without-tcmalloc to disable)])])])
AM_CONDITIONAL(WITH_TCMALLOC, [test "$HAVE_LIBTCMALLOC" = "1"])
+#set pg ref debugging?
+AC_ARG_ENABLE([pgrefdebugging],
+ [AS_HELP_STRING([--enable-pgrefdebugging], [enable pg ref debugging])],
+ [AC_DEFINE([PG_DEBUG_REFS], [1], [Defined if you want pg ref debugging])],
+ [])
+
#
# Java is painful
# - adapted from OMPI wrappers package
cur_ratio(0),
is_stopping_lock("OSDService::is_stopping_lock"),
state(NOT_STOPPING)
+#ifdef PG_DEBUG_REFS
+ , pgid_lock("OSDService::pgid_lock")
+#endif
{}
void OSDService::_start_split(const set<pg_t> &pgs)
g_ceph_context->_conf->set_val("debug_ms", "100");
g_ceph_context->_conf->apply_changes(NULL);
- // Remove PGs
+ // Shutdown PGs
for (hash_map<pg_t, PG*>::iterator p = pg_map.begin();
p != pg_map.end();
++p) {
p->second->kick();
p->second->unlock();
p->second->osr->flush();
- p->second->put();
}
- pg_map.clear();
// finish ops
op_wq.drain(); // should already be empty except for lagard PGs
assert(pg_stat_queue.empty());
}
+ peering_wq.clear();
+ // Remove PGs
+#ifdef PG_DEBUG_REFS
+ service.dump_live_pgids();
+#endif
+ for (hash_map<pg_t, PG*>::iterator p = pg_map.begin();
+ p != pg_map.end();
+ ++p) {
+ dout(20) << " kicking pg " << p->first << dendl;
+ p->second->lock();
+ if (p->second->ref.read() != 1) {
+ derr << "pgid " << p->first << " has ref count of "
+ << p->second->ref.read() << dendl;
+ assert(0);
+ }
+ p->second->unlock();
+ p->second->put("PGMap");
+ }
+ pg_map.clear();
+#ifdef PG_DEBUG_REFS
+ service.dump_live_pgids();
+#endif
g_conf->remove_observer(this);
monc->shutdown();
cluster_messenger->shutdown();
hbclient_messenger->shutdown();
hbserver_messenger->shutdown();
+ peering_wq.clear();
return r;
}
pg->lock_with_map_lock_held(no_lockdep_check);
else
pg->lock(no_lockdep_check);
- pg->get(); // because it's in pg_map
+ pg->get("PGMap"); // because it's in pg_map
return pg;
}
void OSD::add_newly_split_pg(PG *pg, PG::RecoveryCtx *rctx)
{
epoch_t e(service.get_osdmap()->get_epoch());
- pg->get(); // For pg_map
+ pg->get("PGMap"); // For pg_map
pg_map[pg->info.pgid] = pg;
dout(10) << "Adding newly split pg " << *pg << dendl;
vector<int> up, acting;
++p;
if (!pg->is_primary()) { // we hold map_lock; role is stable.
pg->stat_queue_item.remove_myself();
- pg->put();
+ pg->put("pg_stat_queue");
continue;
}
pg->pg_stats_lock.Lock();
if (acked == pg->pg_stats_stable.reported) {
dout(25) << " ack on " << pg->info.pgid << " " << pg->pg_stats_stable.reported << dendl;
pg->stat_queue_item.remove_myself();
- pg->put();
+ pg->put("pg_stat_queue");
} else {
dout(25) << " still pending " << pg->info.pgid << " " << pg->pg_stats_stable.reported
<< " > acked " << acked << dendl;
// remove from map
pg_map.erase(pg->info.pgid);
- pg->put(); // since we've taken it out of map
+ pg->put("PGMap"); // since we've taken it out of map
}
bool prepare_to_stop();
void got_stop_ack();
+
+#ifdef PG_DEBUG_REFS
+ Mutex pgid_lock;
+ map<pg_t, int> pgid_tracker;
+ map<pg_t, PG*> live_pgs;
+ void add_pgid(pg_t pgid, PG *pg) {
+ Mutex::Locker l(pgid_lock);
+ if (!pgid_tracker.count(pgid)) {
+ pgid_tracker[pgid] = 0;
+ live_pgs[pgid] = pg;
+ }
+ pgid_tracker[pgid]++;
+ }
+ void remove_pgid(pg_t pgid, PG *pg) {
+ Mutex::Locker l(pgid_lock);
+ assert(pgid_tracker.count(pgid));
+ assert(pgid_tracker[pgid] > 0);
+ pgid_tracker[pgid]--;
+ if (pgid_tracker[pgid] == 0) {
+ pgid_tracker.erase(pgid);
+ live_pgs.erase(pgid);
+ }
+ }
+ void dump_live_pgids() {
+ Mutex::Locker l(pgid_lock);
+ derr << "live pgids:" << dendl;
+ for (map<pg_t, int>::iterator i = pgid_tracker.begin();
+ i != pgid_tracker.end();
+ ++i) {
+ derr << "\t" << *i << dendl;
+ live_pgs[i->first]->dump_live_ids();
+ }
+ }
+#endif
+
OSDService(OSD *osd);
};
class OSD : public Dispatcher,
) {
if (*i == pg) {
peering_queue.erase(i++);
- pg->put();
+ pg->put("PeeringWQ");
} else {
++i;
}
}
}
bool _enqueue(PG *pg) {
- pg->get();
+ pg->get("PeeringWQ");
peering_queue.push_back(pg);
return true;
}
for (list<PG *>::const_iterator i = pgs.begin();
i != pgs.end();
++i) {
- (*i)->put();
+ (*i)->put("PeeringWQ");
}
}
void _process_finish(const list<PG *> &pgs) {
void pg_stat_queue_enqueue(PG *pg) {
pg_stat_queue_lock.Lock();
if (pg->is_primary() && !pg->stat_queue_item.is_on_list()) {
- pg->get();
+ pg->get("pg_stat_queue");
pg_stat_queue.push_back(&pg->stat_queue_item);
}
osd_stat_updated = true;
void pg_stat_queue_dequeue(PG *pg) {
pg_stat_queue_lock.Lock();
if (pg->stat_queue_item.remove_myself())
- pg->put();
+ pg->put("pg_stat_queue");
pg_stat_queue_lock.Unlock();
}
void clear_pg_stat_queue() {
while (!pg_stat_queue.empty()) {
PG *pg = pg_stat_queue.front();
pg_stat_queue.pop_front();
- pg->put();
+ pg->put("pg_stat_queue");
}
pg_stat_queue_lock.Unlock();
}
}
bool _enqueue(PG *pg) {
if (!pg->recovery_item.is_on_list()) {
- pg->get();
+ pg->get("RecoveryWQ");
osd->recovery_queue.push_back(&pg->recovery_item);
if (g_conf->osd_recovery_delay_start > 0) {
}
void _dequeue(PG *pg) {
if (pg->recovery_item.remove_myself())
- pg->put();
+ pg->put("RecoveryWQ");
}
PG *_dequeue() {
if (osd->recovery_queue.empty())
}
void _queue_front(PG *pg) {
if (!pg->recovery_item.is_on_list()) {
- pg->get();
+ pg->get("RecoveryWQ");
osd->recovery_queue.push_front(&pg->recovery_item);
}
}
void _process(PG *pg) {
osd->do_recovery(pg);
- pg->put();
+ pg->put("RecoveryWQ");
}
void _clear() {
while (!osd->recovery_queue.empty()) {
PG *pg = osd->recovery_queue.front();
osd->recovery_queue.pop_front();
- pg->put();
+ pg->put("RecoveryWQ");
}
}
} recovery_wq;
bool _enqueue(PG *pg) {
if (pg->snap_trim_item.is_on_list())
return false;
- pg->get();
+ pg->get("SnapTrimWQ");
osd->snap_trim_queue.push_back(&pg->snap_trim_item);
return true;
}
void _dequeue(PG *pg) {
if (pg->snap_trim_item.remove_myself())
- pg->put();
+ pg->put("SnapTrimWQ");
}
PG *_dequeue() {
if (osd->snap_trim_queue.empty())
}
void _process(PG *pg) {
pg->snap_trimmer();
- pg->put();
+ pg->put("SnapTrimWQ");
}
void _clear() {
osd->snap_trim_queue.clear();
if (pg->scrub_item.is_on_list()) {
return false;
}
- pg->get();
+ pg->get("ScrubWQ");
osd->scrub_queue.push_back(&pg->scrub_item);
return true;
}
void _dequeue(PG *pg) {
if (pg->scrub_item.remove_myself()) {
- pg->put();
+ pg->put("ScrubWQ");
}
}
PG *_dequeue() {
}
void _process(PG *pg) {
pg->scrub();
- pg->put();
+ pg->put("ScrubWQ");
}
void _clear() {
while (!osd->scrub_queue.empty()) {
PG *pg = osd->scrub_queue.front();
osd->scrub_queue.pop_front();
- pg->put();
+ pg->put("ScrubWQ");
}
}
} scrub_wq;
if (pg->scrub_finalize_item.is_on_list()) {
return false;
}
- pg->get();
+ pg->get("ScrubFinalizeWQ");
scrub_finalize_queue.push_back(&pg->scrub_finalize_item);
return true;
}
void _dequeue(PG *pg) {
if (pg->scrub_finalize_item.remove_myself()) {
- pg->put();
+ pg->put("ScrubFinalizeWQ");
}
}
PG *_dequeue() {
}
void _process(PG *pg) {
pg->scrub_finalize();
- pg->put();
+ pg->put("ScrubFinalizeWQ");
}
void _clear() {
while (!scrub_finalize_queue.empty()) {
PG *pg = scrub_finalize_queue.front();
scrub_finalize_queue.pop_front();
- pg->put();
+ pg->put("ScrubFinalizeWQ");
}
}
} scrub_finalize_wq;
#include "messages/MOSDSubOp.h"
#include "messages/MOSDSubOpReply.h"
+#include "common/BackTrace.h"
#include <sstream>
return *_dout << pg->gen_prefix();
}
+void PG::get(const string &tag) {
+ ref.inc();
+#ifdef PG_DEBUG_REFS
+ Mutex::Locker l(_ref_id_lock);
+ if (!_tag_counts.count(tag)) {
+ _tag_counts[tag] = 0;
+ }
+ _tag_counts[tag]++;
+#endif
+}
+void PG::put(const string &tag) {
+#ifdef PG_DEBUG_REFS
+ {
+ Mutex::Locker l(_ref_id_lock);
+ assert(_tag_counts.count(tag));
+ _tag_counts[tag]--;
+ if (_tag_counts[tag] == 0) {
+ _tag_counts.erase(tag);
+ }
+ }
+#endif
+ if (ref.dec() == 0)
+ delete this;
+}
+
+#ifdef PG_DEBUG_REFS
+uint64_t PG::get_with_id() {
+ ref.inc();
+ Mutex::Locker l(_ref_id_lock);
+ uint64_t id = ++_ref_id;
+ BackTrace bt(0);
+ stringstream ss;
+ bt.print(ss);
+ dout(20) << __func__ << ": " << info.pgid << " got id " << id << dendl;
+ assert(!_live_ids.count(id));
+ _live_ids.insert(make_pair(id, ss.str()));
+ return id;
+}
+
+void PG::put_with_id(uint64_t id) {
+ dout(20) << __func__ << ": " << info.pgid << " put id " << id << dendl;
+ {
+ Mutex::Locker l(_ref_id_lock);
+ assert(_live_ids.count(id));
+ _live_ids.erase(id);
+ }
+ if (ref.dec() == 0)
+ delete this;
+}
+
+void PG::dump_live_ids() {
+ Mutex::Locker l(_ref_id_lock);
+ dout(0) << "\t" << __func__ << ": " << info.pgid << " live ids:" << dendl;
+ for (map<uint64_t, string>::iterator i = _live_ids.begin();
+ i != _live_ids.end();
+ ++i) {
+ dout(0) << "\t\tid: " << *i << dendl;
+ }
+ dout(0) << "\t" << __func__ << ": " << info.pgid << " live tags:" << dendl;
+ for (map<string, uint64_t>::iterator i = _tag_counts.begin();
+ i != _tag_counts.end();
+ ++i) {
+ dout(0) << "\t\tid: " << *i << dendl;
+ }
+}
+#endif
+
void PGPool::update(OSDMapRef map)
{
const pg_pool_t *pi = map->get_pg_pool(id);
_pool.id),
osdmap_ref(curmap), pool(_pool),
_lock("PG::_lock"),
- ref(0), deleting(false), dirty_info(false), dirty_big_info(false), dirty_log(false),
+ ref(0),
+ #ifdef PG_DEBUG_REFS
+ _ref_id_lock("PG::_ref_id_lock"), _ref_id(0),
+ #endif
+ deleting(false), dirty_info(false), dirty_big_info(false), dirty_log(false),
info(p),
info_struct_v(0),
coll(p), log_oid(loid), biginfo_oid(ioid),
active_pushes(0),
recovery_state(this)
{
+#ifdef PG_DEBUG_REFS
+ osd->add_pgid(p, this);
+#endif
}
PG::~PG()
{
+#ifdef PG_DEBUG_REFS
+ osd->remove_pgid(info.pgid, this);
+#endif
}
void PG::lock(bool no_lockdep)
return false;
}
-void intrusive_ptr_add_ref(PG *pg) { pg->get(); }
-void intrusive_ptr_release(PG *pg) { pg->put(); }
+void intrusive_ptr_add_ref(PG *pg) { pg->get("intptr"); }
+void intrusive_ptr_release(PG *pg) { pg->put("intptr"); }
+
+#ifdef PG_DEBUG_REFS
+ uint64_t get_with_id(PG *pg) { return pg->get_with_id(); }
+ void put_with_id(PG *pg, uint64_t id) { return pg->put_with_id(id); }
+#endif
#include "msg/Messenger.h"
#include "messages/MOSDRepScrub.h"
#include "messages/MOSDPGLog.h"
+#include "common/tracked_int_ptr.hpp"
#include <list>
#include <memory>
void intrusive_ptr_add_ref(PG *pg);
void intrusive_ptr_release(PG *pg);
+#ifdef PG_DEBUG_REFS
+ uint64_t get_with_id(PG *pg);
+ void put_with_id(PG *pg, uint64_t id);
+ typedef TrackedIntPtr<PG> PGRef;
+#else
typedef boost::intrusive_ptr<PG> PGRef;
+#endif
struct PGRecoveryStats {
struct per_state_info {
Cond _cond;
atomic_t ref;
+#ifdef PG_DEBUG_REFS
+ Mutex _ref_id_lock;
+ map<uint64_t, string> _live_ids;
+ map<string, uint64_t> _tag_counts;
+ uint64_t _ref_id;
+#endif
+
public:
bool deleting; // true while in removing or OSD is shutting down
_cond.Signal();
}
- void get() {
- //generic_dout(0) << this << " " << info.pgid << " get " << ref.test() << dendl;
- //assert(_lock.is_locked());
- ref.inc();
- }
- void put() {
- //generic_dout(0) << this << " " << info.pgid << " put " << ref.test() << dendl;
- if (ref.dec() == 0)
- delete this;
- }
-
+#ifdef PG_DEBUG_REFS
+ uint64_t get_with_id();
+ void put_with_id(uint64_t);
+ void dump_live_ids();
+#endif
+ void get(const string &tag);
+ void put(const string &tag);
bool dirty_info, dirty_big_info, dirty_log;
RepModify *rm = new RepModify;
rm->pg = this;
- get();
+ get("RepModify");
rm->op = op;
rm->ctx = 0;
rm->ackerosd = ackerosd;
if (done) {
delete rm->ctx;
delete rm;
- put();
+ put("RepModify");
}
}
if (done) {
delete rm->ctx;
delete rm;
- put();
+ put("RepModify");
}
}
return transit< NotTrimming >();
}
-void intrusive_ptr_add_ref(ReplicatedPG *pg) { pg->get(); }
-void intrusive_ptr_release(ReplicatedPG *pg) { pg->put(); }
+void intrusive_ptr_add_ref(ReplicatedPG *pg) { pg->get("intptr"); }
+void intrusive_ptr_release(ReplicatedPG *pg) { pg->put("intptr"); }
+
+#ifdef PG_DEBUG_REFS
+uint64_t get_with_id(ReplicatedPG *pg) { return pg->get_with_id(); }
+void put_with_id(ReplicatedPG *pg, uint64_t id) { return pg->put_with_id(id); }
+#endif
class ReplicatedPG;
void intrusive_ptr_add_ref(ReplicatedPG *pg);
void intrusive_ptr_release(ReplicatedPG *pg);
+uint64_t get_with_id(ReplicatedPG *pg);
+void put_with_id(ReplicatedPG *pg, uint64_t id);
+
+#ifdef PG_DEBUG_REFS
+ typedef TrackedIntPtr<ReplicatedPG> ReplicatedPGRef;
+#else
typedef boost::intrusive_ptr<ReplicatedPG> ReplicatedPGRef;
+#endif
+
class PGLSFilter {
protected:
string xattr;