1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
15 #ifndef CEPH_OSD_SESSION_H
16 #define CEPH_OSD_SESSION_H
18 #include "common/RefCountedObj.h"
19 #include "common/Mutex.h"
20 #include "include/Spinlock.h"
26 typedef boost::intrusive_ptr<Session> SessionRef;
28 typedef boost::intrusive_ptr<Backoff> BackoffRef;
31 #include "common/tracked_int_ptr.hpp"
32 typedef TrackedIntPtr<PG> PGRef;
34 typedef boost::intrusive_ptr<PG> PGRef;
38 * A Backoff represents one instance of either a PG or an OID
39 * being plugged at the client. It's refcounted and linked from
40 * the PG {pg_oid}_backoffs map and from the client Session
43 * The Backoff has a lock that protects it's internal fields.
45 * The PG has a backoff_lock that protects it's maps to Backoffs.
46 * This lock is *inside* of Backoff::lock.
48 * The Session has a backoff_lock that protects it's map of pg and
49 * oid backoffs. This lock is *inside* the Backoff::lock *and*
56 * Session::backoff_lock
58 * When the Session goes away, we move our backoff lists aside,
59 * then we lock each of the Backoffs we
60 * previously referenced and clear the Session* pointer. If the PG
61 * is still linked, we unlink it, too.
63 * When the PG clears the backoff, it will send an unblock message
64 * if the Session* is still non-null, and unlink the session.
68 struct Backoff : public RefCountedObject {
70 STATE_NEW = 1, ///< backoff in flight to client
71 STATE_ACKED = 2, ///< backoff acked
72 STATE_DELETING = 3 ///< backoff deleted, but un-acked
74 std::atomic_int state = {STATE_NEW};
75 spg_t pgid; ///< owning pgid
76 uint64_t id = 0; ///< unique id (within the Session)
79 return state.load() == STATE_NEW;
81 bool is_acked() const {
82 return state.load() == STATE_ACKED;
84 bool is_deleting() const {
85 return state.load() == STATE_DELETING;
87 const char *get_state_name() const {
88 switch (state.load()) {
89 case STATE_NEW: return "new";
90 case STATE_ACKED: return "acked";
91 case STATE_DELETING: return "deleting";
92 default: return "???";
97 // NOTE: the owning PG and session are either
99 // - both null (teardown), or
100 // - only session is set (and state == DELETING)
101 PGRef pg; ///< owning pg
102 SessionRef session; ///< owning session
103 hobject_t begin, end; ///< [) range to block, unless ==, then single obj
105 Backoff(spg_t pgid, PGRef pg, SessionRef s,
107 const hobject_t& b, const hobject_t& e)
108 : RefCountedObject(g_ceph_context, 0),
111 lock("Backoff::lock"),
117 friend ostream& operator<<(ostream& out, const Backoff& b) {
118 return out << "Backoff(" << &b << " " << b.pgid << " " << b.id
119 << " " << b.get_state_name()
120 << " [" << b.begin << "," << b.end << ") "
121 << " session " << b.session
122 << " pg " << b.pg << ")";
128 struct Session : public RefCountedObject {
129 EntityName entity_name;
133 WatchConState wstate;
135 Mutex session_dispatch_lock;
136 boost::intrusive::list<OpRequest> waiting_on_map;
138 Spinlock sent_epoch_lock;
139 epoch_t last_sent_epoch;
140 Spinlock received_map_lock;
141 epoch_t received_map_epoch; // largest epoch seen in MOSDMap from here
143 /// protects backoffs; orders inside Backoff::lock *and* PG::backoff_lock
145 std::atomic_int backoff_count= {0}; ///< simple count of backoffs
146 map<spg_t,map<hobject_t,set<BackoffRef>>> backoffs;
148 std::atomic<uint64_t> backoff_seq = {0};
150 explicit Session(CephContext *cct) :
151 RefCountedObject(cct),
154 session_dispatch_lock("Session::session_dispatch_lock"),
155 last_sent_epoch(0), received_map_epoch(0),
156 backoff_lock("Session::backoff_lock")
163 const hobject_t& start,
164 const hobject_t& end);
166 BackoffRef have_backoff(spg_t pgid, const hobject_t& oid) {
167 if (!backoff_count.load()) {
170 Mutex::Locker l(backoff_lock);
171 assert(!backoff_count == backoffs.empty());
172 auto i = backoffs.find(pgid);
173 if (i == backoffs.end()) {
176 auto p = i->second.lower_bound(oid);
177 if (p != i->second.begin() &&
181 if (p != i->second.end()) {
182 int r = cmp(oid, p->first);
183 if (r == 0 || r > 0) {
184 for (auto& q : p->second) {
185 if (r == 0 || oid < q->end) {
195 CephContext *cct, spg_t pgid, const hobject_t& oid, const Message *m);
197 void add_backoff(BackoffRef b) {
198 Mutex::Locker l(backoff_lock);
199 assert(!backoff_count == backoffs.empty());
200 backoffs[b->pgid][b->begin].insert(b);
204 // called by PG::release_*_backoffs and PG::clear_backoffs()
205 void rm_backoff(BackoffRef b) {
206 Mutex::Locker l(backoff_lock);
207 assert(b->lock.is_locked_by_me());
208 assert(b->session == this);
209 auto i = backoffs.find(b->pgid);
210 if (i != backoffs.end()) {
211 // may race with clear_backoffs()
212 auto p = i->second.find(b->begin);
213 if (p != i->second.end()) {
214 auto q = p->second.find(b);
215 if (q != p->second.end()) {
218 if (p->second.empty()) {
220 if (i->second.empty()) {
227 assert(!backoff_count == backoffs.empty());
229 void clear_backoffs();