]> git.proxmox.com Git - ceph.git/blob - ceph/src/mds/MDCache.cc
387cd9b478b0e71c22f72dfb8207b390e6b74dbc
[ceph.git] / ceph / src / mds / MDCache.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
7 *
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.
12 *
13 */
14
15 #include <errno.h>
16 #include <ostream>
17 #include <string>
18 #include <string_view>
19 #include <map>
20
21 #include "MDCache.h"
22 #include "MDSRank.h"
23 #include "Server.h"
24 #include "Locker.h"
25 #include "MDLog.h"
26 #include "MDBalancer.h"
27 #include "Migrator.h"
28 #include "ScrubStack.h"
29
30 #include "SnapClient.h"
31
32 #include "MDSMap.h"
33
34 #include "CInode.h"
35 #include "CDir.h"
36
37 #include "Mutation.h"
38
39 #include "include/ceph_fs.h"
40 #include "include/filepath.h"
41 #include "include/util.h"
42
43 #include "messages/MClientCaps.h"
44
45 #include "msg/Message.h"
46 #include "msg/Messenger.h"
47
48 #include "common/MemoryModel.h"
49 #include "common/errno.h"
50 #include "common/perf_counters.h"
51 #include "common/safe_io.h"
52
53 #include "osdc/Journaler.h"
54 #include "osdc/Filer.h"
55
56 #include "events/ESubtreeMap.h"
57 #include "events/EUpdate.h"
58 #include "events/EPeerUpdate.h"
59 #include "events/EImportFinish.h"
60 #include "events/EFragment.h"
61 #include "events/ECommitted.h"
62 #include "events/EPurged.h"
63 #include "events/ESessions.h"
64
65 #include "InoTable.h"
66
67 #include "common/Timer.h"
68
69 #include "perfglue/heap_profiler.h"
70
71
72 #include "common/config.h"
73 #include "include/ceph_assert.h"
74
75 #define dout_context g_ceph_context
76 #define dout_subsys ceph_subsys_mds
77 #undef dout_prefix
78 #define dout_prefix _prefix(_dout, mds)
79
80 using namespace std;
81
82 static ostream& _prefix(std::ostream *_dout, MDSRank *mds) {
83 return *_dout << "mds." << mds->get_nodeid() << ".cache ";
84 }
85
86 set<int> SimpleLock::empty_gather_set;
87
88
89 /**
90 * All non-I/O contexts that require a reference
91 * to an MDCache instance descend from this.
92 */
93 class MDCacheContext : public virtual MDSContext {
94 protected:
95 MDCache *mdcache;
96 MDSRank *get_mds() override
97 {
98 ceph_assert(mdcache != NULL);
99 return mdcache->mds;
100 }
101 public:
102 explicit MDCacheContext(MDCache *mdc_) : mdcache(mdc_) {}
103 };
104
105
106 /**
107 * Only for contexts called back from an I/O completion
108 *
109 * Note: duplication of members wrt MDCacheContext, because
110 * it'ls the lesser of two evils compared with introducing
111 * yet another piece of (multiple) inheritance.
112 */
113 class MDCacheIOContext : public virtual MDSIOContextBase {
114 protected:
115 MDCache *mdcache;
116 MDSRank *get_mds() override
117 {
118 ceph_assert(mdcache != NULL);
119 return mdcache->mds;
120 }
121 public:
122 explicit MDCacheIOContext(MDCache *mdc_, bool track=true) :
123 MDSIOContextBase(track), mdcache(mdc_) {}
124 };
125
126 class MDCacheLogContext : public virtual MDSLogContextBase {
127 protected:
128 MDCache *mdcache;
129 MDSRank *get_mds() override
130 {
131 ceph_assert(mdcache != NULL);
132 return mdcache->mds;
133 }
134 public:
135 explicit MDCacheLogContext(MDCache *mdc_) : mdcache(mdc_) {}
136 };
137
138 MDCache::MDCache(MDSRank *m, PurgeQueue &purge_queue_) :
139 mds(m),
140 open_file_table(m),
141 filer(m->objecter, m->finisher),
142 stray_manager(m, purge_queue_),
143 recovery_queue(m),
144 trim_counter(g_conf().get_val<double>("mds_cache_trim_decay_rate"))
145 {
146 migrator.reset(new Migrator(mds, this));
147
148 max_dir_commit_size = g_conf()->mds_dir_max_commit_size ?
149 (g_conf()->mds_dir_max_commit_size << 20) :
150 (0.9 *(g_conf()->osd_max_write_size << 20));
151
152 cache_memory_limit = g_conf().get_val<Option::size_t>("mds_cache_memory_limit");
153 cache_reservation = g_conf().get_val<double>("mds_cache_reservation");
154 cache_health_threshold = g_conf().get_val<double>("mds_health_cache_threshold");
155
156 export_ephemeral_distributed_config = g_conf().get_val<bool>("mds_export_ephemeral_distributed");
157 export_ephemeral_random_config = g_conf().get_val<bool>("mds_export_ephemeral_random");
158 export_ephemeral_random_max = g_conf().get_val<double>("mds_export_ephemeral_random_max");
159
160 symlink_recovery = g_conf().get_val<bool>("mds_symlink_recovery");
161
162 lru.lru_set_midpoint(g_conf().get_val<double>("mds_cache_mid"));
163
164 bottom_lru.lru_set_midpoint(0);
165
166 decayrate.set_halflife(g_conf()->mds_decay_halflife);
167
168 upkeeper = std::thread(&MDCache::upkeep_main, this);
169 }
170
171 MDCache::~MDCache()
172 {
173 if (logger) {
174 g_ceph_context->get_perfcounters_collection()->remove(logger.get());
175 }
176 if (upkeeper.joinable())
177 upkeeper.join();
178 }
179
180 void MDCache::handle_conf_change(const std::set<std::string>& changed, const MDSMap& mdsmap)
181 {
182 dout(20) << "config changes: " << changed << dendl;
183 if (changed.count("mds_cache_memory_limit"))
184 cache_memory_limit = g_conf().get_val<Option::size_t>("mds_cache_memory_limit");
185 if (changed.count("mds_cache_reservation"))
186 cache_reservation = g_conf().get_val<double>("mds_cache_reservation");
187
188 bool ephemeral_pin_config_changed = false;
189 if (changed.count("mds_export_ephemeral_distributed")) {
190 export_ephemeral_distributed_config = g_conf().get_val<bool>("mds_export_ephemeral_distributed");
191 dout(10) << "Migrating any ephemeral distributed pinned inodes" << dendl;
192 /* copy to vector to avoid removals during iteration */
193 ephemeral_pin_config_changed = true;
194 }
195 if (changed.count("mds_export_ephemeral_random")) {
196 export_ephemeral_random_config = g_conf().get_val<bool>("mds_export_ephemeral_random");
197 dout(10) << "Migrating any ephemeral random pinned inodes" << dendl;
198 /* copy to vector to avoid removals during iteration */
199 ephemeral_pin_config_changed = true;
200 }
201 if (ephemeral_pin_config_changed) {
202 std::vector<CInode*> migrate;
203 migrate.assign(export_ephemeral_pins.begin(), export_ephemeral_pins.end());
204 for (auto& in : migrate) {
205 in->maybe_export_pin(true);
206 }
207 }
208 if (changed.count("mds_export_ephemeral_random_max")) {
209 export_ephemeral_random_max = g_conf().get_val<double>("mds_export_ephemeral_random_max");
210 }
211 if (changed.count("mds_health_cache_threshold"))
212 cache_health_threshold = g_conf().get_val<double>("mds_health_cache_threshold");
213 if (changed.count("mds_cache_mid"))
214 lru.lru_set_midpoint(g_conf().get_val<double>("mds_cache_mid"));
215 if (changed.count("mds_cache_trim_decay_rate")) {
216 trim_counter = DecayCounter(g_conf().get_val<double>("mds_cache_trim_decay_rate"));
217 }
218 if (changed.count("mds_symlink_recovery")) {
219 symlink_recovery = g_conf().get_val<bool>("mds_symlink_recovery");
220 dout(10) << "Storing symlink targets on file object's head " << symlink_recovery << dendl;
221 }
222
223 migrator->handle_conf_change(changed, mdsmap);
224 mds->balancer->handle_conf_change(changed, mdsmap);
225 }
226
227 void MDCache::log_stat()
228 {
229 mds->logger->set(l_mds_inodes, lru.lru_get_size());
230 mds->logger->set(l_mds_inodes_pinned, lru.lru_get_num_pinned());
231 mds->logger->set(l_mds_inodes_top, lru.lru_get_top());
232 mds->logger->set(l_mds_inodes_bottom, lru.lru_get_bot());
233 mds->logger->set(l_mds_inodes_pin_tail, lru.lru_get_pintail());
234 mds->logger->set(l_mds_inodes_with_caps, num_inodes_with_caps);
235 mds->logger->set(l_mds_caps, Capability::count());
236 if (root) {
237 mds->logger->set(l_mds_root_rfiles, root->get_inode()->rstat.rfiles);
238 mds->logger->set(l_mds_root_rbytes, root->get_inode()->rstat.rbytes);
239 mds->logger->set(l_mds_root_rsnaps, root->get_inode()->rstat.rsnaps);
240 }
241 }
242
243
244 //
245
246 bool MDCache::shutdown()
247 {
248 {
249 std::scoped_lock lock(upkeep_mutex);
250 upkeep_trim_shutdown = true;
251 upkeep_cvar.notify_one();
252 }
253 if (lru.lru_get_size() > 0) {
254 dout(7) << "WARNING: mdcache shutdown with non-empty cache" << dendl;
255 //show_cache();
256 show_subtrees();
257 //dump();
258 }
259 return true;
260 }
261
262
263 // ====================================================================
264 // some inode functions
265
266 void MDCache::add_inode(CInode *in)
267 {
268 // add to inode map
269 if (in->last == CEPH_NOSNAP) {
270 auto &p = inode_map[in->ino()];
271 ceph_assert(!p); // should be no dup inos!
272 p = in;
273 } else {
274 auto &p = snap_inode_map[in->vino()];
275 ceph_assert(!p); // should be no dup inos!
276 p = in;
277 }
278
279 if (in->ino() < MDS_INO_SYSTEM_BASE) {
280 if (in->ino() == CEPH_INO_ROOT)
281 root = in;
282 else if (in->ino() == MDS_INO_MDSDIR(mds->get_nodeid()))
283 myin = in;
284 else if (in->is_stray()) {
285 if (MDS_INO_STRAY_OWNER(in->ino()) == mds->get_nodeid()) {
286 strays[MDS_INO_STRAY_INDEX(in->ino())] = in;
287 }
288 }
289 if (in->is_base())
290 base_inodes.insert(in);
291 }
292 }
293
294 void MDCache::remove_inode(CInode *o)
295 {
296 dout(14) << "remove_inode " << *o << dendl;
297
298 if (o->get_parent_dn()) {
299 // FIXME: multiple parents?
300 CDentry *dn = o->get_parent_dn();
301 ceph_assert(!dn->is_dirty());
302 dn->dir->unlink_inode(dn); // leave dentry ... FIXME?
303 }
304
305 if (o->is_dirty())
306 o->mark_clean();
307 if (o->is_dirty_parent())
308 o->clear_dirty_parent();
309
310 o->clear_scatter_dirty();
311
312 o->clear_clientwriteable();
313
314 o->item_open_file.remove_myself();
315
316 if (o->state_test(CInode::STATE_QUEUEDEXPORTPIN))
317 export_pin_queue.erase(o);
318
319 if (o->state_test(CInode::STATE_DELAYEDEXPORTPIN))
320 export_pin_delayed_queue.erase(o);
321
322 o->clear_ephemeral_pin(true, true);
323
324 // remove from inode map
325 if (o->last == CEPH_NOSNAP) {
326 inode_map.erase(o->ino());
327 } else {
328 o->item_caps.remove_myself();
329 snap_inode_map.erase(o->vino());
330 }
331
332 if (o->ino() < MDS_INO_SYSTEM_BASE) {
333 if (o == root) root = 0;
334 if (o == myin) myin = 0;
335 if (o->is_stray()) {
336 if (MDS_INO_STRAY_OWNER(o->ino()) == mds->get_nodeid()) {
337 strays[MDS_INO_STRAY_INDEX(o->ino())] = 0;
338 }
339 }
340 if (o->is_base())
341 base_inodes.erase(o);
342 }
343
344 // delete it
345 ceph_assert(o->get_num_ref() == 0);
346 delete o;
347 }
348
349 file_layout_t MDCache::gen_default_file_layout(const MDSMap &mdsmap)
350 {
351 file_layout_t result = file_layout_t::get_default();
352 result.pool_id = mdsmap.get_first_data_pool();
353 return result;
354 }
355
356 file_layout_t MDCache::gen_default_log_layout(const MDSMap &mdsmap)
357 {
358 file_layout_t result = file_layout_t::get_default();
359 result.pool_id = mdsmap.get_metadata_pool();
360 if (g_conf()->mds_log_segment_size > 0) {
361 result.object_size = g_conf()->mds_log_segment_size;
362 result.stripe_unit = g_conf()->mds_log_segment_size;
363 }
364 return result;
365 }
366
367 void MDCache::init_layouts()
368 {
369 default_file_layout = gen_default_file_layout(*(mds->mdsmap));
370 default_log_layout = gen_default_log_layout(*(mds->mdsmap));
371 }
372
373 void MDCache::create_unlinked_system_inode(CInode *in, inodeno_t ino, int mode) const
374 {
375 auto _inode = in->_get_inode();
376 _inode->ino = ino;
377 _inode->version = 1;
378 _inode->xattr_version = 1;
379 _inode->mode = 0500 | mode;
380 _inode->size = 0;
381 _inode->ctime = _inode->mtime = _inode->btime = ceph_clock_now();
382 _inode->nlink = 1;
383 _inode->truncate_size = -1ull;
384 _inode->change_attr = 0;
385 _inode->export_pin = MDS_RANK_NONE;
386
387 // FIPS zeroization audit 20191117: this memset is not security related.
388 memset(&_inode->dir_layout, 0, sizeof(_inode->dir_layout));
389 if (_inode->is_dir()) {
390 _inode->dir_layout.dl_dir_hash = g_conf()->mds_default_dir_hash;
391 _inode->rstat.rsubdirs = 1; /* itself */
392 _inode->rstat.rctime = in->get_inode()->ctime;
393 } else {
394 _inode->layout = default_file_layout;
395 ++_inode->rstat.rfiles;
396 }
397 _inode->accounted_rstat = _inode->rstat;
398
399 if (in->is_base()) {
400 if (in->is_root())
401 in->inode_auth = mds_authority_t(mds->get_nodeid(), CDIR_AUTH_UNKNOWN);
402 else
403 in->inode_auth = mds_authority_t(mds_rank_t(in->ino() - MDS_INO_MDSDIR_OFFSET), CDIR_AUTH_UNKNOWN);
404 in->open_snaprealm(); // empty snaprealm
405 ceph_assert(!in->snaprealm->parent); // created its own
406 in->snaprealm->srnode.seq = 1;
407 }
408 }
409
410 CInode *MDCache::create_system_inode(inodeno_t ino, int mode)
411 {
412 dout(0) << "creating system inode with ino:" << ino << dendl;
413 CInode *in = new CInode(this);
414 create_unlinked_system_inode(in, ino, mode);
415 add_inode(in);
416 return in;
417 }
418
419 CInode *MDCache::create_root_inode()
420 {
421 CInode *in = create_system_inode(CEPH_INO_ROOT, S_IFDIR|0755);
422 auto _inode = in->_get_inode();
423 _inode->uid = g_conf()->mds_root_ino_uid;
424 _inode->gid = g_conf()->mds_root_ino_gid;
425 _inode->layout = default_file_layout;
426 _inode->layout.pool_id = mds->mdsmap->get_first_data_pool();
427 return in;
428 }
429
430 void MDCache::create_empty_hierarchy(MDSGather *gather)
431 {
432 // create root dir
433 CInode *root = create_root_inode();
434
435 // force empty root dir
436 CDir *rootdir = root->get_or_open_dirfrag(this, frag_t());
437 adjust_subtree_auth(rootdir, mds->get_nodeid());
438 rootdir->dir_rep = CDir::REP_ALL; //NONE;
439
440 ceph_assert(rootdir->get_fnode()->accounted_fragstat == rootdir->get_fnode()->fragstat);
441 ceph_assert(rootdir->get_fnode()->fragstat == root->get_inode()->dirstat);
442 ceph_assert(rootdir->get_fnode()->accounted_rstat == rootdir->get_fnode()->rstat);
443 /* Do no update rootdir rstat information of the fragment, rstat upkeep magic
444 * assume version 0 is stale/invalid.
445 */
446
447 rootdir->mark_complete();
448 rootdir->_get_fnode()->version = rootdir->pre_dirty();
449 rootdir->mark_dirty(mds->mdlog->get_current_segment());
450 rootdir->commit(0, gather->new_sub());
451
452 root->store(gather->new_sub());
453 root->mark_dirty_parent(mds->mdlog->get_current_segment(), true);
454 root->store_backtrace(gather->new_sub());
455 }
456
457 void MDCache::create_mydir_hierarchy(MDSGather *gather)
458 {
459 // create mds dir
460 CInode *my = create_system_inode(MDS_INO_MDSDIR(mds->get_nodeid()), S_IFDIR);
461
462 CDir *mydir = my->get_or_open_dirfrag(this, frag_t());
463 auto mydir_fnode = mydir->_get_fnode();
464
465 adjust_subtree_auth(mydir, mds->get_nodeid());
466
467 LogSegment *ls = mds->mdlog->get_current_segment();
468
469 // stray dir
470 for (int i = 0; i < NUM_STRAY; ++i) {
471 CInode *stray = create_system_inode(MDS_INO_STRAY(mds->get_nodeid(), i), S_IFDIR);
472 CDir *straydir = stray->get_or_open_dirfrag(this, frag_t());
473 CachedStackStringStream css;
474 *css << "stray" << i;
475 CDentry *sdn = mydir->add_primary_dentry(css->str(), stray, "");
476 sdn->_mark_dirty(mds->mdlog->get_current_segment());
477
478 stray->_get_inode()->dirstat = straydir->get_fnode()->fragstat;
479
480 mydir_fnode->rstat.add(stray->get_inode()->rstat);
481 mydir_fnode->fragstat.nsubdirs++;
482 // save them
483 straydir->mark_complete();
484 straydir->_get_fnode()->version = straydir->pre_dirty();
485 straydir->mark_dirty(ls);
486 straydir->commit(0, gather->new_sub());
487 stray->mark_dirty_parent(ls, true);
488 stray->store_backtrace(gather->new_sub());
489 }
490
491 mydir_fnode->accounted_fragstat = mydir->get_fnode()->fragstat;
492 mydir_fnode->accounted_rstat = mydir->get_fnode()->rstat;
493
494 auto inode = myin->_get_inode();
495 inode->dirstat = mydir->get_fnode()->fragstat;
496 inode->rstat = mydir->get_fnode()->rstat;
497 ++inode->rstat.rsubdirs;
498 inode->accounted_rstat = inode->rstat;
499
500 mydir->mark_complete();
501 mydir_fnode->version = mydir->pre_dirty();
502 mydir->mark_dirty(ls);
503 mydir->commit(0, gather->new_sub());
504
505 myin->store(gather->new_sub());
506 }
507
508 struct C_MDC_CreateSystemFile : public MDCacheLogContext {
509 MutationRef mut;
510 CDentry *dn;
511 version_t dpv;
512 MDSContext *fin;
513 C_MDC_CreateSystemFile(MDCache *c, MutationRef& mu, CDentry *d, version_t v, MDSContext *f) :
514 MDCacheLogContext(c), mut(mu), dn(d), dpv(v), fin(f) {}
515 void finish(int r) override {
516 mdcache->_create_system_file_finish(mut, dn, dpv, fin);
517 }
518 };
519
520 void MDCache::_create_system_file(CDir *dir, std::string_view name, CInode *in, MDSContext *fin)
521 {
522 dout(10) << "_create_system_file " << name << " in " << *dir << dendl;
523 CDentry *dn = dir->add_null_dentry(name);
524
525 dn->push_projected_linkage(in);
526 version_t dpv = dn->pre_dirty();
527
528 CDir *mdir = 0;
529 auto inode = in->_get_inode();
530 if (in->is_dir()) {
531 inode->rstat.rsubdirs = 1;
532
533 mdir = in->get_or_open_dirfrag(this, frag_t());
534 mdir->mark_complete();
535 mdir->_get_fnode()->version = mdir->pre_dirty();
536 } else {
537 inode->rstat.rfiles = 1;
538 }
539
540 inode->version = dn->pre_dirty();
541
542 SnapRealm *realm = dir->get_inode()->find_snaprealm();
543 dn->first = in->first = realm->get_newest_seq() + 1;
544
545 MutationRef mut(new MutationImpl());
546
547 // force some locks. hacky.
548 mds->locker->wrlock_force(&dir->inode->filelock, mut);
549 mds->locker->wrlock_force(&dir->inode->nestlock, mut);
550
551 mut->ls = mds->mdlog->get_current_segment();
552 EUpdate *le = new EUpdate(mds->mdlog, "create system file");
553 mds->mdlog->start_entry(le);
554
555 if (!in->is_mdsdir()) {
556 predirty_journal_parents(mut, &le->metablob, in, dir, PREDIRTY_PRIMARY|PREDIRTY_DIR, 1);
557 le->metablob.add_primary_dentry(dn, in, true);
558 } else {
559 predirty_journal_parents(mut, &le->metablob, in, dir, PREDIRTY_DIR, 1);
560 journal_dirty_inode(mut.get(), &le->metablob, in);
561 dn->push_projected_linkage(in->ino(), in->d_type());
562 le->metablob.add_remote_dentry(dn, true, in->ino(), in->d_type());
563 le->metablob.add_root(true, in);
564 }
565 if (mdir)
566 le->metablob.add_new_dir(mdir); // dirty AND complete AND new
567
568 mds->mdlog->submit_entry(le, new C_MDC_CreateSystemFile(this, mut, dn, dpv, fin));
569 mds->mdlog->flush();
570 }
571
572 void MDCache::_create_system_file_finish(MutationRef& mut, CDentry *dn, version_t dpv, MDSContext *fin)
573 {
574 dout(10) << "_create_system_file_finish " << *dn << dendl;
575
576 dn->pop_projected_linkage();
577 dn->mark_dirty(dpv, mut->ls);
578
579 CInode *in = dn->get_linkage()->get_inode();
580 in->mark_dirty(mut->ls);
581
582 if (in->is_dir()) {
583 CDir *dir = in->get_dirfrag(frag_t());
584 ceph_assert(dir);
585 dir->mark_dirty(mut->ls);
586 dir->mark_new(mut->ls);
587 }
588
589 mut->apply();
590 mds->locker->drop_locks(mut.get());
591 mut->cleanup();
592
593 fin->complete(0);
594
595 //if (dir && MDS_INO_IS_MDSDIR(in->ino()))
596 //migrator->export_dir(dir, (int)in->ino() - MDS_INO_MDSDIR_OFFSET);
597 }
598
599
600
601 struct C_MDS_RetryOpenRoot : public MDSInternalContext {
602 MDCache *cache;
603 explicit C_MDS_RetryOpenRoot(MDCache *c) : MDSInternalContext(c->mds), cache(c) {}
604 void finish(int r) override {
605 if (r < 0) {
606 // If we can't open root, something disastrous has happened: mark
607 // this rank damaged for operator intervention. Note that
608 // it is not okay to call suicide() here because we are in
609 // a Finisher callback.
610 cache->mds->damaged();
611 ceph_abort(); // damaged should never return
612 } else {
613 cache->open_root();
614 }
615 }
616 };
617
618 void MDCache::open_root_inode(MDSContext *c)
619 {
620 if (mds->get_nodeid() == mds->mdsmap->get_root()) {
621 CInode *in;
622 in = create_system_inode(CEPH_INO_ROOT, S_IFDIR|0755); // initially inaccurate!
623 in->fetch(c);
624 } else {
625 discover_base_ino(CEPH_INO_ROOT, c, mds->mdsmap->get_root());
626 }
627 }
628
629 void MDCache::open_mydir_inode(MDSContext *c)
630 {
631 CInode *in = create_system_inode(MDS_INO_MDSDIR(mds->get_nodeid()), S_IFDIR|0755); // initially inaccurate!
632 in->fetch(c);
633 }
634
635 void MDCache::open_mydir_frag(MDSContext *c)
636 {
637 open_mydir_inode(
638 new MDSInternalContextWrapper(mds,
639 new LambdaContext([this, c](int r) {
640 if (r < 0) {
641 c->complete(r);
642 return;
643 }
644 CDir *mydir = myin->get_or_open_dirfrag(this, frag_t());
645 ceph_assert(mydir);
646 adjust_subtree_auth(mydir, mds->get_nodeid());
647 mydir->fetch(c);
648 })
649 )
650 );
651 }
652
653 void MDCache::open_root()
654 {
655 dout(10) << "open_root" << dendl;
656
657 if (!root) {
658 open_root_inode(new C_MDS_RetryOpenRoot(this));
659 return;
660 }
661 if (mds->get_nodeid() == mds->mdsmap->get_root()) {
662 ceph_assert(root->is_auth());
663 CDir *rootdir = root->get_or_open_dirfrag(this, frag_t());
664 ceph_assert(rootdir);
665 if (!rootdir->is_subtree_root())
666 adjust_subtree_auth(rootdir, mds->get_nodeid());
667 if (!rootdir->is_complete()) {
668 rootdir->fetch(new C_MDS_RetryOpenRoot(this));
669 return;
670 }
671 } else {
672 ceph_assert(!root->is_auth());
673 CDir *rootdir = root->get_dirfrag(frag_t());
674 if (!rootdir) {
675 open_remote_dirfrag(root, frag_t(), new C_MDS_RetryOpenRoot(this));
676 return;
677 }
678 }
679
680 if (!myin) {
681 CInode *in = create_system_inode(MDS_INO_MDSDIR(mds->get_nodeid()), S_IFDIR|0755); // initially inaccurate!
682 in->fetch(new C_MDS_RetryOpenRoot(this));
683 return;
684 }
685 CDir *mydir = myin->get_or_open_dirfrag(this, frag_t());
686 ceph_assert(mydir);
687 adjust_subtree_auth(mydir, mds->get_nodeid());
688
689 populate_mydir();
690 }
691
692 void MDCache::advance_stray() {
693 // check whether the directory has been fragmented
694 if (stray_fragmenting_index >= 0) {
695 auto&& dfs = strays[stray_fragmenting_index]->get_dirfrags();
696 bool any_fragmenting = false;
697 for (const auto& dir : dfs) {
698 if (dir->state_test(CDir::STATE_FRAGMENTING) ||
699 mds->balancer->is_fragment_pending(dir->dirfrag())) {
700 any_fragmenting = true;
701 break;
702 }
703 }
704 if (!any_fragmenting)
705 stray_fragmenting_index = -1;
706 }
707
708 for (int i = 1; i < NUM_STRAY; i++){
709 stray_index = (stray_index + i) % NUM_STRAY;
710 if (stray_index != stray_fragmenting_index)
711 break;
712 }
713
714 if (stray_fragmenting_index == -1 && is_open()) {
715 // Fragment later stray dir in advance. We don't choose past
716 // stray dir because in-flight requests may still use it.
717 stray_fragmenting_index = (stray_index + 3) % NUM_STRAY;
718 auto&& dfs = strays[stray_fragmenting_index]->get_dirfrags();
719 bool any_fragmenting = false;
720 for (const auto& dir : dfs) {
721 if (dir->should_split()) {
722 mds->balancer->queue_split(dir, true);
723 any_fragmenting = true;
724 } else if (dir->should_merge()) {
725 mds->balancer->queue_merge(dir);
726 any_fragmenting = true;
727 }
728 }
729 if (!any_fragmenting)
730 stray_fragmenting_index = -1;
731 }
732
733 dout(10) << "advance_stray to index " << stray_index
734 << " fragmenting index " << stray_fragmenting_index << dendl;
735 }
736
737 void MDCache::populate_mydir()
738 {
739 ceph_assert(myin);
740 CDir *mydir = myin->get_or_open_dirfrag(this, frag_t());
741 ceph_assert(mydir);
742
743 dout(10) << "populate_mydir " << *mydir << dendl;
744
745 if (!mydir->is_complete()) {
746 mydir->fetch(new C_MDS_RetryOpenRoot(this));
747 return;
748 }
749
750 if (mydir->get_version() == 0 && mydir->state_test(CDir::STATE_BADFRAG)) {
751 // A missing dirfrag, we will recreate it. Before that, we must dirty
752 // it before dirtying any of the strays we create within it.
753 mds->clog->warn() << "fragment " << mydir->dirfrag() << " was unreadable, "
754 "recreating it now";
755 LogSegment *ls = mds->mdlog->get_current_segment();
756 mydir->state_clear(CDir::STATE_BADFRAG);
757 mydir->mark_complete();
758 mydir->_get_fnode()->version = mydir->pre_dirty();
759 mydir->mark_dirty(ls);
760 }
761
762 // open or create stray
763 uint64_t num_strays = 0;
764 for (int i = 0; i < NUM_STRAY; ++i) {
765 CachedStackStringStream css;
766 *css << "stray" << i;
767 CDentry *straydn = mydir->lookup(css->str());
768
769 // allow for older fs's with stray instead of stray0
770 if (straydn == NULL && i == 0)
771 straydn = mydir->lookup("stray");
772
773 if (!straydn || !straydn->get_linkage()->get_inode()) {
774 _create_system_file(mydir, css->strv(), create_system_inode(MDS_INO_STRAY(mds->get_nodeid(), i), S_IFDIR),
775 new C_MDS_RetryOpenRoot(this));
776 return;
777 }
778 ceph_assert(straydn);
779 ceph_assert(strays[i]);
780 // we make multiple passes through this method; make sure we only pin each stray once.
781 if (!strays[i]->state_test(CInode::STATE_STRAYPINNED)) {
782 strays[i]->get(CInode::PIN_STRAY);
783 strays[i]->state_set(CInode::STATE_STRAYPINNED);
784 strays[i]->get_stickydirs();
785 }
786 dout(20) << " stray num " << i << " is " << *strays[i] << dendl;
787
788 // open all frags
789 frag_vec_t leaves;
790 strays[i]->dirfragtree.get_leaves(leaves);
791 for (const auto& leaf : leaves) {
792 CDir *dir = strays[i]->get_dirfrag(leaf);
793 if (!dir) {
794 dir = strays[i]->get_or_open_dirfrag(this, leaf);
795 }
796
797 // DamageTable applies special handling to strays: it will
798 // have damaged() us out if one is damaged.
799 ceph_assert(!dir->state_test(CDir::STATE_BADFRAG));
800
801 if (dir->get_version() == 0) {
802 dir->fetch(new C_MDS_RetryOpenRoot(this));
803 return;
804 }
805
806 if (dir->get_frag_size() > 0)
807 num_strays += dir->get_frag_size();
808 }
809 }
810
811 // okay!
812 dout(10) << "populate_mydir done" << dendl;
813 ceph_assert(!open);
814 open = true;
815 mds->queue_waiters(waiting_for_open);
816
817 stray_manager.set_num_strays(num_strays);
818 stray_manager.activate();
819
820 scan_stray_dir();
821 }
822
823 void MDCache::open_foreign_mdsdir(inodeno_t ino, MDSContext *fin)
824 {
825 discover_base_ino(ino, fin, mds_rank_t(ino & (MAX_MDS-1)));
826 }
827
828 CDir *MDCache::get_stray_dir(CInode *in)
829 {
830 string straydname;
831 in->name_stray_dentry(straydname);
832
833 CInode *strayi = get_stray();
834 ceph_assert(strayi);
835 frag_t fg = strayi->pick_dirfrag(straydname);
836 CDir *straydir = strayi->get_dirfrag(fg);
837 ceph_assert(straydir);
838 return straydir;
839 }
840
841 MDSCacheObject *MDCache::get_object(const MDSCacheObjectInfo &info)
842 {
843 // inode?
844 if (info.ino)
845 return get_inode(info.ino, info.snapid);
846
847 // dir or dentry.
848 CDir *dir = get_dirfrag(info.dirfrag);
849 if (!dir) return 0;
850
851 if (info.dname.length())
852 return dir->lookup(info.dname, info.snapid);
853 else
854 return dir;
855 }
856
857
858 // ====================================================================
859 // consistent hash ring
860
861 /*
862 * hashing implementation based on Lamping and Veach's Jump Consistent Hash: https://arxiv.org/pdf/1406.2294.pdf
863 */
864 mds_rank_t MDCache::hash_into_rank_bucket(inodeno_t ino, frag_t fg)
865 {
866 const mds_rank_t max_mds = mds->mdsmap->get_max_mds();
867 uint64_t hash = rjhash64(ino);
868 if (fg)
869 hash = rjhash64(hash + rjhash64(fg.value()));
870
871 int64_t b = -1, j = 0;
872 while (j < max_mds) {
873 b = j;
874 hash = hash*2862933555777941757ULL + 1;
875 j = (b + 1) * (double(1LL << 31) / double((hash >> 33) + 1));
876 }
877 // verify bounds before returning
878 auto result = mds_rank_t(b);
879 ceph_assert(result >= 0 && result < max_mds);
880 return result;
881 }
882
883
884 // ====================================================================
885 // subtree management
886
887 /*
888 * adjust the dir_auth of a subtree.
889 * merge with parent and/or child subtrees, if is it appropriate.
890 * merge can ONLY happen if both parent and child have unambiguous auth.
891 */
892 void MDCache::adjust_subtree_auth(CDir *dir, mds_authority_t auth, bool adjust_pop)
893 {
894 dout(7) << "adjust_subtree_auth " << dir->get_dir_auth() << " -> " << auth
895 << " on " << *dir << dendl;
896
897 show_subtrees();
898
899 CDir *root;
900 if (dir->inode->is_base()) {
901 root = dir; // bootstrap hack.
902 if (subtrees.count(root) == 0) {
903 subtrees[root];
904 root->get(CDir::PIN_SUBTREE);
905 }
906 } else {
907 root = get_subtree_root(dir); // subtree root
908 }
909 ceph_assert(root);
910 ceph_assert(subtrees.count(root));
911 dout(7) << " current root is " << *root << dendl;
912
913 if (root == dir) {
914 // i am already a subtree.
915 dir->set_dir_auth(auth);
916 } else {
917 // i am a new subtree.
918 dout(10) << " new subtree at " << *dir << dendl;
919 ceph_assert(subtrees.count(dir) == 0);
920 subtrees[dir]; // create empty subtree bounds list for me.
921 dir->get(CDir::PIN_SUBTREE);
922
923 // set dir_auth
924 dir->set_dir_auth(auth);
925
926 // move items nested beneath me, under me.
927 set<CDir*>::iterator p = subtrees[root].begin();
928 while (p != subtrees[root].end()) {
929 set<CDir*>::iterator next = p;
930 ++next;
931 if (get_subtree_root((*p)->get_parent_dir()) == dir) {
932 // move under me
933 dout(10) << " claiming child bound " << **p << dendl;
934 subtrees[dir].insert(*p);
935 subtrees[root].erase(p);
936 }
937 p = next;
938 }
939
940 // i am a bound of the parent subtree.
941 subtrees[root].insert(dir);
942
943 // i am now the subtree root.
944 root = dir;
945
946 // adjust recursive pop counters
947 if (adjust_pop && dir->is_auth()) {
948 CDir *p = dir->get_parent_dir();
949 while (p) {
950 p->pop_auth_subtree.sub(dir->pop_auth_subtree);
951 if (p->is_subtree_root()) break;
952 p = p->inode->get_parent_dir();
953 }
954 }
955 }
956
957 show_subtrees();
958 }
959
960
961 void MDCache::try_subtree_merge(CDir *dir)
962 {
963 dout(7) << "try_subtree_merge " << *dir << dendl;
964 // record my old bounds
965 auto oldbounds = subtrees.at(dir);
966
967 set<CInode*> to_eval;
968 // try merge at my root
969 try_subtree_merge_at(dir, &to_eval);
970
971 // try merge at my old bounds
972 for (auto bound : oldbounds)
973 try_subtree_merge_at(bound, &to_eval);
974
975 if (!(mds->is_any_replay() || mds->is_resolve())) {
976 for(auto in : to_eval)
977 eval_subtree_root(in);
978 }
979 }
980
981 void MDCache::try_subtree_merge_at(CDir *dir, set<CInode*> *to_eval, bool adjust_pop)
982 {
983 dout(10) << "try_subtree_merge_at " << *dir << dendl;
984
985 if (dir->dir_auth.second != CDIR_AUTH_UNKNOWN ||
986 dir->state_test(CDir::STATE_EXPORTBOUND) ||
987 dir->state_test(CDir::STATE_AUXSUBTREE))
988 return;
989
990 auto it = subtrees.find(dir);
991 ceph_assert(it != subtrees.end());
992
993 // merge with parent?
994 CDir *parent = dir;
995 if (!dir->inode->is_base())
996 parent = get_subtree_root(dir->get_parent_dir());
997
998 if (parent != dir && // we have a parent,
999 parent->dir_auth == dir->dir_auth) { // auth matches,
1000 // merge with parent.
1001 dout(10) << " subtree merge at " << *dir << dendl;
1002 dir->set_dir_auth(CDIR_AUTH_DEFAULT);
1003
1004 // move our bounds under the parent
1005 subtrees[parent].insert(it->second.begin(), it->second.end());
1006
1007 // we are no longer a subtree or bound
1008 dir->put(CDir::PIN_SUBTREE);
1009 subtrees.erase(it);
1010 subtrees[parent].erase(dir);
1011
1012 // adjust popularity?
1013 if (adjust_pop && dir->is_auth()) {
1014 CDir *cur = dir;
1015 CDir *p = dir->get_parent_dir();
1016 while (p) {
1017 p->pop_auth_subtree.add(dir->pop_auth_subtree);
1018 p->pop_lru_subdirs.push_front(&cur->get_inode()->item_pop_lru);
1019 if (p->is_subtree_root()) break;
1020 cur = p;
1021 p = p->inode->get_parent_dir();
1022 }
1023 }
1024
1025 if (to_eval && dir->get_inode()->is_auth())
1026 to_eval->insert(dir->get_inode());
1027
1028 show_subtrees(15);
1029 }
1030 }
1031
1032 void MDCache::eval_subtree_root(CInode *diri)
1033 {
1034 // evaluate subtree inode filelock?
1035 // (we should scatter the filelock on subtree bounds)
1036 ceph_assert(diri->is_auth());
1037 mds->locker->try_eval(diri, CEPH_LOCK_IFILE | CEPH_LOCK_INEST);
1038 }
1039
1040
1041 void MDCache::adjust_bounded_subtree_auth(CDir *dir, const set<CDir*>& bounds, mds_authority_t auth)
1042 {
1043 dout(7) << "adjust_bounded_subtree_auth " << dir->get_dir_auth() << " -> " << auth
1044 << " on " << *dir
1045 << " bounds " << bounds
1046 << dendl;
1047
1048 show_subtrees();
1049
1050 CDir *root;
1051 if (dir->ino() == CEPH_INO_ROOT) {
1052 root = dir; // bootstrap hack.
1053 if (subtrees.count(root) == 0) {
1054 subtrees[root];
1055 root->get(CDir::PIN_SUBTREE);
1056 }
1057 } else {
1058 root = get_subtree_root(dir); // subtree root
1059 }
1060 ceph_assert(root);
1061 ceph_assert(subtrees.count(root));
1062 dout(7) << " current root is " << *root << dendl;
1063
1064 mds_authority_t oldauth = dir->authority();
1065
1066 if (root == dir) {
1067 // i am already a subtree.
1068 dir->set_dir_auth(auth);
1069 } else {
1070 // i am a new subtree.
1071 dout(10) << " new subtree at " << *dir << dendl;
1072 ceph_assert(subtrees.count(dir) == 0);
1073 subtrees[dir]; // create empty subtree bounds list for me.
1074 dir->get(CDir::PIN_SUBTREE);
1075
1076 // set dir_auth
1077 dir->set_dir_auth(auth);
1078
1079 // move items nested beneath me, under me.
1080 set<CDir*>::iterator p = subtrees[root].begin();
1081 while (p != subtrees[root].end()) {
1082 set<CDir*>::iterator next = p;
1083 ++next;
1084 if (get_subtree_root((*p)->get_parent_dir()) == dir) {
1085 // move under me
1086 dout(10) << " claiming child bound " << **p << dendl;
1087 subtrees[dir].insert(*p);
1088 subtrees[root].erase(p);
1089 }
1090 p = next;
1091 }
1092
1093 // i am a bound of the parent subtree.
1094 subtrees[root].insert(dir);
1095
1096 // i am now the subtree root.
1097 root = dir;
1098 }
1099
1100 set<CInode*> to_eval;
1101
1102 // verify/adjust bounds.
1103 // - these may be new, or
1104 // - beneath existing ambiguous bounds (which will be collapsed),
1105 // - but NOT beneath unambiguous bounds.
1106 for (const auto& bound : bounds) {
1107 // new bound?
1108 if (subtrees[dir].count(bound) == 0) {
1109 if (get_subtree_root(bound) == dir) {
1110 dout(10) << " new bound " << *bound << ", adjusting auth back to old " << oldauth << dendl;
1111 adjust_subtree_auth(bound, oldauth); // otherwise, adjust at bound.
1112 }
1113 else {
1114 dout(10) << " want bound " << *bound << dendl;
1115 CDir *t = get_subtree_root(bound->get_parent_dir());
1116 if (subtrees[t].count(bound) == 0) {
1117 ceph_assert(t != dir);
1118 dout(10) << " new bound " << *bound << dendl;
1119 adjust_subtree_auth(bound, t->authority());
1120 }
1121 // make sure it's nested beneath ambiguous subtree(s)
1122 while (1) {
1123 while (subtrees[dir].count(t) == 0)
1124 t = get_subtree_root(t->get_parent_dir());
1125 dout(10) << " swallowing intervening subtree at " << *t << dendl;
1126 adjust_subtree_auth(t, auth);
1127 try_subtree_merge_at(t, &to_eval);
1128 t = get_subtree_root(bound->get_parent_dir());
1129 if (t == dir) break;
1130 }
1131 }
1132 }
1133 else {
1134 dout(10) << " already have bound " << *bound << dendl;
1135 }
1136 }
1137 // merge stray bounds?
1138 while (!subtrees[dir].empty()) {
1139 set<CDir*> copy = subtrees[dir];
1140 for (set<CDir*>::iterator p = copy.begin(); p != copy.end(); ++p) {
1141 if (bounds.count(*p) == 0) {
1142 CDir *stray = *p;
1143 dout(10) << " swallowing extra subtree at " << *stray << dendl;
1144 adjust_subtree_auth(stray, auth);
1145 try_subtree_merge_at(stray, &to_eval);
1146 }
1147 }
1148 // swallowing subtree may add new subtree bounds
1149 if (copy == subtrees[dir])
1150 break;
1151 }
1152
1153 // bound should now match.
1154 verify_subtree_bounds(dir, bounds);
1155
1156 show_subtrees();
1157
1158 if (!(mds->is_any_replay() || mds->is_resolve())) {
1159 for(auto in : to_eval)
1160 eval_subtree_root(in);
1161 }
1162 }
1163
1164
1165 /*
1166 * return a set of CDir*'s that correspond to the given bound set. Only adjust
1167 * fragmentation as necessary to get an equivalent bounding set. That is, only
1168 * split if one of our frags spans the provided bounding set. Never merge.
1169 */
1170 void MDCache::get_force_dirfrag_bound_set(const vector<dirfrag_t>& dfs, set<CDir*>& bounds)
1171 {
1172 dout(10) << "get_force_dirfrag_bound_set " << dfs << dendl;
1173
1174 // sort by ino
1175 map<inodeno_t, fragset_t> byino;
1176 for (auto& frag : dfs) {
1177 byino[frag.ino].insert_raw(frag.frag);
1178 }
1179 dout(10) << " by ino: " << byino << dendl;
1180
1181 for (map<inodeno_t,fragset_t>::iterator p = byino.begin(); p != byino.end(); ++p) {
1182 p->second.simplify();
1183 CInode *diri = get_inode(p->first);
1184 if (!diri)
1185 continue;
1186 dout(10) << " checking fragset " << p->second.get() << " on " << *diri << dendl;
1187
1188 fragtree_t tmpdft;
1189 for (set<frag_t>::iterator q = p->second.begin(); q != p->second.end(); ++q)
1190 tmpdft.force_to_leaf(g_ceph_context, *q);
1191
1192 for (const auto& fg : p->second) {
1193 frag_vec_t leaves;
1194 diri->dirfragtree.get_leaves_under(fg, leaves);
1195 if (leaves.empty()) {
1196 frag_t approx_fg = diri->dirfragtree[fg.value()];
1197 frag_vec_t approx_leaves;
1198 tmpdft.get_leaves_under(approx_fg, approx_leaves);
1199 for (const auto& leaf : approx_leaves) {
1200 if (p->second.get().count(leaf) == 0) {
1201 // not bound, so the resolve message is from auth MDS of the dirfrag
1202 force_dir_fragment(diri, leaf);
1203 }
1204 }
1205 }
1206
1207 auto&& [complete, sibs] = diri->get_dirfrags_under(fg);
1208 for (const auto& sib : sibs)
1209 bounds.insert(sib);
1210 }
1211 }
1212 }
1213
1214 void MDCache::adjust_bounded_subtree_auth(CDir *dir, const vector<dirfrag_t>& bound_dfs, const mds_authority_t &auth)
1215 {
1216 dout(7) << "adjust_bounded_subtree_auth " << dir->get_dir_auth() << " -> " << auth
1217 << " on " << *dir << " bound_dfs " << bound_dfs << dendl;
1218
1219 set<CDir*> bounds;
1220 get_force_dirfrag_bound_set(bound_dfs, bounds);
1221 adjust_bounded_subtree_auth(dir, bounds, auth);
1222 }
1223
1224 void MDCache::map_dirfrag_set(const list<dirfrag_t>& dfs, set<CDir*>& result)
1225 {
1226 dout(10) << "map_dirfrag_set " << dfs << dendl;
1227
1228 // group by inode
1229 map<inodeno_t, fragset_t> ino_fragset;
1230 for (const auto &df : dfs) {
1231 ino_fragset[df.ino].insert_raw(df.frag);
1232 }
1233 // get frags
1234 for (map<inodeno_t, fragset_t>::iterator p = ino_fragset.begin();
1235 p != ino_fragset.end();
1236 ++p) {
1237 p->second.simplify();
1238 CInode *in = get_inode(p->first);
1239 if (!in)
1240 continue;
1241
1242 frag_vec_t fgs;
1243 for (const auto& fg : p->second) {
1244 in->dirfragtree.get_leaves_under(fg, fgs);
1245 }
1246
1247 dout(15) << "map_dirfrag_set " << p->second << " -> " << fgs
1248 << " on " << *in << dendl;
1249
1250 for (const auto& fg : fgs) {
1251 CDir *dir = in->get_dirfrag(fg);
1252 if (dir)
1253 result.insert(dir);
1254 }
1255 }
1256 }
1257
1258
1259
1260 CDir *MDCache::get_subtree_root(CDir *dir)
1261 {
1262 // find the underlying dir that delegates (or is about to delegate) auth
1263 while (true) {
1264 if (dir->is_subtree_root())
1265 return dir;
1266 dir = dir->get_inode()->get_parent_dir();
1267 if (!dir)
1268 return 0; // none
1269 }
1270 }
1271
1272 CDir *MDCache::get_projected_subtree_root(CDir *dir)
1273 {
1274 // find the underlying dir that delegates (or is about to delegate) auth
1275 while (true) {
1276 if (dir->is_subtree_root())
1277 return dir;
1278 dir = dir->get_inode()->get_projected_parent_dir();
1279 if (!dir)
1280 return 0; // none
1281 }
1282 }
1283
1284 void MDCache::remove_subtree(CDir *dir)
1285 {
1286 dout(10) << "remove_subtree " << *dir << dendl;
1287 auto it = subtrees.find(dir);
1288 ceph_assert(it != subtrees.end());
1289 subtrees.erase(it);
1290 dir->put(CDir::PIN_SUBTREE);
1291 if (dir->get_parent_dir()) {
1292 CDir *p = get_subtree_root(dir->get_parent_dir());
1293 auto it = subtrees.find(p);
1294 ceph_assert(it != subtrees.end());
1295 auto count = it->second.erase(dir);
1296 ceph_assert(count == 1);
1297 }
1298 }
1299
1300 void MDCache::get_subtree_bounds(CDir *dir, set<CDir*>& bounds)
1301 {
1302 ceph_assert(subtrees.count(dir));
1303 bounds = subtrees[dir];
1304 }
1305
1306 void MDCache::get_wouldbe_subtree_bounds(CDir *dir, set<CDir*>& bounds)
1307 {
1308 if (subtrees.count(dir)) {
1309 // just copy them, dir is a subtree.
1310 get_subtree_bounds(dir, bounds);
1311 } else {
1312 // find them
1313 CDir *root = get_subtree_root(dir);
1314 for (set<CDir*>::iterator p = subtrees[root].begin();
1315 p != subtrees[root].end();
1316 ++p) {
1317 CDir *t = *p;
1318 while (t != root) {
1319 t = t->get_parent_dir();
1320 ceph_assert(t);
1321 if (t == dir) {
1322 bounds.insert(*p);
1323 continue;
1324 }
1325 }
1326 }
1327 }
1328 }
1329
1330 void MDCache::verify_subtree_bounds(CDir *dir, const set<CDir*>& bounds)
1331 {
1332 // for debugging only.
1333 ceph_assert(subtrees.count(dir));
1334 if (bounds != subtrees[dir]) {
1335 dout(0) << "verify_subtree_bounds failed" << dendl;
1336 set<CDir*> b = bounds;
1337 for (auto &cd : subtrees[dir]) {
1338 if (bounds.count(cd)) {
1339 b.erase(cd);
1340 continue;
1341 }
1342 dout(0) << " missing bound " << *cd << dendl;
1343 }
1344 for (const auto &cd : b)
1345 dout(0) << " extra bound " << *cd << dendl;
1346 }
1347 ceph_assert(bounds == subtrees[dir]);
1348 }
1349
1350 void MDCache::verify_subtree_bounds(CDir *dir, const list<dirfrag_t>& bounds)
1351 {
1352 // for debugging only.
1353 ceph_assert(subtrees.count(dir));
1354
1355 // make sure that any bounds i do have are properly noted as such.
1356 int failed = 0;
1357 for (const auto &fg : bounds) {
1358 CDir *bd = get_dirfrag(fg);
1359 if (!bd) continue;
1360 if (subtrees[dir].count(bd) == 0) {
1361 dout(0) << "verify_subtree_bounds failed: extra bound " << *bd << dendl;
1362 failed++;
1363 }
1364 }
1365 ceph_assert(failed == 0);
1366 }
1367
1368 void MDCache::project_subtree_rename(CInode *diri, CDir *olddir, CDir *newdir)
1369 {
1370 dout(10) << "project_subtree_rename " << *diri << " from " << *olddir
1371 << " to " << *newdir << dendl;
1372 projected_subtree_renames[diri].push_back(pair<CDir*,CDir*>(olddir, newdir));
1373 }
1374
1375 void MDCache::adjust_subtree_after_rename(CInode *diri, CDir *olddir, bool pop)
1376 {
1377 dout(10) << "adjust_subtree_after_rename " << *diri << " from " << *olddir << dendl;
1378
1379 CDir *newdir = diri->get_parent_dir();
1380
1381 if (pop) {
1382 map<CInode*,list<pair<CDir*,CDir*> > >::iterator p = projected_subtree_renames.find(diri);
1383 ceph_assert(p != projected_subtree_renames.end());
1384 ceph_assert(!p->second.empty());
1385 ceph_assert(p->second.front().first == olddir);
1386 ceph_assert(p->second.front().second == newdir);
1387 p->second.pop_front();
1388 if (p->second.empty())
1389 projected_subtree_renames.erase(p);
1390 }
1391
1392 // adjust total auth pin of freezing subtree
1393 if (olddir != newdir) {
1394 auto&& dfls = diri->get_nested_dirfrags();
1395 for (const auto& dir : dfls)
1396 olddir->adjust_freeze_after_rename(dir);
1397 }
1398
1399 // adjust subtree
1400 // N.B. make sure subtree dirfrags are at the front of the list
1401 auto dfls = diri->get_subtree_dirfrags();
1402 diri->get_nested_dirfrags(dfls);
1403 for (const auto& dir : dfls) {
1404 dout(10) << "dirfrag " << *dir << dendl;
1405 CDir *oldparent = get_subtree_root(olddir);
1406 dout(10) << " old parent " << *oldparent << dendl;
1407 CDir *newparent = get_subtree_root(newdir);
1408 dout(10) << " new parent " << *newparent << dendl;
1409
1410 auto& oldbounds = subtrees[oldparent];
1411 auto& newbounds = subtrees[newparent];
1412
1413 if (olddir != newdir)
1414 mds->balancer->adjust_pop_for_rename(olddir, dir, false);
1415
1416 if (oldparent == newparent) {
1417 dout(10) << "parent unchanged for " << *dir << " at " << *oldparent << dendl;
1418 } else if (dir->is_subtree_root()) {
1419 // children are fine. change parent.
1420 dout(10) << "moving " << *dir << " from " << *oldparent << " to " << *newparent << dendl;
1421 {
1422 auto n = oldbounds.erase(dir);
1423 ceph_assert(n == 1);
1424 }
1425 newbounds.insert(dir);
1426 // caller is responsible for 'eval diri'
1427 try_subtree_merge_at(dir, NULL, false);
1428 } else {
1429 // mid-subtree.
1430
1431 // see if any old bounds move to the new parent.
1432 std::vector<CDir*> tomove;
1433 for (const auto& bound : oldbounds) {
1434 CDir *broot = get_subtree_root(bound->get_parent_dir());
1435 if (broot != oldparent) {
1436 ceph_assert(broot == newparent);
1437 tomove.push_back(bound);
1438 }
1439 }
1440 for (const auto& bound : tomove) {
1441 dout(10) << "moving bound " << *bound << " from " << *oldparent << " to " << *newparent << dendl;
1442 oldbounds.erase(bound);
1443 newbounds.insert(bound);
1444 }
1445
1446 // did auth change?
1447 if (oldparent->authority() != newparent->authority()) {
1448 adjust_subtree_auth(dir, oldparent->authority(), false);
1449 // caller is responsible for 'eval diri'
1450 try_subtree_merge_at(dir, NULL, false);
1451 }
1452 }
1453
1454 if (olddir != newdir)
1455 mds->balancer->adjust_pop_for_rename(newdir, dir, true);
1456 }
1457
1458 show_subtrees();
1459 }
1460
1461 // ===================================
1462 // journal and snap/cow helpers
1463
1464
1465 /*
1466 * find first inode in cache that follows given snapid. otherwise, return current.
1467 */
1468 CInode *MDCache::pick_inode_snap(CInode *in, snapid_t follows)
1469 {
1470 dout(10) << "pick_inode_snap follows " << follows << " on " << *in << dendl;
1471 ceph_assert(in->last == CEPH_NOSNAP);
1472
1473 auto p = snap_inode_map.upper_bound(vinodeno_t(in->ino(), follows));
1474 if (p != snap_inode_map.end() && p->second->ino() == in->ino()) {
1475 dout(10) << "pick_inode_snap found " << *p->second << dendl;
1476 in = p->second;
1477 }
1478
1479 return in;
1480 }
1481
1482
1483 /*
1484 * note: i'm currently cheating wrt dirty and inode.version on cow
1485 * items. instead of doing a full dir predirty, i just take the
1486 * original item's version, and set the dirty flag (via
1487 * mutation::add_cow_{inode,dentry}() and mutation::apply(). that
1488 * means a special case in the dir commit clean sweep assertions.
1489 * bah.
1490 */
1491 CInode *MDCache::cow_inode(CInode *in, snapid_t last)
1492 {
1493 ceph_assert(last >= in->first);
1494
1495 CInode *oldin = new CInode(this, true, in->first, last);
1496 auto _inode = CInode::allocate_inode(*in->get_previous_projected_inode());
1497 _inode->trim_client_ranges(last);
1498 oldin->reset_inode(std::move(_inode));
1499 auto _xattrs = in->get_previous_projected_xattrs();
1500 oldin->reset_xattrs(std::move(_xattrs));
1501
1502 oldin->symlink = in->symlink;
1503
1504 if (in->first < in->oldest_snap)
1505 in->oldest_snap = in->first;
1506
1507 in->first = last+1;
1508
1509 dout(10) << "cow_inode " << *in << " to " << *oldin << dendl;
1510 add_inode(oldin);
1511
1512 if (in->last != CEPH_NOSNAP) {
1513 CInode *head_in = get_inode(in->ino());
1514 ceph_assert(head_in);
1515 auto ret = head_in->split_need_snapflush(oldin, in);
1516 if (ret.first) {
1517 oldin->client_snap_caps = in->client_snap_caps;
1518 if (!oldin->client_snap_caps.empty()) {
1519 for (int i = 0; i < num_cinode_locks; i++) {
1520 SimpleLock *lock = oldin->get_lock(cinode_lock_info[i].lock);
1521 ceph_assert(lock);
1522 if (lock->get_state() != LOCK_SNAP_SYNC) {
1523 ceph_assert(lock->is_stable());
1524 lock->set_state(LOCK_SNAP_SYNC); // gathering
1525 oldin->auth_pin(lock);
1526 }
1527 lock->get_wrlock(true);
1528 }
1529 }
1530 }
1531 if (!ret.second) {
1532 auto client_snap_caps = std::move(in->client_snap_caps);
1533 in->client_snap_caps.clear();
1534 in->item_open_file.remove_myself();
1535 in->item_caps.remove_myself();
1536
1537 if (!client_snap_caps.empty()) {
1538 MDSContext::vec finished;
1539 for (int i = 0; i < num_cinode_locks; i++) {
1540 SimpleLock *lock = in->get_lock(cinode_lock_info[i].lock);
1541 ceph_assert(lock);
1542 ceph_assert(lock->get_state() == LOCK_SNAP_SYNC); // gathering
1543 lock->put_wrlock();
1544 if (!lock->get_num_wrlocks()) {
1545 lock->set_state(LOCK_SYNC);
1546 lock->take_waiting(SimpleLock::WAIT_STABLE|SimpleLock::WAIT_RD, finished);
1547 in->auth_unpin(lock);
1548 }
1549 }
1550 mds->queue_waiters(finished);
1551 }
1552 }
1553 return oldin;
1554 }
1555
1556 if (!in->client_caps.empty()) {
1557 const set<snapid_t>& snaps = in->find_snaprealm()->get_snaps();
1558 // clone caps?
1559 for (auto &p : in->client_caps) {
1560 client_t client = p.first;
1561 Capability *cap = &p.second;
1562 int issued = cap->need_snapflush() ? CEPH_CAP_ANY_WR : cap->issued();
1563 if ((issued & CEPH_CAP_ANY_WR) &&
1564 cap->client_follows < last) {
1565 dout(10) << " client." << client << " cap " << ccap_string(issued) << dendl;
1566 oldin->client_snap_caps.insert(client);
1567 cap->client_follows = last;
1568
1569 // we need snapflushes for any intervening snaps
1570 dout(10) << " snaps " << snaps << dendl;
1571 for (auto q = snaps.lower_bound(oldin->first);
1572 q != snaps.end() && *q <= last;
1573 ++q) {
1574 in->add_need_snapflush(oldin, *q, client);
1575 }
1576 } else {
1577 dout(10) << " ignoring client." << client << " cap follows " << cap->client_follows << dendl;
1578 }
1579 }
1580
1581 if (!oldin->client_snap_caps.empty()) {
1582 for (int i = 0; i < num_cinode_locks; i++) {
1583 SimpleLock *lock = oldin->get_lock(cinode_lock_info[i].lock);
1584 ceph_assert(lock);
1585 if (lock->get_state() != LOCK_SNAP_SYNC) {
1586 ceph_assert(lock->is_stable());
1587 lock->set_state(LOCK_SNAP_SYNC); // gathering
1588 oldin->auth_pin(lock);
1589 }
1590 lock->get_wrlock(true);
1591 }
1592 }
1593 }
1594 return oldin;
1595 }
1596
1597 void MDCache::journal_cow_dentry(MutationImpl *mut, EMetaBlob *metablob,
1598 CDentry *dn, snapid_t follows,
1599 CInode **pcow_inode, CDentry::linkage_t *dnl)
1600 {
1601 if (!dn) {
1602 dout(10) << "journal_cow_dentry got null CDentry, returning" << dendl;
1603 return;
1604 }
1605 dout(10) << "journal_cow_dentry follows " << follows << " on " << *dn << dendl;
1606 ceph_assert(dn->is_auth());
1607
1608 // nothing to cow on a null dentry, fix caller
1609 if (!dnl)
1610 dnl = dn->get_projected_linkage();
1611 ceph_assert(!dnl->is_null());
1612
1613 CInode *in = dnl->is_primary() ? dnl->get_inode() : NULL;
1614 bool cow_head = false;
1615 if (in && in->state_test(CInode::STATE_AMBIGUOUSAUTH)) {
1616 ceph_assert(in->is_frozen_inode());
1617 cow_head = true;
1618 }
1619 if (in && (in->is_multiversion() || cow_head)) {
1620 // multiversion inode.
1621 SnapRealm *realm = NULL;
1622
1623 if (in->get_projected_parent_dn() != dn) {
1624 ceph_assert(follows == CEPH_NOSNAP);
1625 realm = dn->dir->inode->find_snaprealm();
1626 snapid_t dir_follows = get_global_snaprealm()->get_newest_seq();
1627 ceph_assert(dir_follows >= realm->get_newest_seq());
1628
1629 if (dir_follows+1 > dn->first) {
1630 snapid_t oldfirst = dn->first;
1631 dn->first = dir_follows+1;
1632 if (realm->has_snaps_in_range(oldfirst, dir_follows)) {
1633 CDir *dir = dn->dir;
1634 CDentry *olddn = dir->add_remote_dentry(dn->get_name(), in->ino(), in->d_type(), dn->alternate_name, oldfirst, dir_follows);
1635 dout(10) << " olddn " << *olddn << dendl;
1636 ceph_assert(dir->is_projected());
1637 olddn->set_projected_version(dir->get_projected_version());
1638 metablob->add_remote_dentry(olddn, true);
1639 mut->add_cow_dentry(olddn);
1640 // FIXME: adjust link count here? hmm.
1641
1642 if (dir_follows+1 > in->first)
1643 in->cow_old_inode(dir_follows, cow_head);
1644 }
1645 }
1646
1647 follows = dir_follows;
1648 if (in->snaprealm) {
1649 realm = in->snaprealm;
1650 ceph_assert(follows >= realm->get_newest_seq());
1651 }
1652 } else {
1653 realm = in->find_snaprealm();
1654 if (follows == CEPH_NOSNAP) {
1655 follows = get_global_snaprealm()->get_newest_seq();
1656 ceph_assert(follows >= realm->get_newest_seq());
1657 }
1658 }
1659
1660 // already cloned?
1661 if (follows < in->first) {
1662 dout(10) << "journal_cow_dentry follows " << follows << " < first on " << *in << dendl;
1663 return;
1664 }
1665
1666 if (!realm->has_snaps_in_range(in->first, follows)) {
1667 dout(10) << "journal_cow_dentry no snapshot follows " << follows << " on " << *in << dendl;
1668 in->first = follows + 1;
1669 return;
1670 }
1671
1672 in->cow_old_inode(follows, cow_head);
1673
1674 } else {
1675 SnapRealm *realm = dn->dir->inode->find_snaprealm();
1676 if (follows == CEPH_NOSNAP) {
1677 follows = get_global_snaprealm()->get_newest_seq();
1678 ceph_assert(follows >= realm->get_newest_seq());
1679 }
1680
1681 // already cloned?
1682 if (follows < dn->first) {
1683 dout(10) << "journal_cow_dentry follows " << follows << " < first on " << *dn << dendl;
1684 return;
1685 }
1686
1687 // update dn.first before adding old dentry to cdir's map
1688 snapid_t oldfirst = dn->first;
1689 dn->first = follows+1;
1690
1691 if (!realm->has_snaps_in_range(oldfirst, follows)) {
1692 dout(10) << "journal_cow_dentry no snapshot follows " << follows << " on " << *dn << dendl;
1693 if (in)
1694 in->first = follows+1;
1695 return;
1696 }
1697
1698 dout(10) << " dn " << *dn << dendl;
1699 CDir *dir = dn->get_dir();
1700 ceph_assert(dir->is_projected());
1701
1702 if (in) {
1703 CInode *oldin = cow_inode(in, follows);
1704 ceph_assert(in->is_projected());
1705 mut->add_cow_inode(oldin);
1706 if (pcow_inode)
1707 *pcow_inode = oldin;
1708 CDentry *olddn = dir->add_primary_dentry(dn->get_name(), oldin, dn->alternate_name, oldfirst, follows);
1709 dout(10) << " olddn " << *olddn << dendl;
1710 bool need_snapflush = !oldin->client_snap_caps.empty();
1711 if (need_snapflush) {
1712 mut->ls->open_files.push_back(&oldin->item_open_file);
1713 mds->locker->mark_need_snapflush_inode(oldin);
1714 }
1715 olddn->set_projected_version(dir->get_projected_version());
1716 metablob->add_primary_dentry(olddn, 0, true, false, false, need_snapflush);
1717 mut->add_cow_dentry(olddn);
1718 } else {
1719 ceph_assert(dnl->is_remote());
1720 CDentry *olddn = dir->add_remote_dentry(dn->get_name(), dnl->get_remote_ino(), dnl->get_remote_d_type(), dn->alternate_name, oldfirst, follows);
1721 dout(10) << " olddn " << *olddn << dendl;
1722
1723 olddn->set_projected_version(dir->get_projected_version());
1724 metablob->add_remote_dentry(olddn, true);
1725 mut->add_cow_dentry(olddn);
1726 }
1727 }
1728 }
1729
1730 void MDCache::journal_dirty_inode(MutationImpl *mut, EMetaBlob *metablob, CInode *in, snapid_t follows)
1731 {
1732 if (in->is_base()) {
1733 metablob->add_root(true, in);
1734 } else {
1735 if (follows == CEPH_NOSNAP && in->last != CEPH_NOSNAP)
1736 follows = in->first - 1;
1737 CDentry *dn = in->get_projected_parent_dn();
1738 if (!dn->get_projected_linkage()->is_null()) // no need to cow a null dentry
1739 journal_cow_dentry(mut, metablob, dn, follows);
1740 if (in->get_projected_inode()->is_backtrace_updated()) {
1741 bool dirty_pool = in->get_projected_inode()->layout.pool_id !=
1742 in->get_previous_projected_inode()->layout.pool_id;
1743 metablob->add_primary_dentry(dn, in, true, true, dirty_pool);
1744 } else {
1745 metablob->add_primary_dentry(dn, in, true);
1746 }
1747 }
1748 }
1749
1750
1751
1752 // nested ---------------------------------------------------------------
1753
1754 void MDCache::project_rstat_inode_to_frag(const MutationRef& mut,
1755 CInode *cur, CDir *parent, snapid_t first,
1756 int linkunlink, SnapRealm *prealm)
1757 {
1758 CDentry *parentdn = cur->get_projected_parent_dn();
1759
1760 if (cur->first > first)
1761 first = cur->first;
1762
1763 dout(10) << "projected_rstat_inode_to_frag first " << first << " linkunlink " << linkunlink
1764 << " " << *cur << dendl;
1765 dout(20) << " frag head is [" << parent->first << ",head] " << dendl;
1766 dout(20) << " inode update is [" << first << "," << cur->last << "]" << dendl;
1767
1768 /*
1769 * FIXME. this incompletely propagates rstats to _old_ parents
1770 * (i.e. shortly after a directory rename). but we need full
1771 * blown hard link backpointers to make this work properly...
1772 */
1773 snapid_t floor = parentdn->first;
1774 dout(20) << " floor of " << floor << " from parent dn " << *parentdn << dendl;
1775
1776 if (!prealm)
1777 prealm = parent->inode->find_snaprealm();
1778 const set<snapid_t> snaps = prealm->get_snaps();
1779
1780 if (cur->last != CEPH_NOSNAP) {
1781 ceph_assert(cur->dirty_old_rstats.empty());
1782 set<snapid_t>::const_iterator q = snaps.lower_bound(std::max(first, floor));
1783 if (q == snaps.end() || *q > cur->last)
1784 return;
1785 }
1786
1787 if (cur->last >= floor) {
1788 bool update = true;
1789 if (cur->state_test(CInode::STATE_AMBIGUOUSAUTH) && cur->is_auth()) {
1790 // rename src inode is not projected in the peer rename prep case. so we should
1791 // avoid updateing the inode.
1792 ceph_assert(linkunlink < 0);
1793 ceph_assert(cur->is_frozen_inode());
1794 update = false;
1795 }
1796 // hacky
1797 const CInode::mempool_inode *pi;
1798 if (update && mut->is_projected(cur)) {
1799 pi = cur->_get_projected_inode();
1800 } else {
1801 pi = cur->get_projected_inode().get();
1802 if (update) {
1803 // new inode
1804 ceph_assert(pi->rstat == pi->accounted_rstat);
1805 update = false;
1806 }
1807 }
1808 _project_rstat_inode_to_frag(pi, std::max(first, floor), cur->last, parent,
1809 linkunlink, update);
1810 }
1811
1812 if (g_conf()->mds_snap_rstat) {
1813 for (const auto &p : cur->dirty_old_rstats) {
1814 const auto &old = cur->get_old_inodes()->at(p);
1815 snapid_t ofirst = std::max(old.first, floor);
1816 auto it = snaps.lower_bound(ofirst);
1817 if (it == snaps.end() || *it > p)
1818 continue;
1819 if (p >= floor)
1820 _project_rstat_inode_to_frag(&old.inode, ofirst, p, parent, 0, false);
1821 }
1822 }
1823 cur->dirty_old_rstats.clear();
1824 }
1825
1826
1827 void MDCache::_project_rstat_inode_to_frag(const CInode::mempool_inode* inode, snapid_t ofirst, snapid_t last,
1828 CDir *parent, int linkunlink, bool update_inode)
1829 {
1830 dout(10) << "_project_rstat_inode_to_frag [" << ofirst << "," << last << "]" << dendl;
1831 dout(20) << " inode rstat " << inode->rstat << dendl;
1832 dout(20) << " inode accounted_rstat " << inode->accounted_rstat << dendl;
1833 nest_info_t delta;
1834 if (linkunlink == 0) {
1835 delta.add(inode->rstat);
1836 delta.sub(inode->accounted_rstat);
1837 } else if (linkunlink < 0) {
1838 delta.sub(inode->accounted_rstat);
1839 } else {
1840 delta.add(inode->rstat);
1841 }
1842 dout(20) << " delta " << delta << dendl;
1843
1844
1845 while (last >= ofirst) {
1846 /*
1847 * pick fnode version to update. at each iteration, we want to
1848 * pick a segment ending in 'last' to update. split as necessary
1849 * to make that work. then, adjust first up so that we only
1850 * update one segment at a time. then loop to cover the whole
1851 * [ofirst,last] interval.
1852 */
1853 nest_info_t *prstat;
1854 snapid_t first;
1855 auto pf = parent->_get_projected_fnode();
1856 if (last == CEPH_NOSNAP) {
1857 if (g_conf()->mds_snap_rstat)
1858 first = std::max(ofirst, parent->first);
1859 else
1860 first = parent->first;
1861 prstat = &pf->rstat;
1862 dout(20) << " projecting to head [" << first << "," << last << "] " << *prstat << dendl;
1863
1864 if (first > parent->first &&
1865 !(pf->rstat == pf->accounted_rstat)) {
1866 dout(10) << " target snapped and not fully accounted, cow to dirty_old_rstat ["
1867 << parent->first << "," << (first-1) << "] "
1868 << " " << *prstat << "/" << pf->accounted_rstat
1869 << dendl;
1870 parent->dirty_old_rstat[first-1].first = parent->first;
1871 parent->dirty_old_rstat[first-1].rstat = pf->rstat;
1872 parent->dirty_old_rstat[first-1].accounted_rstat = pf->accounted_rstat;
1873 }
1874 parent->first = first;
1875 } else if (!g_conf()->mds_snap_rstat) {
1876 // drop snapshots' rstats
1877 break;
1878 } else if (last >= parent->first) {
1879 first = parent->first;
1880 parent->dirty_old_rstat[last].first = first;
1881 parent->dirty_old_rstat[last].rstat = pf->rstat;
1882 parent->dirty_old_rstat[last].accounted_rstat = pf->accounted_rstat;
1883 prstat = &parent->dirty_old_rstat[last].rstat;
1884 dout(10) << " projecting to newly split dirty_old_fnode [" << first << "," << last << "] "
1885 << " " << *prstat << "/" << pf->accounted_rstat << dendl;
1886 } else {
1887 // be careful, dirty_old_rstat is a _sparse_ map.
1888 // sorry, this is ugly.
1889 first = ofirst;
1890
1891 // find any intersection with last
1892 auto it = parent->dirty_old_rstat.lower_bound(last);
1893 if (it == parent->dirty_old_rstat.end()) {
1894 dout(20) << " no dirty_old_rstat with last >= last " << last << dendl;
1895 if (!parent->dirty_old_rstat.empty() && parent->dirty_old_rstat.rbegin()->first >= first) {
1896 dout(20) << " last dirty_old_rstat ends at " << parent->dirty_old_rstat.rbegin()->first << dendl;
1897 first = parent->dirty_old_rstat.rbegin()->first+1;
1898 }
1899 } else {
1900 // *it last is >= last
1901 if (it->second.first <= last) {
1902 // *it intersects [first,last]
1903 if (it->second.first < first) {
1904 dout(10) << " splitting off left bit [" << it->second.first << "," << first-1 << "]" << dendl;
1905 parent->dirty_old_rstat[first-1] = it->second;
1906 it->second.first = first;
1907 }
1908 if (it->second.first > first)
1909 first = it->second.first;
1910 if (last < it->first) {
1911 dout(10) << " splitting off right bit [" << last+1 << "," << it->first << "]" << dendl;
1912 parent->dirty_old_rstat[last] = it->second;
1913 it->second.first = last+1;
1914 }
1915 } else {
1916 // *it is to the _right_ of [first,last]
1917 it = parent->dirty_old_rstat.lower_bound(first);
1918 // new *it last is >= first
1919 if (it->second.first <= last && // new *it isn't also to the right, and
1920 it->first >= first) { // it intersects our first bit,
1921 dout(10) << " staying to the right of [" << it->second.first << "," << it->first << "]..." << dendl;
1922 first = it->first+1;
1923 }
1924 dout(10) << " projecting to new dirty_old_rstat [" << first << "," << last << "]" << dendl;
1925 }
1926 }
1927 dout(20) << " projecting to dirty_old_rstat [" << first << "," << last << "]" << dendl;
1928 parent->dirty_old_rstat[last].first = first;
1929 prstat = &parent->dirty_old_rstat[last].rstat;
1930 }
1931
1932 // apply
1933 dout(20) << " project to [" << first << "," << last << "] " << *prstat << dendl;
1934 ceph_assert(last >= first);
1935 prstat->add(delta);
1936 dout(20) << " result [" << first << "," << last << "] " << *prstat << " " << *parent << dendl;
1937
1938 last = first-1;
1939 }
1940
1941 if (update_inode) {
1942 auto _inode = const_cast<CInode::mempool_inode*>(inode);
1943 _inode->accounted_rstat = _inode->rstat;
1944 }
1945 }
1946
1947 void MDCache::project_rstat_frag_to_inode(const nest_info_t& rstat,
1948 const nest_info_t& accounted_rstat,
1949 snapid_t ofirst, snapid_t last,
1950 CInode *pin, bool cow_head)
1951 {
1952 dout(10) << "project_rstat_frag_to_inode [" << ofirst << "," << last << "]" << dendl;
1953 dout(20) << " frag rstat " << rstat << dendl;
1954 dout(20) << " frag accounted_rstat " << accounted_rstat << dendl;
1955 nest_info_t delta = rstat;
1956 delta.sub(accounted_rstat);
1957 dout(20) << " delta " << delta << dendl;
1958
1959 CInode::old_inode_map_ptr _old_inodes;
1960 while (last >= ofirst) {
1961 CInode::mempool_inode *pi;
1962 snapid_t first;
1963 if (last == pin->last) {
1964 pi = pin->_get_projected_inode();
1965 first = std::max(ofirst, pin->first);
1966 if (first > pin->first) {
1967 auto& old = pin->cow_old_inode(first-1, cow_head);
1968 dout(20) << " cloned old_inode rstat is " << old.inode.rstat << dendl;
1969 }
1970 } else {
1971 if (!_old_inodes) {
1972 _old_inodes = CInode::allocate_old_inode_map();
1973 if (pin->is_any_old_inodes())
1974 *_old_inodes = *pin->get_old_inodes();
1975 }
1976 if (last >= pin->first) {
1977 first = pin->first;
1978 pin->cow_old_inode(last, cow_head);
1979 } else {
1980 // our life is easier here because old_inodes is not sparse
1981 // (although it may not begin at snapid 1)
1982 auto it = _old_inodes->lower_bound(last);
1983 if (it == _old_inodes->end()) {
1984 dout(10) << " no old_inode <= " << last << ", done." << dendl;
1985 break;
1986 }
1987 first = it->second.first;
1988 if (first > last) {
1989 dout(10) << " oldest old_inode is [" << first << "," << it->first << "], done." << dendl;
1990 //assert(p == pin->old_inodes.begin());
1991 break;
1992 }
1993 if (it->first > last) {
1994 dout(10) << " splitting right old_inode [" << first << "," << it->first << "] to ["
1995 << (last+1) << "," << it->first << "]" << dendl;
1996 (*_old_inodes)[last] = it->second;
1997 it->second.first = last+1;
1998 pin->dirty_old_rstats.insert(it->first);
1999 }
2000 }
2001 if (first < ofirst) {
2002 dout(10) << " splitting left old_inode [" << first << "," << last << "] to ["
2003 << first << "," << ofirst-1 << "]" << dendl;
2004 (*_old_inodes)[ofirst-1] = (*_old_inodes)[last];
2005 pin->dirty_old_rstats.insert(ofirst-1);
2006 (*_old_inodes)[last].first = first = ofirst;
2007 }
2008 pi = &(*_old_inodes)[last].inode;
2009 pin->dirty_old_rstats.insert(last);
2010 }
2011 dout(20) << " projecting to [" << first << "," << last << "] " << pi->rstat << dendl;
2012 pi->rstat.add(delta);
2013 dout(20) << " result [" << first << "," << last << "] " << pi->rstat << dendl;
2014
2015 last = first-1;
2016 }
2017 if (_old_inodes)
2018 pin->reset_old_inodes(std::move(_old_inodes));
2019 }
2020
2021 void MDCache::broadcast_quota_to_client(CInode *in, client_t exclude_ct, bool quota_change)
2022 {
2023 if (!(mds->is_active() || mds->is_stopping()))
2024 return;
2025
2026 if (!in->is_auth() || in->is_frozen())
2027 return;
2028
2029 const auto& pi = in->get_projected_inode();
2030 if (!pi->quota.is_enable() && !quota_change)
2031 return;
2032
2033 // creaete snaprealm for quota inode (quota was set before mimic)
2034 if (!in->get_projected_srnode())
2035 mds->server->create_quota_realm(in);
2036
2037 for (auto &p : in->client_caps) {
2038 Capability *cap = &p.second;
2039 if (cap->is_noquota())
2040 continue;
2041
2042 if (exclude_ct >= 0 && exclude_ct != p.first)
2043 goto update;
2044
2045 if (cap->last_rbytes == pi->rstat.rbytes &&
2046 cap->last_rsize == pi->rstat.rsize())
2047 continue;
2048
2049 if (pi->quota.max_files > 0) {
2050 if (pi->rstat.rsize() >= pi->quota.max_files)
2051 goto update;
2052
2053 if ((abs(cap->last_rsize - pi->quota.max_files) >> 4) <
2054 abs(cap->last_rsize - pi->rstat.rsize()))
2055 goto update;
2056 }
2057
2058 if (pi->quota.max_bytes > 0) {
2059 if (pi->rstat.rbytes > pi->quota.max_bytes - (pi->quota.max_bytes >> 3))
2060 goto update;
2061
2062 if ((abs(cap->last_rbytes - pi->quota.max_bytes) >> 4) <
2063 abs(cap->last_rbytes - pi->rstat.rbytes))
2064 goto update;
2065 }
2066
2067 continue;
2068
2069 update:
2070 cap->last_rsize = pi->rstat.rsize();
2071 cap->last_rbytes = pi->rstat.rbytes;
2072
2073 auto msg = make_message<MClientQuota>();
2074 msg->ino = in->ino();
2075 msg->rstat = pi->rstat;
2076 msg->quota = pi->quota;
2077 mds->send_message_client_counted(msg, cap->get_session());
2078 }
2079 for (const auto &it : in->get_replicas()) {
2080 auto msg = make_message<MGatherCaps>();
2081 msg->ino = in->ino();
2082 mds->send_message_mds(msg, it.first);
2083 }
2084 }
2085
2086 /*
2087 * NOTE: we _have_ to delay the scatter if we are called during a
2088 * rejoin, because we can't twiddle locks between when the
2089 * rejoin_(weak|strong) is received and when we send the rejoin_ack.
2090 * normally, this isn't a problem: a recover mds doesn't twiddle locks
2091 * (no requests), and a survivor acks immediately. _except_ that
2092 * during rejoin_(weak|strong) processing, we may complete a lock
2093 * gather, and do a scatter_writebehind.. and we _can't_ twiddle the
2094 * scatterlock state in that case or the lock states will get out of
2095 * sync between the auth and replica.
2096 *
2097 * the simple solution is to never do the scatter here. instead, put
2098 * the scatterlock on a list if it isn't already wrlockable. this is
2099 * probably the best plan anyway, since we avoid too many
2100 * scatters/locks under normal usage.
2101 */
2102 /*
2103 * some notes on dirlock/nestlock scatterlock semantics:
2104 *
2105 * the fragstat (dirlock) will never be updated without
2106 * dirlock+nestlock wrlock held by the caller.
2107 *
2108 * the rstat (nestlock) _may_ get updated without a wrlock when nested
2109 * data is pushed up the tree. this could be changed with some
2110 * restructuring here, but in its current form we ensure that the
2111 * fragstat+rstat _always_ reflect an accurrate summation over the dir
2112 * frag, which is nice. and, we only need to track frags that need to
2113 * be nudged (and not inodes with pending rstat changes that need to
2114 * be pushed into the frag). a consequence of this is that the
2115 * accounted_rstat on scatterlock sync may not match our current
2116 * rstat. this is normal and expected.
2117 */
2118 void MDCache::predirty_journal_parents(MutationRef mut, EMetaBlob *blob,
2119 CInode *in, CDir *parent,
2120 int flags, int linkunlink,
2121 snapid_t cfollows)
2122 {
2123 bool primary_dn = flags & PREDIRTY_PRIMARY;
2124 bool do_parent_mtime = flags & PREDIRTY_DIR;
2125 bool shallow = flags & PREDIRTY_SHALLOW;
2126
2127 ceph_assert(mds->mdlog->entry_is_open());
2128
2129 // make sure stamp is set
2130 if (mut->get_mds_stamp() == utime_t())
2131 mut->set_mds_stamp(ceph_clock_now());
2132
2133 if (in->is_base())
2134 return;
2135
2136 dout(10) << "predirty_journal_parents"
2137 << (do_parent_mtime ? " do_parent_mtime":"")
2138 << " linkunlink=" << linkunlink
2139 << (primary_dn ? " primary_dn":" remote_dn")
2140 << (shallow ? " SHALLOW":"")
2141 << " follows " << cfollows
2142 << " " << *in << dendl;
2143
2144 if (!parent) {
2145 ceph_assert(primary_dn);
2146 parent = in->get_projected_parent_dn()->get_dir();
2147 }
2148
2149 if (flags == 0 && linkunlink == 0) {
2150 dout(10) << " no flags/linkunlink, just adding dir context to blob(s)" << dendl;
2151 blob->add_dir_context(parent);
2152 return;
2153 }
2154
2155 // build list of inodes to wrlock, dirty, and update
2156 list<CInode*> lsi;
2157 CInode *cur = in;
2158 CDentry *parentdn = NULL;
2159 bool first = true;
2160 while (parent) {
2161 //assert(cur->is_auth() || !primary_dn); // this breaks the rename auth twiddle hack
2162 ceph_assert(parent->is_auth());
2163
2164 // opportunistically adjust parent dirfrag
2165 CInode *pin = parent->get_inode();
2166
2167 // inode -> dirfrag
2168 mut->auth_pin(parent);
2169
2170 auto pf = parent->project_fnode(mut);
2171 pf->version = parent->pre_dirty();
2172
2173 if (do_parent_mtime || linkunlink) {
2174 ceph_assert(mut->is_wrlocked(&pin->filelock));
2175 ceph_assert(mut->is_wrlocked(&pin->nestlock));
2176 ceph_assert(cfollows == CEPH_NOSNAP);
2177
2178 // update stale fragstat/rstat?
2179 parent->resync_accounted_fragstat();
2180 parent->resync_accounted_rstat();
2181
2182 if (do_parent_mtime) {
2183 pf->fragstat.mtime = mut->get_op_stamp();
2184 pf->fragstat.change_attr++;
2185 dout(10) << "predirty_journal_parents bumping change_attr to " << pf->fragstat.change_attr << " on " << parent << dendl;
2186 if (pf->fragstat.mtime > pf->rstat.rctime) {
2187 dout(10) << "predirty_journal_parents updating mtime on " << *parent << dendl;
2188 pf->rstat.rctime = pf->fragstat.mtime;
2189 } else {
2190 dout(10) << "predirty_journal_parents updating mtime UNDERWATER on " << *parent << dendl;
2191 }
2192 }
2193 if (linkunlink) {
2194 dout(10) << "predirty_journal_parents updating size on " << *parent << dendl;
2195 if (in->is_dir()) {
2196 pf->fragstat.nsubdirs += linkunlink;
2197 //pf->rstat.rsubdirs += linkunlink;
2198 } else {
2199 pf->fragstat.nfiles += linkunlink;
2200 //pf->rstat.rfiles += linkunlink;
2201 }
2202 }
2203 }
2204
2205 // rstat
2206 if (!primary_dn) {
2207 // don't update parent this pass
2208 } else if (!linkunlink && !(pin->nestlock.can_wrlock(-1) &&
2209 pin->versionlock.can_wrlock())) {
2210 dout(20) << " unwritable parent nestlock " << pin->nestlock
2211 << ", marking dirty rstat on " << *cur << dendl;
2212 cur->mark_dirty_rstat();
2213 } else {
2214 // if we don't hold a wrlock reference on this nestlock, take one,
2215 // because we are about to write into the dirfrag fnode and that needs
2216 // to commit before the lock can cycle.
2217 if (linkunlink) {
2218 ceph_assert(pin->nestlock.get_num_wrlocks() || mut->is_peer());
2219 }
2220
2221 if (!mut->is_wrlocked(&pin->nestlock)) {
2222 dout(10) << " taking wrlock on " << pin->nestlock << " on " << *pin << dendl;
2223 mds->locker->wrlock_force(&pin->nestlock, mut);
2224 }
2225
2226 // now we can project the inode rstat diff the dirfrag
2227 SnapRealm *prealm = pin->find_snaprealm();
2228
2229 snapid_t follows = cfollows;
2230 if (follows == CEPH_NOSNAP)
2231 follows = prealm->get_newest_seq();
2232
2233 snapid_t first = follows+1;
2234
2235 // first, if the frag is stale, bring it back in sync.
2236 parent->resync_accounted_rstat();
2237
2238 // now push inode rstats into frag
2239 project_rstat_inode_to_frag(mut, cur, parent, first, linkunlink, prealm);
2240 cur->clear_dirty_rstat();
2241 }
2242
2243 bool stop = false;
2244 if (!pin->is_auth() || (!mut->is_auth_pinned(pin) && !pin->can_auth_pin())) {
2245 dout(10) << "predirty_journal_parents !auth or ambig or can't authpin on " << *pin << dendl;
2246 stop = true;
2247 }
2248
2249 // delay propagating until later?
2250 if (!stop && !first &&
2251 g_conf()->mds_dirstat_min_interval > 0) {
2252 double since_last_prop = mut->get_mds_stamp() - pin->last_dirstat_prop;
2253 if (since_last_prop < g_conf()->mds_dirstat_min_interval) {
2254 dout(10) << "predirty_journal_parents last prop " << since_last_prop
2255 << " < " << g_conf()->mds_dirstat_min_interval
2256 << ", stopping" << dendl;
2257 stop = true;
2258 } else {
2259 dout(10) << "predirty_journal_parents last prop " << since_last_prop << " ago, continuing" << dendl;
2260 }
2261 }
2262
2263 // can cast only because i'm passing nowait=true in the sole user
2264 if (!stop &&
2265 !mut->is_wrlocked(&pin->nestlock) &&
2266 (!pin->versionlock.can_wrlock() || // make sure we can take versionlock, too
2267 !mds->locker->wrlock_try(&pin->nestlock, mut)
2268 )) { // ** do not initiate.. see above comment **
2269 dout(10) << "predirty_journal_parents can't wrlock one of " << pin->versionlock << " or " << pin->nestlock
2270 << " on " << *pin << dendl;
2271 stop = true;
2272 }
2273 if (stop) {
2274 dout(10) << "predirty_journal_parents stop. marking nestlock on " << *pin << dendl;
2275 mds->locker->mark_updated_scatterlock(&pin->nestlock);
2276 mut->ls->dirty_dirfrag_nest.push_back(&pin->item_dirty_dirfrag_nest);
2277 mut->add_updated_lock(&pin->nestlock);
2278 if (do_parent_mtime || linkunlink) {
2279 mds->locker->mark_updated_scatterlock(&pin->filelock);
2280 mut->ls->dirty_dirfrag_dir.push_back(&pin->item_dirty_dirfrag_dir);
2281 mut->add_updated_lock(&pin->filelock);
2282 }
2283 break;
2284 }
2285 if (!mut->is_wrlocked(&pin->versionlock))
2286 mds->locker->local_wrlock_grab(&pin->versionlock, mut);
2287
2288 ceph_assert(mut->is_wrlocked(&pin->nestlock) || mut->is_peer());
2289
2290 pin->last_dirstat_prop = mut->get_mds_stamp();
2291
2292 // dirfrag -> diri
2293 mut->auth_pin(pin);
2294 lsi.push_front(pin);
2295
2296 pin->pre_cow_old_inode(); // avoid cow mayhem!
2297
2298 auto pi = pin->project_inode(mut);
2299 pi.inode->version = pin->pre_dirty();
2300
2301 // dirstat
2302 if (do_parent_mtime || linkunlink) {
2303 dout(20) << "predirty_journal_parents add_delta " << pf->fragstat << dendl;
2304 dout(20) << "predirty_journal_parents - " << pf->accounted_fragstat << dendl;
2305 bool touched_mtime = false, touched_chattr = false;
2306 pi.inode->dirstat.add_delta(pf->fragstat, pf->accounted_fragstat, &touched_mtime, &touched_chattr);
2307 pf->accounted_fragstat = pf->fragstat;
2308 if (touched_mtime)
2309 pi.inode->mtime = pi.inode->ctime = pi.inode->dirstat.mtime;
2310 if (touched_chattr)
2311 pi.inode->change_attr = pi.inode->dirstat.change_attr;
2312 dout(20) << "predirty_journal_parents gives " << pi.inode->dirstat << " on " << *pin << dendl;
2313
2314 if (parent->get_frag() == frag_t()) { // i.e., we are the only frag
2315 if (pi.inode->dirstat.size() < 0)
2316 ceph_assert(!"negative dirstat size" == g_conf()->mds_verify_scatter);
2317 if (pi.inode->dirstat.size() != pf->fragstat.size()) {
2318 mds->clog->error() << "unmatched fragstat size on single dirfrag "
2319 << parent->dirfrag() << ", inode has " << pi.inode->dirstat
2320 << ", dirfrag has " << pf->fragstat;
2321
2322 // trust the dirfrag for now
2323 pi.inode->dirstat = pf->fragstat;
2324
2325 ceph_assert(!"unmatched fragstat size" == g_conf()->mds_verify_scatter);
2326 }
2327 }
2328 }
2329
2330 // rstat
2331 dout(10) << "predirty_journal_parents frag->inode on " << *parent << dendl;
2332
2333 // first, if the frag is stale, bring it back in sync.
2334 parent->resync_accounted_rstat();
2335
2336 if (g_conf()->mds_snap_rstat) {
2337 for (auto &p : parent->dirty_old_rstat) {
2338 project_rstat_frag_to_inode(p.second.rstat, p.second.accounted_rstat, p.second.first,
2339 p.first, pin, true);
2340 }
2341 }
2342 parent->dirty_old_rstat.clear();
2343 project_rstat_frag_to_inode(pf->rstat, pf->accounted_rstat, parent->first, CEPH_NOSNAP, pin, true);//false);
2344
2345 pf->accounted_rstat = pf->rstat;
2346
2347 if (parent->get_frag() == frag_t()) { // i.e., we are the only frag
2348 if (pi.inode->rstat.rbytes != pf->rstat.rbytes) {
2349 mds->clog->error() << "unmatched rstat rbytes on single dirfrag "
2350 << parent->dirfrag() << ", inode has " << pi.inode->rstat
2351 << ", dirfrag has " << pf->rstat;
2352
2353 // trust the dirfrag for now
2354 pi.inode->rstat = pf->rstat;
2355
2356 ceph_assert(!"unmatched rstat rbytes" == g_conf()->mds_verify_scatter);
2357 }
2358 }
2359
2360 parent->check_rstats();
2361 broadcast_quota_to_client(pin);
2362 if (pin->is_base())
2363 break;
2364 // next parent!
2365 cur = pin;
2366 parentdn = pin->get_projected_parent_dn();
2367 ceph_assert(parentdn);
2368 parent = parentdn->get_dir();
2369 linkunlink = 0;
2370 do_parent_mtime = false;
2371 primary_dn = true;
2372 first = false;
2373 }
2374
2375 // now, stick it in the blob
2376 ceph_assert(parent);
2377 ceph_assert(parent->is_auth());
2378 blob->add_dir_context(parent);
2379 blob->add_dir(parent, true);
2380 for (const auto& in : lsi) {
2381 journal_dirty_inode(mut.get(), blob, in);
2382 }
2383
2384 }
2385
2386
2387
2388
2389
2390 // ===================================
2391 // peer requests
2392
2393
2394 /*
2395 * some handlers for leader requests with peers. we need to make
2396 * sure leader journal commits before we forget we leadered them and
2397 * remove them from the uncommitted_leaders map (used during recovery
2398 * to commit|abort peers).
2399 */
2400 struct C_MDC_CommittedLeader : public MDCacheLogContext {
2401 metareqid_t reqid;
2402 C_MDC_CommittedLeader(MDCache *s, metareqid_t r) : MDCacheLogContext(s), reqid(r) {}
2403 void finish(int r) override {
2404 mdcache->_logged_leader_commit(reqid);
2405 }
2406 };
2407
2408 void MDCache::log_leader_commit(metareqid_t reqid)
2409 {
2410 dout(10) << "log_leader_commit " << reqid << dendl;
2411 uncommitted_leaders[reqid].committing = true;
2412 mds->mdlog->start_submit_entry(new ECommitted(reqid),
2413 new C_MDC_CommittedLeader(this, reqid));
2414 }
2415
2416 void MDCache::_logged_leader_commit(metareqid_t reqid)
2417 {
2418 dout(10) << "_logged_leader_commit " << reqid << dendl;
2419 ceph_assert(uncommitted_leaders.count(reqid));
2420 uncommitted_leaders[reqid].ls->uncommitted_leaders.erase(reqid);
2421 mds->queue_waiters(uncommitted_leaders[reqid].waiters);
2422 uncommitted_leaders.erase(reqid);
2423 }
2424
2425 // while active...
2426
2427 void MDCache::committed_leader_peer(metareqid_t r, mds_rank_t from)
2428 {
2429 dout(10) << "committed_leader_peer mds." << from << " on " << r << dendl;
2430 ceph_assert(uncommitted_leaders.count(r));
2431 uncommitted_leaders[r].peers.erase(from);
2432 if (!uncommitted_leaders[r].recovering && uncommitted_leaders[r].peers.empty())
2433 log_leader_commit(r);
2434 }
2435
2436 void MDCache::logged_leader_update(metareqid_t reqid)
2437 {
2438 dout(10) << "logged_leader_update " << reqid << dendl;
2439 ceph_assert(uncommitted_leaders.count(reqid));
2440 uncommitted_leaders[reqid].safe = true;
2441 auto p = pending_leaders.find(reqid);
2442 if (p != pending_leaders.end()) {
2443 pending_leaders.erase(p);
2444 if (pending_leaders.empty())
2445 process_delayed_resolve();
2446 }
2447 }
2448
2449 /*
2450 * Leader may crash after receiving all peers' commit acks, but before journalling
2451 * the final commit. Peers may crash after journalling the peer commit, but before
2452 * sending commit ack to the leader. Commit leaders with no uncommitted peer when
2453 * resolve finishes.
2454 */
2455 void MDCache::finish_committed_leaders()
2456 {
2457 for (map<metareqid_t, uleader>::iterator p = uncommitted_leaders.begin();
2458 p != uncommitted_leaders.end();
2459 ++p) {
2460 p->second.recovering = false;
2461 if (!p->second.committing && p->second.peers.empty()) {
2462 dout(10) << "finish_committed_leaders " << p->first << dendl;
2463 log_leader_commit(p->first);
2464 }
2465 }
2466 }
2467
2468 /*
2469 * at end of resolve... we must journal a commit|abort for all peer
2470 * updates, before moving on.
2471 *
2472 * this is so that the leader can safely journal ECommitted on ops it
2473 * leaders when it reaches up:active (all other recovering nodes must
2474 * complete resolve before that happens).
2475 */
2476 struct C_MDC_PeerCommit : public MDCacheLogContext {
2477 mds_rank_t from;
2478 metareqid_t reqid;
2479 C_MDC_PeerCommit(MDCache *c, int f, metareqid_t r) : MDCacheLogContext(c), from(f), reqid(r) {}
2480 void finish(int r) override {
2481 mdcache->_logged_peer_commit(from, reqid);
2482 }
2483 };
2484
2485 void MDCache::_logged_peer_commit(mds_rank_t from, metareqid_t reqid)
2486 {
2487 dout(10) << "_logged_peer_commit from mds." << from << " " << reqid << dendl;
2488
2489 // send a message
2490 auto req = make_message<MMDSPeerRequest>(reqid, 0, MMDSPeerRequest::OP_COMMITTED);
2491 mds->send_message_mds(req, from);
2492 }
2493
2494
2495
2496
2497
2498
2499 // ====================================================================
2500 // import map, recovery
2501
2502 void MDCache::_move_subtree_map_bound(dirfrag_t df, dirfrag_t oldparent, dirfrag_t newparent,
2503 map<dirfrag_t,vector<dirfrag_t> >& subtrees)
2504 {
2505 if (subtrees.count(oldparent)) {
2506 vector<dirfrag_t>& v = subtrees[oldparent];
2507 dout(10) << " removing " << df << " from " << oldparent << " bounds " << v << dendl;
2508 for (vector<dirfrag_t>::iterator it = v.begin(); it != v.end(); ++it)
2509 if (*it == df) {
2510 v.erase(it);
2511 break;
2512 }
2513 }
2514 if (subtrees.count(newparent)) {
2515 vector<dirfrag_t>& v = subtrees[newparent];
2516 dout(10) << " adding " << df << " to " << newparent << " bounds " << v << dendl;
2517 v.push_back(df);
2518 }
2519 }
2520
2521 ESubtreeMap *MDCache::create_subtree_map()
2522 {
2523 dout(10) << "create_subtree_map " << num_subtrees() << " subtrees, "
2524 << num_subtrees_fullauth() << " fullauth"
2525 << dendl;
2526
2527 show_subtrees();
2528
2529 ESubtreeMap *le = new ESubtreeMap();
2530 mds->mdlog->_start_entry(le);
2531
2532 map<dirfrag_t, CDir*> dirs_to_add;
2533
2534 if (myin) {
2535 CDir* mydir = myin->get_dirfrag(frag_t());
2536 dirs_to_add[mydir->dirfrag()] = mydir;
2537 }
2538
2539 // include all auth subtrees, and their bounds.
2540 // and a spanning tree to tie it to the root.
2541 for (auto& [dir, bounds] : subtrees) {
2542 // journal subtree as "ours" if we are
2543 // me, -2
2544 // me, me
2545 // me, !me (may be importing and ambiguous!)
2546
2547 // so not
2548 // !me, *
2549 if (dir->get_dir_auth().first != mds->get_nodeid())
2550 continue;
2551
2552 if (migrator->is_ambiguous_import(dir->dirfrag()) ||
2553 my_ambiguous_imports.count(dir->dirfrag())) {
2554 dout(15) << " ambig subtree " << *dir << dendl;
2555 le->ambiguous_subtrees.insert(dir->dirfrag());
2556 } else {
2557 dout(15) << " auth subtree " << *dir << dendl;
2558 }
2559
2560 dirs_to_add[dir->dirfrag()] = dir;
2561 le->subtrees[dir->dirfrag()].clear();
2562
2563 // bounds
2564 size_t nbounds = bounds.size();
2565 if (nbounds > 3) {
2566 dout(15) << " subtree has " << nbounds << " bounds" << dendl;
2567 }
2568 for (auto& bound : bounds) {
2569 if (nbounds <= 3) {
2570 dout(15) << " subtree bound " << *bound << dendl;
2571 }
2572 dirs_to_add[bound->dirfrag()] = bound;
2573 le->subtrees[dir->dirfrag()].push_back(bound->dirfrag());
2574 }
2575 }
2576
2577 // apply projected renames
2578 for (const auto& [diri, renames] : projected_subtree_renames) {
2579 for (const auto& [olddir, newdir] : renames) {
2580 dout(15) << " adjusting for projected rename of " << *diri << " to " << *newdir << dendl;
2581
2582 auto&& dfls = diri->get_dirfrags();
2583 for (const auto& dir : dfls) {
2584 dout(15) << "dirfrag " << dir->dirfrag() << " " << *dir << dendl;
2585 CDir *oldparent = get_projected_subtree_root(olddir);
2586 dout(15) << " old parent " << oldparent->dirfrag() << " " << *oldparent << dendl;
2587 CDir *newparent = get_projected_subtree_root(newdir);
2588 dout(15) << " new parent " << newparent->dirfrag() << " " << *newparent << dendl;
2589
2590 if (oldparent == newparent) {
2591 dout(15) << "parent unchanged for " << dir->dirfrag() << " at "
2592 << oldparent->dirfrag() << dendl;
2593 continue;
2594 }
2595
2596 if (dir->is_subtree_root()) {
2597 if (le->subtrees.count(newparent->dirfrag()) &&
2598 oldparent->get_dir_auth() != newparent->get_dir_auth())
2599 dirs_to_add[dir->dirfrag()] = dir;
2600 // children are fine. change parent.
2601 _move_subtree_map_bound(dir->dirfrag(), oldparent->dirfrag(), newparent->dirfrag(),
2602 le->subtrees);
2603 } else {
2604 // mid-subtree.
2605
2606 if (oldparent->get_dir_auth() != newparent->get_dir_auth()) {
2607 dout(10) << " creating subtree for " << dir->dirfrag() << dendl;
2608 // if oldparent is auth, subtree is mine; include it.
2609 if (le->subtrees.count(oldparent->dirfrag())) {
2610 dirs_to_add[dir->dirfrag()] = dir;
2611 le->subtrees[dir->dirfrag()].clear();
2612 }
2613 // if newparent is auth, subtree is a new bound
2614 if (le->subtrees.count(newparent->dirfrag())) {
2615 dirs_to_add[dir->dirfrag()] = dir;
2616 le->subtrees[newparent->dirfrag()].push_back(dir->dirfrag()); // newparent is auth; new bound
2617 }
2618 newparent = dir;
2619 }
2620
2621 // see if any old bounds move to the new parent.
2622 for (auto& bound : subtrees.at(oldparent)) {
2623 if (dir->contains(bound->get_parent_dir()))
2624 _move_subtree_map_bound(bound->dirfrag(), oldparent->dirfrag(), newparent->dirfrag(),
2625 le->subtrees);
2626 }
2627 }
2628 }
2629 }
2630 }
2631
2632 // simplify the journaled map. our in memory map may have more
2633 // subtrees than needed due to migrations that are just getting
2634 // started or just completing. but on replay, the "live" map will
2635 // be simple and we can do a straight comparison.
2636 for (auto& [frag, bfrags] : le->subtrees) {
2637 if (le->ambiguous_subtrees.count(frag))
2638 continue;
2639 unsigned i = 0;
2640 while (i < bfrags.size()) {
2641 dirfrag_t b = bfrags[i];
2642 if (le->subtrees.count(b) &&
2643 le->ambiguous_subtrees.count(b) == 0) {
2644 auto& bb = le->subtrees.at(b);
2645 dout(10) << "simplify: " << frag << " swallowing " << b << " with bounds " << bb << dendl;
2646 for (auto& r : bb) {
2647 bfrags.push_back(r);
2648 }
2649 dirs_to_add.erase(b);
2650 le->subtrees.erase(b);
2651 bfrags.erase(bfrags.begin() + i);
2652 } else {
2653 ++i;
2654 }
2655 }
2656 }
2657
2658 for (auto &p : dirs_to_add) {
2659 CDir *dir = p.second;
2660 le->metablob.add_dir_context(dir, EMetaBlob::TO_ROOT);
2661 le->metablob.add_dir(dir, false);
2662 }
2663
2664 dout(15) << " subtrees " << le->subtrees << dendl;
2665 dout(15) << " ambiguous_subtrees " << le->ambiguous_subtrees << dendl;
2666
2667 //le->metablob.print(cout);
2668 le->expire_pos = mds->mdlog->journaler->get_expire_pos();
2669 return le;
2670 }
2671
2672 void MDCache::dump_resolve_status(Formatter *f) const
2673 {
2674 f->open_object_section("resolve_status");
2675 f->dump_stream("resolve_gather") << resolve_gather;
2676 f->dump_stream("resolve_ack_gather") << resolve_gather;
2677 f->close_section();
2678 }
2679
2680 void MDCache::resolve_start(MDSContext *resolve_done_)
2681 {
2682 dout(10) << "resolve_start" << dendl;
2683 ceph_assert(!resolve_done);
2684 resolve_done.reset(resolve_done_);
2685
2686 if (mds->mdsmap->get_root() != mds->get_nodeid()) {
2687 // if we don't have the root dir, adjust it to UNKNOWN. during
2688 // resolve we want mds0 to explicit claim the portion of it that
2689 // it owns, so that anything beyond its bounds get left as
2690 // unknown.
2691 CDir *rootdir = root->get_dirfrag(frag_t());
2692 if (rootdir)
2693 adjust_subtree_auth(rootdir, CDIR_AUTH_UNKNOWN);
2694 }
2695 resolve_gather = recovery_set;
2696
2697 resolve_snapclient_commits = mds->snapclient->get_journaled_tids();
2698 }
2699
2700 void MDCache::send_resolves()
2701 {
2702 send_peer_resolves();
2703
2704 if (!resolve_done) {
2705 // I'm survivor: refresh snap cache
2706 mds->snapclient->sync(
2707 new MDSInternalContextWrapper(mds,
2708 new LambdaContext([this](int r) {
2709 maybe_finish_peer_resolve();
2710 })
2711 )
2712 );
2713 dout(10) << "send_resolves waiting for snapclient cache to sync" << dendl;
2714 return;
2715 }
2716 if (!resolve_ack_gather.empty()) {
2717 dout(10) << "send_resolves still waiting for resolve ack from ("
2718 << resolve_ack_gather << ")" << dendl;
2719 return;
2720 }
2721 if (!resolve_need_rollback.empty()) {
2722 dout(10) << "send_resolves still waiting for rollback to commit on ("
2723 << resolve_need_rollback << ")" << dendl;
2724 return;
2725 }
2726
2727 send_subtree_resolves();
2728 }
2729
2730 void MDCache::send_peer_resolves()
2731 {
2732 dout(10) << "send_peer_resolves" << dendl;
2733
2734 map<mds_rank_t, ref_t<MMDSResolve>> resolves;
2735
2736 if (mds->is_resolve()) {
2737 for (map<metareqid_t, upeer>::iterator p = uncommitted_peers.begin();
2738 p != uncommitted_peers.end();
2739 ++p) {
2740 mds_rank_t leader = p->second.leader;
2741 auto &m = resolves[leader];
2742 if (!m) m = make_message<MMDSResolve>();
2743 m->add_peer_request(p->first, false);
2744 }
2745 } else {
2746 set<mds_rank_t> resolve_set;
2747 mds->mdsmap->get_mds_set(resolve_set, MDSMap::STATE_RESOLVE);
2748 for (ceph::unordered_map<metareqid_t, MDRequestRef>::iterator p = active_requests.begin();
2749 p != active_requests.end();
2750 ++p) {
2751 MDRequestRef& mdr = p->second;
2752 if (!mdr->is_peer())
2753 continue;
2754 if (!mdr->peer_did_prepare() && !mdr->committing) {
2755 continue;
2756 }
2757 mds_rank_t leader = mdr->peer_to_mds;
2758 if (resolve_set.count(leader) || is_ambiguous_peer_update(p->first, leader)) {
2759 dout(10) << " including uncommitted " << *mdr << dendl;
2760 if (!resolves.count(leader))
2761 resolves[leader] = make_message<MMDSResolve>();
2762 if (!mdr->committing &&
2763 mdr->has_more() && mdr->more()->is_inode_exporter) {
2764 // re-send cap exports
2765 CInode *in = mdr->more()->rename_inode;
2766 map<client_t, Capability::Export> cap_map;
2767 in->export_client_caps(cap_map);
2768 bufferlist bl;
2769 MMDSResolve::peer_inode_cap inode_caps(in->ino(), cap_map);
2770 encode(inode_caps, bl);
2771 resolves[leader]->add_peer_request(p->first, bl);
2772 } else {
2773 resolves[leader]->add_peer_request(p->first, mdr->committing);
2774 }
2775 }
2776 }
2777 }
2778
2779 for (auto &p : resolves) {
2780 dout(10) << "sending peer resolve to mds." << p.first << dendl;
2781 mds->send_message_mds(p.second, p.first);
2782 resolve_ack_gather.insert(p.first);
2783 }
2784 }
2785
2786 void MDCache::send_subtree_resolves()
2787 {
2788 dout(10) << "send_subtree_resolves" << dendl;
2789
2790 if (migrator->is_exporting() || migrator->is_importing()) {
2791 dout(7) << "send_subtree_resolves waiting, imports/exports still in progress" << dendl;
2792 migrator->show_importing();
2793 migrator->show_exporting();
2794 resolves_pending = true;
2795 return; // not now
2796 }
2797
2798 map<mds_rank_t, ref_t<MMDSResolve>> resolves;
2799 for (set<mds_rank_t>::iterator p = recovery_set.begin();
2800 p != recovery_set.end();
2801 ++p) {
2802 if (*p == mds->get_nodeid())
2803 continue;
2804 if (mds->is_resolve() || mds->mdsmap->is_resolve(*p))
2805 resolves[*p] = make_message<MMDSResolve>();
2806 }
2807
2808 map<dirfrag_t, vector<dirfrag_t> > my_subtrees;
2809 map<dirfrag_t, vector<dirfrag_t> > my_ambig_imports;
2810
2811 // known
2812 for (map<CDir*,set<CDir*> >::iterator p = subtrees.begin();
2813 p != subtrees.end();
2814 ++p) {
2815 CDir *dir = p->first;
2816
2817 // only our subtrees
2818 if (dir->authority().first != mds->get_nodeid())
2819 continue;
2820
2821 if (mds->is_resolve() && my_ambiguous_imports.count(dir->dirfrag()))
2822 continue; // we'll add it below
2823
2824 if (migrator->is_ambiguous_import(dir->dirfrag())) {
2825 // ambiguous (mid-import)
2826 set<CDir*> bounds;
2827 get_subtree_bounds(dir, bounds);
2828 vector<dirfrag_t> dfls;
2829 for (set<CDir*>::iterator q = bounds.begin(); q != bounds.end(); ++q)
2830 dfls.push_back((*q)->dirfrag());
2831
2832 my_ambig_imports[dir->dirfrag()] = dfls;
2833 dout(10) << " ambig " << dir->dirfrag() << " " << dfls << dendl;
2834 } else {
2835 // not ambiguous.
2836 for (auto &q : resolves) {
2837 resolves[q.first]->add_subtree(dir->dirfrag());
2838 }
2839 // bounds too
2840 vector<dirfrag_t> dfls;
2841 for (set<CDir*>::iterator q = subtrees[dir].begin();
2842 q != subtrees[dir].end();
2843 ++q) {
2844 CDir *bound = *q;
2845 dfls.push_back(bound->dirfrag());
2846 }
2847
2848 my_subtrees[dir->dirfrag()] = dfls;
2849 dout(10) << " claim " << dir->dirfrag() << " " << dfls << dendl;
2850 }
2851 }
2852
2853 // ambiguous
2854 for (map<dirfrag_t, vector<dirfrag_t> >::iterator p = my_ambiguous_imports.begin();
2855 p != my_ambiguous_imports.end();
2856 ++p) {
2857 my_ambig_imports[p->first] = p->second;
2858 dout(10) << " ambig " << p->first << " " << p->second << dendl;
2859 }
2860
2861 // simplify the claimed subtree.
2862 for (auto p = my_subtrees.begin(); p != my_subtrees.end(); ++p) {
2863 unsigned i = 0;
2864 while (i < p->second.size()) {
2865 dirfrag_t b = p->second[i];
2866 if (my_subtrees.count(b)) {
2867 vector<dirfrag_t>& bb = my_subtrees[b];
2868 dout(10) << " simplify: " << p->first << " swallowing " << b << " with bounds " << bb << dendl;
2869 for (vector<dirfrag_t>::iterator r = bb.begin(); r != bb.end(); ++r)
2870 p->second.push_back(*r);
2871 my_subtrees.erase(b);
2872 p->second.erase(p->second.begin() + i);
2873 } else {
2874 ++i;
2875 }
2876 }
2877 }
2878
2879 // send
2880 for (auto &p : resolves) {
2881 const ref_t<MMDSResolve> &m = p.second;
2882 if (mds->is_resolve()) {
2883 m->add_table_commits(TABLE_SNAP, resolve_snapclient_commits);
2884 } else {
2885 m->add_table_commits(TABLE_SNAP, mds->snapclient->get_journaled_tids());
2886 }
2887 m->subtrees = my_subtrees;
2888 m->ambiguous_imports = my_ambig_imports;
2889 dout(10) << "sending subtee resolve to mds." << p.first << dendl;
2890 mds->send_message_mds(m, p.first);
2891 }
2892 resolves_pending = false;
2893 }
2894
2895 void MDCache::maybe_finish_peer_resolve() {
2896 if (resolve_ack_gather.empty() && resolve_need_rollback.empty()) {
2897 // snap cache get synced or I'm in resolve state
2898 if (mds->snapclient->is_synced() || resolve_done)
2899 send_subtree_resolves();
2900 process_delayed_resolve();
2901 }
2902 }
2903
2904 void MDCache::handle_mds_failure(mds_rank_t who)
2905 {
2906 dout(7) << "handle_mds_failure mds." << who << dendl;
2907
2908 dout(1) << "handle_mds_failure mds." << who << " : recovery peers are " << recovery_set << dendl;
2909
2910 resolve_gather.insert(who);
2911 discard_delayed_resolve(who);
2912 ambiguous_peer_updates.erase(who);
2913
2914 rejoin_gather.insert(who);
2915 rejoin_sent.erase(who); // i need to send another
2916 rejoin_ack_sent.erase(who); // i need to send another
2917 rejoin_ack_gather.erase(who); // i'll need/get another.
2918
2919 dout(10) << " resolve_gather " << resolve_gather << dendl;
2920 dout(10) << " resolve_ack_gather " << resolve_ack_gather << dendl;
2921 dout(10) << " rejoin_sent " << rejoin_sent << dendl;
2922 dout(10) << " rejoin_gather " << rejoin_gather << dendl;
2923 dout(10) << " rejoin_ack_gather " << rejoin_ack_gather << dendl;
2924
2925
2926 // tell the migrator too.
2927 migrator->handle_mds_failure_or_stop(who);
2928
2929 // tell the balancer too.
2930 mds->balancer->handle_mds_failure(who);
2931
2932 // clean up any requests peer to/from this node
2933 list<MDRequestRef> finish;
2934 for (ceph::unordered_map<metareqid_t, MDRequestRef>::iterator p = active_requests.begin();
2935 p != active_requests.end();
2936 ++p) {
2937 MDRequestRef& mdr = p->second;
2938 // peer to the failed node?
2939 if (mdr->peer_to_mds == who) {
2940 if (mdr->peer_did_prepare()) {
2941 dout(10) << " peer request " << *mdr << " uncommitted, will resolve shortly" << dendl;
2942 if (is_ambiguous_peer_update(p->first, mdr->peer_to_mds))
2943 remove_ambiguous_peer_update(p->first, mdr->peer_to_mds);
2944
2945 if (!mdr->more()->waiting_on_peer.empty()) {
2946 ceph_assert(mdr->more()->srcdn_auth_mds == mds->get_nodeid());
2947 // will rollback, no need to wait
2948 mdr->reset_peer_request();
2949 mdr->more()->waiting_on_peer.clear();
2950 }
2951 } else if (!mdr->committing) {
2952 dout(10) << " peer request " << *mdr << " has no prepare, finishing up" << dendl;
2953 if (mdr->peer_request || mdr->peer_rolling_back())
2954 mdr->aborted = true;
2955 else
2956 finish.push_back(mdr);
2957 }
2958 }
2959
2960 if (mdr->is_peer() && mdr->peer_did_prepare()) {
2961 if (mdr->more()->waiting_on_peer.count(who)) {
2962 ceph_assert(mdr->more()->srcdn_auth_mds == mds->get_nodeid());
2963 dout(10) << " peer request " << *mdr << " no longer need rename notity ack from mds."
2964 << who << dendl;
2965 mdr->more()->waiting_on_peer.erase(who);
2966 if (mdr->more()->waiting_on_peer.empty() && mdr->peer_request)
2967 mds->queue_waiter(new C_MDS_RetryRequest(this, mdr));
2968 }
2969
2970 if (mdr->more()->srcdn_auth_mds == who &&
2971 mds->mdsmap->is_clientreplay_or_active_or_stopping(mdr->peer_to_mds)) {
2972 // rename srcdn's auth mds failed, resolve even I'm a survivor.
2973 dout(10) << " peer request " << *mdr << " uncommitted, will resolve shortly" << dendl;
2974 add_ambiguous_peer_update(p->first, mdr->peer_to_mds);
2975 }
2976 } else if (mdr->peer_request) {
2977 const cref_t<MMDSPeerRequest> &peer_req = mdr->peer_request;
2978 // FIXME: Peer rename request can arrive after we notice mds failure.
2979 // This can cause mds to crash (does not affect integrity of FS).
2980 if (peer_req->get_op() == MMDSPeerRequest::OP_RENAMEPREP &&
2981 peer_req->srcdn_auth == who)
2982 peer_req->mark_interrupted();
2983 }
2984
2985 // failed node is peer?
2986 if (mdr->is_leader() && !mdr->committing) {
2987 if (mdr->more()->srcdn_auth_mds == who) {
2988 dout(10) << " leader request " << *mdr << " waiting for rename srcdn's auth mds."
2989 << who << " to recover" << dendl;
2990 ceph_assert(mdr->more()->witnessed.count(who) == 0);
2991 if (mdr->more()->is_ambiguous_auth)
2992 mdr->clear_ambiguous_auth();
2993 // rename srcdn's auth mds failed, all witnesses will rollback
2994 mdr->more()->witnessed.clear();
2995 pending_leaders.erase(p->first);
2996 }
2997
2998 if (mdr->more()->witnessed.count(who)) {
2999 mds_rank_t srcdn_auth = mdr->more()->srcdn_auth_mds;
3000 if (srcdn_auth >= 0 && mdr->more()->waiting_on_peer.count(srcdn_auth)) {
3001 dout(10) << " leader request " << *mdr << " waiting for rename srcdn's auth mds."
3002 << mdr->more()->srcdn_auth_mds << " to reply" << dendl;
3003 // waiting for the peer (rename srcdn's auth mds), delay sending resolve ack
3004 // until either the request is committing or the peer also fails.
3005 ceph_assert(mdr->more()->waiting_on_peer.size() == 1);
3006 pending_leaders.insert(p->first);
3007 } else {
3008 dout(10) << " leader request " << *mdr << " no longer witnessed by peer mds."
3009 << who << " to recover" << dendl;
3010 if (srcdn_auth >= 0)
3011 ceph_assert(mdr->more()->witnessed.count(srcdn_auth) == 0);
3012
3013 // discard this peer's prepare (if any)
3014 mdr->more()->witnessed.erase(who);
3015 }
3016 }
3017
3018 if (mdr->more()->waiting_on_peer.count(who)) {
3019 dout(10) << " leader request " << *mdr << " waiting for peer mds." << who
3020 << " to recover" << dendl;
3021 // retry request when peer recovers
3022 mdr->more()->waiting_on_peer.erase(who);
3023 if (mdr->more()->waiting_on_peer.empty())
3024 mds->wait_for_active_peer(who, new C_MDS_RetryRequest(this, mdr));
3025 }
3026
3027 if (mdr->locking && mdr->locking_target_mds == who)
3028 mdr->finish_locking(mdr->locking);
3029 }
3030 }
3031
3032 for (map<metareqid_t, uleader>::iterator p = uncommitted_leaders.begin();
3033 p != uncommitted_leaders.end();
3034 ++p) {
3035 // The failed MDS may have already committed the peer update
3036 if (p->second.peers.count(who)) {
3037 p->second.recovering = true;
3038 p->second.peers.erase(who);
3039 }
3040 }
3041
3042 while (!finish.empty()) {
3043 dout(10) << "cleaning up peer request " << *finish.front() << dendl;
3044 request_finish(finish.front());
3045 finish.pop_front();
3046 }
3047
3048 kick_find_ino_peers(who);
3049 kick_open_ino_peers(who);
3050
3051 for (map<dirfrag_t,fragment_info_t>::iterator p = fragments.begin();
3052 p != fragments.end(); ) {
3053 dirfrag_t df = p->first;
3054 fragment_info_t& info = p->second;
3055
3056 if (info.is_fragmenting()) {
3057 if (info.notify_ack_waiting.erase(who) &&
3058 info.notify_ack_waiting.empty()) {
3059 fragment_drop_locks(info);
3060 fragment_maybe_finish(p++);
3061 } else {
3062 ++p;
3063 }
3064 continue;
3065 }
3066
3067 ++p;
3068 dout(10) << "cancelling fragment " << df << " bit " << info.bits << dendl;
3069 std::vector<CDir*> dirs;
3070 info.dirs.swap(dirs);
3071 fragments.erase(df);
3072 fragment_unmark_unfreeze_dirs(dirs);
3073 }
3074
3075 // MDCache::shutdown_export_strays() always exports strays to mds.0
3076 if (who == mds_rank_t(0))
3077 shutdown_exporting_strays.clear();
3078
3079 show_subtrees();
3080 }
3081
3082 /*
3083 * handle_mds_recovery - called on another node's transition
3084 * from resolve -> active.
3085 */
3086 void MDCache::handle_mds_recovery(mds_rank_t who)
3087 {
3088 dout(7) << "handle_mds_recovery mds." << who << dendl;
3089
3090 // exclude all discover waiters. kick_discovers() will do the job
3091 static const uint64_t i_mask = CInode::WAIT_ANY_MASK & ~CInode::WAIT_DIR;
3092 static const uint64_t d_mask = CDir::WAIT_ANY_MASK & ~CDir::WAIT_DENTRY;
3093
3094 MDSContext::vec waiters;
3095
3096 // wake up any waiters in their subtrees
3097 for (map<CDir*,set<CDir*> >::iterator p = subtrees.begin();
3098 p != subtrees.end();
3099 ++p) {
3100 CDir *dir = p->first;
3101
3102 if (dir->authority().first != who ||
3103 dir->authority().second == mds->get_nodeid())
3104 continue;
3105 ceph_assert(!dir->is_auth());
3106
3107 // wake any waiters
3108 std::queue<CDir*> q;
3109 q.push(dir);
3110
3111 while (!q.empty()) {
3112 CDir *d = q.front();
3113 q.pop();
3114 d->take_waiting(d_mask, waiters);
3115
3116 // inode waiters too
3117 for (auto &p : d->items) {
3118 CDentry *dn = p.second;
3119 CDentry::linkage_t *dnl = dn->get_linkage();
3120 if (dnl->is_primary()) {
3121 dnl->get_inode()->take_waiting(i_mask, waiters);
3122
3123 // recurse?
3124 auto&& ls = dnl->get_inode()->get_dirfrags();
3125 for (const auto& subdir : ls) {
3126 if (!subdir->is_subtree_root())
3127 q.push(subdir);
3128 }
3129 }
3130 }
3131 }
3132 }
3133
3134 kick_open_ino_peers(who);
3135 kick_find_ino_peers(who);
3136
3137 // queue them up.
3138 mds->queue_waiters(waiters);
3139 }
3140
3141 void MDCache::set_recovery_set(set<mds_rank_t>& s)
3142 {
3143 dout(7) << "set_recovery_set " << s << dendl;
3144 recovery_set = s;
3145 }
3146
3147
3148 /*
3149 * during resolve state, we share resolves to determine who
3150 * is authoritative for which trees. we expect to get an resolve
3151 * from _everyone_ in the recovery_set (the mds cluster at the time of
3152 * the first failure).
3153 *
3154 * This functions puts the passed message before returning
3155 */
3156 void MDCache::handle_resolve(const cref_t<MMDSResolve> &m)
3157 {
3158 dout(7) << "handle_resolve from " << m->get_source() << dendl;
3159 mds_rank_t from = mds_rank_t(m->get_source().num());
3160
3161 if (mds->get_state() < MDSMap::STATE_RESOLVE) {
3162 if (mds->get_want_state() == CEPH_MDS_STATE_RESOLVE) {
3163 mds->wait_for_resolve(new C_MDS_RetryMessage(mds, m));
3164 return;
3165 }
3166 // wait until we reach the resolve stage!
3167 return;
3168 }
3169
3170 discard_delayed_resolve(from);
3171
3172 // ambiguous peer requests?
3173 if (!m->peer_requests.empty()) {
3174 if (mds->is_clientreplay() || mds->is_active() || mds->is_stopping()) {
3175 for (auto p = m->peer_requests.begin(); p != m->peer_requests.end(); ++p) {
3176 if (uncommitted_leaders.count(p->first) && !uncommitted_leaders[p->first].safe) {
3177 ceph_assert(!p->second.committing);
3178 pending_leaders.insert(p->first);
3179 }
3180 }
3181
3182 if (!pending_leaders.empty()) {
3183 dout(10) << " still have pending updates, delay processing peer resolve" << dendl;
3184 delayed_resolve[from] = m;
3185 return;
3186 }
3187 }
3188
3189 auto ack = make_message<MMDSResolveAck>();
3190 for (const auto &p : m->peer_requests) {
3191 if (uncommitted_leaders.count(p.first)) { //mds->sessionmap.have_completed_request(p.first)) {
3192 // COMMIT
3193 if (p.second.committing) {
3194 // already committing, waiting for the OP_COMMITTED peer reply
3195 dout(10) << " already committing peer request " << p << " noop "<< dendl;
3196 } else {
3197 dout(10) << " ambiguous peer request " << p << " will COMMIT" << dendl;
3198 ack->add_commit(p.first);
3199 }
3200 uncommitted_leaders[p.first].peers.insert(from); // wait for peer OP_COMMITTED before we log ECommitted
3201
3202 if (p.second.inode_caps.length() > 0) {
3203 // peer wants to export caps (rename)
3204 ceph_assert(mds->is_resolve());
3205 MMDSResolve::peer_inode_cap inode_caps;
3206 auto q = p.second.inode_caps.cbegin();
3207 decode(inode_caps, q);
3208 inodeno_t ino = inode_caps.ino;
3209 map<client_t,Capability::Export> cap_exports = inode_caps.cap_exports;
3210 ceph_assert(get_inode(ino));
3211
3212 for (map<client_t,Capability::Export>::iterator q = cap_exports.begin();
3213 q != cap_exports.end();
3214 ++q) {
3215 Capability::Import& im = rejoin_imported_caps[from][ino][q->first];
3216 im.cap_id = ++last_cap_id; // assign a new cap ID
3217 im.issue_seq = 1;
3218 im.mseq = q->second.mseq;
3219
3220 Session *session = mds->sessionmap.get_session(entity_name_t::CLIENT(q->first.v));
3221 if (session)
3222 rejoin_client_map.emplace(q->first, session->info.inst);
3223 }
3224
3225 // will process these caps in rejoin stage
3226 rejoin_peer_exports[ino].first = from;
3227 rejoin_peer_exports[ino].second.swap(cap_exports);
3228
3229 // send information of imported caps back to peer
3230 encode(rejoin_imported_caps[from][ino], ack->commit[p.first]);
3231 }
3232 } else {
3233 // ABORT
3234 dout(10) << " ambiguous peer request " << p << " will ABORT" << dendl;
3235 ceph_assert(!p.second.committing);
3236 ack->add_abort(p.first);
3237 }
3238 }
3239 mds->send_message(ack, m->get_connection());
3240 return;
3241 }
3242
3243 if (!resolve_ack_gather.empty() || !resolve_need_rollback.empty()) {
3244 dout(10) << "delay processing subtree resolve" << dendl;
3245 delayed_resolve[from] = m;
3246 return;
3247 }
3248
3249 bool survivor = false;
3250 // am i a surviving ambiguous importer?
3251 if (mds->is_clientreplay() || mds->is_active() || mds->is_stopping()) {
3252 survivor = true;
3253 // check for any import success/failure (from this node)
3254 map<dirfrag_t, vector<dirfrag_t> >::iterator p = my_ambiguous_imports.begin();
3255 while (p != my_ambiguous_imports.end()) {
3256 map<dirfrag_t, vector<dirfrag_t> >::iterator next = p;
3257 ++next;
3258 CDir *dir = get_dirfrag(p->first);
3259 ceph_assert(dir);
3260 dout(10) << "checking ambiguous import " << *dir << dendl;
3261 if (migrator->is_importing(dir->dirfrag()) &&
3262 migrator->get_import_peer(dir->dirfrag()) == from) {
3263 ceph_assert(migrator->get_import_state(dir->dirfrag()) == Migrator::IMPORT_ACKING);
3264
3265 // check if sender claims the subtree
3266 bool claimed_by_sender = false;
3267 for (const auto &q : m->subtrees) {
3268 // an ambiguous import won't race with a refragmentation; it's appropriate to force here.
3269 CDir *base = get_force_dirfrag(q.first, false);
3270 if (!base || !base->contains(dir))
3271 continue; // base not dir or an ancestor of dir, clearly doesn't claim dir.
3272
3273 bool inside = true;
3274 set<CDir*> bounds;
3275 get_force_dirfrag_bound_set(q.second, bounds);
3276 for (set<CDir*>::iterator p = bounds.begin(); p != bounds.end(); ++p) {
3277 CDir *bound = *p;
3278 if (bound->contains(dir)) {
3279 inside = false; // nope, bound is dir or parent of dir, not inside.
3280 break;
3281 }
3282 }
3283 if (inside)
3284 claimed_by_sender = true;
3285 }
3286
3287 my_ambiguous_imports.erase(p); // no longer ambiguous.
3288 if (claimed_by_sender) {
3289 dout(7) << "ambiguous import failed on " << *dir << dendl;
3290 migrator->import_reverse(dir);
3291 } else {
3292 dout(7) << "ambiguous import succeeded on " << *dir << dendl;
3293 migrator->import_finish(dir, true);
3294 }
3295 }
3296 p = next;
3297 }
3298 }
3299
3300 // update my dir_auth values
3301 // need to do this on recoverying nodes _and_ bystanders (to resolve ambiguous
3302 // migrations between other nodes)
3303 for (const auto& p : m->subtrees) {
3304 dout(10) << "peer claims " << p.first << " bounds " << p.second << dendl;
3305 CDir *dir = get_force_dirfrag(p.first, !survivor);
3306 if (!dir)
3307 continue;
3308 adjust_bounded_subtree_auth(dir, p.second, from);
3309 try_subtree_merge(dir);
3310 }
3311
3312 show_subtrees();
3313
3314 // note ambiguous imports too
3315 for (const auto& p : m->ambiguous_imports) {
3316 dout(10) << "noting ambiguous import on " << p.first << " bounds " << p.second << dendl;
3317 other_ambiguous_imports[from][p.first] = p.second;
3318 }
3319
3320 // learn other mds' pendina snaptable commits. later when resolve finishes, we will reload
3321 // snaptable cache from snapserver. By this way, snaptable cache get synced among all mds
3322 for (const auto& p : m->table_clients) {
3323 dout(10) << " noting " << get_mdstable_name(p.type)
3324 << " pending_commits " << p.pending_commits << dendl;
3325 MDSTableClient *client = mds->get_table_client(p.type);
3326 for (const auto& q : p.pending_commits)
3327 client->notify_commit(q);
3328 }
3329
3330 // did i get them all?
3331 resolve_gather.erase(from);
3332
3333 maybe_resolve_finish();
3334 }
3335
3336 void MDCache::process_delayed_resolve()
3337 {
3338 dout(10) << "process_delayed_resolve" << dendl;
3339 map<mds_rank_t, cref_t<MMDSResolve>> tmp;
3340 tmp.swap(delayed_resolve);
3341 for (auto &p : tmp) {
3342 handle_resolve(p.second);
3343 }
3344 }
3345
3346 void MDCache::discard_delayed_resolve(mds_rank_t who)
3347 {
3348 delayed_resolve.erase(who);
3349 }
3350
3351 void MDCache::maybe_resolve_finish()
3352 {
3353 ceph_assert(resolve_ack_gather.empty());
3354 ceph_assert(resolve_need_rollback.empty());
3355
3356 if (!resolve_gather.empty()) {
3357 dout(10) << "maybe_resolve_finish still waiting for resolves ("
3358 << resolve_gather << ")" << dendl;
3359 return;
3360 }
3361
3362 dout(10) << "maybe_resolve_finish got all resolves+resolve_acks, done." << dendl;
3363 disambiguate_my_imports();
3364 finish_committed_leaders();
3365
3366 if (resolve_done) {
3367 ceph_assert(mds->is_resolve());
3368 trim_unlinked_inodes();
3369 recalc_auth_bits(false);
3370 resolve_done.release()->complete(0);
3371 } else {
3372 // I am survivor.
3373 maybe_send_pending_rejoins();
3374 }
3375 }
3376
3377 void MDCache::handle_resolve_ack(const cref_t<MMDSResolveAck> &ack)
3378 {
3379 dout(10) << "handle_resolve_ack " << *ack << " from " << ack->get_source() << dendl;
3380 mds_rank_t from = mds_rank_t(ack->get_source().num());
3381
3382 if (!resolve_ack_gather.count(from) ||
3383 mds->mdsmap->get_state(from) < MDSMap::STATE_RESOLVE) {
3384 return;
3385 }
3386
3387 if (ambiguous_peer_updates.count(from)) {
3388 ceph_assert(mds->mdsmap->is_clientreplay_or_active_or_stopping(from));
3389 ceph_assert(mds->is_clientreplay() || mds->is_active() || mds->is_stopping());
3390 }
3391
3392 for (const auto &p : ack->commit) {
3393 dout(10) << " commit on peer " << p.first << dendl;
3394
3395 if (ambiguous_peer_updates.count(from)) {
3396 remove_ambiguous_peer_update(p.first, from);
3397 continue;
3398 }
3399
3400 if (mds->is_resolve()) {
3401 // replay
3402 MDPeerUpdate *su = get_uncommitted_peer(p.first, from);
3403 ceph_assert(su);
3404
3405 // log commit
3406 mds->mdlog->start_submit_entry(new EPeerUpdate(mds->mdlog, "unknown", p.first, from,
3407 EPeerUpdate::OP_COMMIT, su->origop),
3408 new C_MDC_PeerCommit(this, from, p.first));
3409 mds->mdlog->flush();
3410
3411 finish_uncommitted_peer(p.first);
3412 } else {
3413 MDRequestRef mdr = request_get(p.first);
3414 // information about leader imported caps
3415 if (p.second.length() > 0)
3416 mdr->more()->inode_import.share(p.second);
3417
3418 ceph_assert(mdr->peer_request == 0); // shouldn't be doing anything!
3419 request_finish(mdr);
3420 }
3421 }
3422
3423 for (const auto &metareq : ack->abort) {
3424 dout(10) << " abort on peer " << metareq << dendl;
3425
3426 if (mds->is_resolve()) {
3427 MDPeerUpdate *su = get_uncommitted_peer(metareq, from);
3428 ceph_assert(su);
3429
3430 // perform rollback (and journal a rollback entry)
3431 // note: this will hold up the resolve a bit, until the rollback entries journal.
3432 MDRequestRef null_ref;
3433 switch (su->origop) {
3434 case EPeerUpdate::LINK:
3435 mds->server->do_link_rollback(su->rollback, from, null_ref);
3436 break;
3437 case EPeerUpdate::RENAME:
3438 mds->server->do_rename_rollback(su->rollback, from, null_ref);
3439 break;
3440 case EPeerUpdate::RMDIR:
3441 mds->server->do_rmdir_rollback(su->rollback, from, null_ref);
3442 break;
3443 default:
3444 ceph_abort();
3445 }
3446 } else {
3447 MDRequestRef mdr = request_get(metareq);
3448 mdr->aborted = true;
3449 if (mdr->peer_request) {
3450 if (mdr->peer_did_prepare()) // journaling peer prepare ?
3451 add_rollback(metareq, from);
3452 } else {
3453 request_finish(mdr);
3454 }
3455 }
3456 }
3457
3458 if (!ambiguous_peer_updates.count(from)) {
3459 resolve_ack_gather.erase(from);
3460 maybe_finish_peer_resolve();
3461 }
3462 }
3463
3464 void MDCache::add_uncommitted_peer(metareqid_t reqid, LogSegment *ls, mds_rank_t leader, MDPeerUpdate *su)
3465 {
3466 auto const &ret = uncommitted_peers.emplace(std::piecewise_construct,
3467 std::forward_as_tuple(reqid),
3468 std::forward_as_tuple());
3469 ceph_assert(ret.second);
3470 ls->uncommitted_peers.insert(reqid);
3471 upeer &u = ret.first->second;
3472 u.leader = leader;
3473 u.ls = ls;
3474 u.su = su;
3475 if (su == nullptr) {
3476 return;
3477 }
3478 for(set<CInode*>::iterator p = su->olddirs.begin(); p != su->olddirs.end(); ++p)
3479 uncommitted_peer_rename_olddir[*p]++;
3480 for(set<CInode*>::iterator p = su->unlinked.begin(); p != su->unlinked.end(); ++p)
3481 uncommitted_peer_unlink[*p]++;
3482 }
3483
3484 void MDCache::finish_uncommitted_peer(metareqid_t reqid, bool assert_exist)
3485 {
3486 auto it = uncommitted_peers.find(reqid);
3487 if (it == uncommitted_peers.end()) {
3488 ceph_assert(!assert_exist);
3489 return;
3490 }
3491 upeer &u = it->second;
3492 MDPeerUpdate* su = u.su;
3493
3494 if (!u.waiters.empty()) {
3495 mds->queue_waiters(u.waiters);
3496 }
3497 u.ls->uncommitted_peers.erase(reqid);
3498 uncommitted_peers.erase(it);
3499
3500 if (su == nullptr) {
3501 return;
3502 }
3503 // discard the non-auth subtree we renamed out of
3504 for(set<CInode*>::iterator p = su->olddirs.begin(); p != su->olddirs.end(); ++p) {
3505 CInode *diri = *p;
3506 map<CInode*, int>::iterator it = uncommitted_peer_rename_olddir.find(diri);
3507 ceph_assert(it != uncommitted_peer_rename_olddir.end());
3508 it->second--;
3509 if (it->second == 0) {
3510 uncommitted_peer_rename_olddir.erase(it);
3511 auto&& ls = diri->get_dirfrags();
3512 for (const auto& dir : ls) {
3513 CDir *root = get_subtree_root(dir);
3514 if (root->get_dir_auth() == CDIR_AUTH_UNDEF) {
3515 try_trim_non_auth_subtree(root);
3516 if (dir != root)
3517 break;
3518 }
3519 }
3520 } else
3521 ceph_assert(it->second > 0);
3522 }
3523 // removed the inodes that were unlinked by peer update
3524 for(set<CInode*>::iterator p = su->unlinked.begin(); p != su->unlinked.end(); ++p) {
3525 CInode *in = *p;
3526 map<CInode*, int>::iterator it = uncommitted_peer_unlink.find(in);
3527 ceph_assert(it != uncommitted_peer_unlink.end());
3528 it->second--;
3529 if (it->second == 0) {
3530 uncommitted_peer_unlink.erase(it);
3531 if (!in->get_projected_parent_dn())
3532 mds->mdcache->remove_inode_recursive(in);
3533 } else
3534 ceph_assert(it->second > 0);
3535 }
3536 delete su;
3537 }
3538
3539 MDPeerUpdate* MDCache::get_uncommitted_peer(metareqid_t reqid, mds_rank_t leader)
3540 {
3541
3542 MDPeerUpdate* su = nullptr;
3543 auto it = uncommitted_peers.find(reqid);
3544 if (it != uncommitted_peers.end() &&
3545 it->second.leader == leader) {
3546 su = it->second.su;
3547 }
3548 return su;
3549 }
3550
3551 void MDCache::finish_rollback(metareqid_t reqid, MDRequestRef& mdr) {
3552 auto p = resolve_need_rollback.find(reqid);
3553 ceph_assert(p != resolve_need_rollback.end());
3554 if (mds->is_resolve()) {
3555 finish_uncommitted_peer(reqid, false);
3556 } else if (mdr) {
3557 finish_uncommitted_peer(mdr->reqid, mdr->more()->peer_update_journaled);
3558 }
3559 resolve_need_rollback.erase(p);
3560 maybe_finish_peer_resolve();
3561 }
3562
3563 void MDCache::disambiguate_other_imports()
3564 {
3565 dout(10) << "disambiguate_other_imports" << dendl;
3566
3567 bool recovering = !(mds->is_clientreplay() || mds->is_active() || mds->is_stopping());
3568 // other nodes' ambiguous imports
3569 for (map<mds_rank_t, map<dirfrag_t, vector<dirfrag_t> > >::iterator p = other_ambiguous_imports.begin();
3570 p != other_ambiguous_imports.end();
3571 ++p) {
3572 mds_rank_t who = p->first;
3573 dout(10) << "ambiguous imports for mds." << who << dendl;
3574
3575 for (map<dirfrag_t, vector<dirfrag_t> >::iterator q = p->second.begin();
3576 q != p->second.end();
3577 ++q) {
3578 dout(10) << " ambiguous import " << q->first << " bounds " << q->second << dendl;
3579 // an ambiguous import will not race with a refragmentation; it's appropriate to force here.
3580 CDir *dir = get_force_dirfrag(q->first, recovering);
3581 if (!dir) continue;
3582
3583 if (dir->is_ambiguous_auth() || // works for me_ambig or if i am a surviving bystander
3584 dir->authority() == CDIR_AUTH_UNDEF) { // resolving
3585 dout(10) << " mds." << who << " did import " << *dir << dendl;
3586 adjust_bounded_subtree_auth(dir, q->second, who);
3587 try_subtree_merge(dir);
3588 } else {
3589 dout(10) << " mds." << who << " did not import " << *dir << dendl;
3590 }
3591 }
3592 }
3593 other_ambiguous_imports.clear();
3594 }
3595
3596 void MDCache::disambiguate_my_imports()
3597 {
3598 dout(10) << "disambiguate_my_imports" << dendl;
3599
3600 if (!mds->is_resolve()) {
3601 ceph_assert(my_ambiguous_imports.empty());
3602 return;
3603 }
3604
3605 disambiguate_other_imports();
3606
3607 // my ambiguous imports
3608 mds_authority_t me_ambig(mds->get_nodeid(), mds->get_nodeid());
3609 while (!my_ambiguous_imports.empty()) {
3610 map<dirfrag_t, vector<dirfrag_t> >::iterator q = my_ambiguous_imports.begin();
3611
3612 CDir *dir = get_dirfrag(q->first);
3613 ceph_assert(dir);
3614
3615 if (dir->authority() != me_ambig) {
3616 dout(10) << "ambiguous import auth known, must not be me " << *dir << dendl;
3617 cancel_ambiguous_import(dir);
3618
3619 mds->mdlog->start_submit_entry(new EImportFinish(dir, false));
3620
3621 // subtree may have been swallowed by another node claiming dir
3622 // as their own.
3623 CDir *root = get_subtree_root(dir);
3624 if (root != dir)
3625 dout(10) << " subtree root is " << *root << dendl;
3626 ceph_assert(root->dir_auth.first != mds->get_nodeid()); // no us!
3627 try_trim_non_auth_subtree(root);
3628 } else {
3629 dout(10) << "ambiguous import auth unclaimed, must be me " << *dir << dendl;
3630 finish_ambiguous_import(q->first);
3631 mds->mdlog->start_submit_entry(new EImportFinish(dir, true));
3632 }
3633 }
3634 ceph_assert(my_ambiguous_imports.empty());
3635 mds->mdlog->flush();
3636
3637 // verify all my subtrees are unambiguous!
3638 for (map<CDir*,set<CDir*> >::iterator p = subtrees.begin();
3639 p != subtrees.end();
3640 ++p) {
3641 CDir *dir = p->first;
3642 if (dir->is_ambiguous_dir_auth()) {
3643 dout(0) << "disambiguate_imports uh oh, dir_auth is still ambiguous for " << *dir << dendl;
3644 }
3645 ceph_assert(!dir->is_ambiguous_dir_auth());
3646 }
3647
3648 show_subtrees();
3649 }
3650
3651
3652 void MDCache::add_ambiguous_import(dirfrag_t base, const vector<dirfrag_t>& bounds)
3653 {
3654 ceph_assert(my_ambiguous_imports.count(base) == 0);
3655 my_ambiguous_imports[base] = bounds;
3656 }
3657
3658
3659 void MDCache::add_ambiguous_import(CDir *base, const set<CDir*>& bounds)
3660 {
3661 // make a list
3662 vector<dirfrag_t> binos;
3663 for (set<CDir*>::iterator p = bounds.begin();
3664 p != bounds.end();
3665 ++p)
3666 binos.push_back((*p)->dirfrag());
3667
3668 // note: this can get called twice if the exporter fails during recovery
3669 if (my_ambiguous_imports.count(base->dirfrag()))
3670 my_ambiguous_imports.erase(base->dirfrag());
3671
3672 add_ambiguous_import(base->dirfrag(), binos);
3673 }
3674
3675 void MDCache::cancel_ambiguous_import(CDir *dir)
3676 {
3677 dirfrag_t df = dir->dirfrag();
3678 ceph_assert(my_ambiguous_imports.count(df));
3679 dout(10) << "cancel_ambiguous_import " << df
3680 << " bounds " << my_ambiguous_imports[df]
3681 << " " << *dir
3682 << dendl;
3683 my_ambiguous_imports.erase(df);
3684 }
3685
3686 void MDCache::finish_ambiguous_import(dirfrag_t df)
3687 {
3688 ceph_assert(my_ambiguous_imports.count(df));
3689 vector<dirfrag_t> bounds;
3690 bounds.swap(my_ambiguous_imports[df]);
3691 my_ambiguous_imports.erase(df);
3692
3693 dout(10) << "finish_ambiguous_import " << df
3694 << " bounds " << bounds
3695 << dendl;
3696 CDir *dir = get_dirfrag(df);
3697 ceph_assert(dir);
3698
3699 // adjust dir_auth, import maps
3700 adjust_bounded_subtree_auth(dir, bounds, mds->get_nodeid());
3701 try_subtree_merge(dir);
3702 }
3703
3704 void MDCache::remove_inode_recursive(CInode *in)
3705 {
3706 dout(10) << "remove_inode_recursive " << *in << dendl;
3707 auto&& ls = in->get_dirfrags();
3708 for (const auto& subdir : ls) {
3709 dout(10) << " removing dirfrag " << *subdir << dendl;
3710 auto it = subdir->items.begin();
3711 while (it != subdir->items.end()) {
3712 CDentry *dn = it->second;
3713 ++it;
3714 CDentry::linkage_t *dnl = dn->get_linkage();
3715 if (dnl->is_primary()) {
3716 CInode *tin = dnl->get_inode();
3717 subdir->unlink_inode(dn, false);
3718 remove_inode_recursive(tin);
3719 }
3720 subdir->remove_dentry(dn);
3721 }
3722
3723 if (subdir->is_subtree_root())
3724 remove_subtree(subdir);
3725 in->close_dirfrag(subdir->dirfrag().frag);
3726 }
3727 remove_inode(in);
3728 }
3729
3730 bool MDCache::expire_recursive(CInode *in, expiremap &expiremap)
3731 {
3732 ceph_assert(!in->is_auth());
3733
3734 dout(10) << __func__ << ":" << *in << dendl;
3735
3736 // Recurse into any dirfrags beneath this inode
3737 auto&& ls = in->get_dirfrags();
3738 for (const auto& subdir : ls) {
3739 if (!in->is_mdsdir() && subdir->is_subtree_root()) {
3740 dout(10) << __func__ << ": stray still has subtree " << *in << dendl;
3741 return true;
3742 }
3743
3744 for (auto it = subdir->items.begin(); it != subdir->items.end();) {
3745 CDentry *dn = it->second;
3746 it++;
3747 CDentry::linkage_t *dnl = dn->get_linkage();
3748 if (dnl->is_primary()) {
3749 CInode *tin = dnl->get_inode();
3750
3751 /* Remote strays with linkage (i.e. hardlinks) should not be
3752 * expired, because they may be the target of
3753 * a rename() as the owning MDS shuts down */
3754 if (!tin->is_stray() && tin->get_inode()->nlink) {
3755 dout(10) << __func__ << ": stray still has linkage " << *tin << dendl;
3756 return true;
3757 }
3758
3759 const bool abort = expire_recursive(tin, expiremap);
3760 if (abort) {
3761 return true;
3762 }
3763 }
3764 if (dn->lru_is_expireable()) {
3765 trim_dentry(dn, expiremap);
3766 } else {
3767 dout(10) << __func__ << ": stray dn is not expireable " << *dn << dendl;
3768 return true;
3769 }
3770 }
3771 }
3772
3773 return false;
3774 }
3775
3776 void MDCache::trim_unlinked_inodes()
3777 {
3778 dout(7) << "trim_unlinked_inodes" << dendl;
3779 int count = 0;
3780 vector<CInode*> q;
3781 for (auto &p : inode_map) {
3782 CInode *in = p.second;
3783 if (in->get_parent_dn() == NULL && !in->is_base()) {
3784 dout(7) << " will trim from " << *in << dendl;
3785 q.push_back(in);
3786 }
3787
3788 if (!(++count % mds->heartbeat_reset_grace()))
3789 mds->heartbeat_reset();
3790 }
3791 for (auto& in : q) {
3792 remove_inode_recursive(in);
3793
3794 if (!(++count % mds->heartbeat_reset_grace()))
3795 mds->heartbeat_reset();
3796 }
3797 }
3798
3799 /** recalc_auth_bits()
3800 * once subtree auth is disambiguated, we need to adjust all the
3801 * auth and dirty bits in our cache before moving on.
3802 */
3803 void MDCache::recalc_auth_bits(bool replay)
3804 {
3805 dout(7) << "recalc_auth_bits " << (replay ? "(replay)" : "") << dendl;
3806
3807 if (root) {
3808 root->inode_auth.first = mds->mdsmap->get_root();
3809 bool auth = mds->get_nodeid() == root->inode_auth.first;
3810 if (auth) {
3811 root->state_set(CInode::STATE_AUTH);
3812 } else {
3813 root->state_clear(CInode::STATE_AUTH);
3814 if (!replay)
3815 root->state_set(CInode::STATE_REJOINING);
3816 }
3817 }
3818
3819 set<CInode*> subtree_inodes;
3820 for (map<CDir*,set<CDir*> >::iterator p = subtrees.begin();
3821 p != subtrees.end();
3822 ++p) {
3823 if (p->first->dir_auth.first == mds->get_nodeid())
3824 subtree_inodes.insert(p->first->inode);
3825 }
3826
3827 for (map<CDir*,set<CDir*> >::iterator p = subtrees.begin();
3828 p != subtrees.end();
3829 ++p) {
3830 if (p->first->inode->is_mdsdir()) {
3831 CInode *in = p->first->inode;
3832 bool auth = in->ino() == MDS_INO_MDSDIR(mds->get_nodeid());
3833 if (auth) {
3834 in->state_set(CInode::STATE_AUTH);
3835 } else {
3836 in->state_clear(CInode::STATE_AUTH);
3837 if (!replay)
3838 in->state_set(CInode::STATE_REJOINING);
3839 }
3840 }
3841
3842 std::queue<CDir*> dfq; // dirfrag queue
3843 dfq.push(p->first);
3844
3845 bool auth = p->first->authority().first == mds->get_nodeid();
3846 dout(10) << " subtree auth=" << auth << " for " << *p->first << dendl;
3847
3848 while (!dfq.empty()) {
3849 CDir *dir = dfq.front();
3850 dfq.pop();
3851
3852 // dir
3853 if (auth) {
3854 dir->state_set(CDir::STATE_AUTH);
3855 } else {
3856 dir->state_clear(CDir::STATE_AUTH);
3857 if (!replay) {
3858 // close empty non-auth dirfrag
3859 if (!dir->is_subtree_root() && dir->get_num_any() == 0) {
3860 dir->inode->close_dirfrag(dir->get_frag());
3861 continue;
3862 }
3863 dir->state_set(CDir::STATE_REJOINING);
3864 dir->state_clear(CDir::STATE_COMPLETE);
3865 if (dir->is_dirty())
3866 dir->mark_clean();
3867 }
3868 }
3869
3870 // dentries in this dir
3871 for (auto &p : dir->items) {
3872 // dn
3873 CDentry *dn = p.second;
3874 CDentry::linkage_t *dnl = dn->get_linkage();
3875 if (auth) {
3876 dn->state_set(CDentry::STATE_AUTH);
3877 } else {
3878 dn->state_clear(CDentry::STATE_AUTH);
3879 if (!replay) {
3880 dn->state_set(CDentry::STATE_REJOINING);
3881 if (dn->is_dirty())
3882 dn->mark_clean();
3883 }
3884 }
3885
3886 if (dnl->is_primary()) {
3887 // inode
3888 CInode *in = dnl->get_inode();
3889 if (auth) {
3890 in->state_set(CInode::STATE_AUTH);
3891 } else {
3892 in->state_clear(CInode::STATE_AUTH);
3893 if (!replay) {
3894 in->state_set(CInode::STATE_REJOINING);
3895 if (in->is_dirty())
3896 in->mark_clean();
3897 if (in->is_dirty_parent())
3898 in->clear_dirty_parent();
3899 // avoid touching scatterlocks for our subtree roots!
3900 if (subtree_inodes.count(in) == 0)
3901 in->clear_scatter_dirty();
3902 }
3903 }
3904 // recurse?
3905 if (in->is_dir()) {
3906 auto&& dfv = in->get_nested_dirfrags();
3907 for (const auto& dir : dfv) {
3908 dfq.push(dir);
3909 }
3910 }
3911 }
3912 }
3913 }
3914 }
3915
3916 show_subtrees();
3917 show_cache();
3918 }
3919
3920
3921
3922 // ===========================================================================
3923 // REJOIN
3924
3925 /*
3926 * notes on scatterlock recovery:
3927 *
3928 * - recovering inode replica sends scatterlock data for any subtree
3929 * roots (the only ones that are possibly dirty).
3930 *
3931 * - surviving auth incorporates any provided scatterlock data. any
3932 * pending gathers are then finished, as with the other lock types.
3933 *
3934 * that takes care of surviving auth + (recovering replica)*.
3935 *
3936 * - surviving replica sends strong_inode, which includes current
3937 * scatterlock state, AND any dirty scatterlock data. this
3938 * provides the recovering auth with everything it might need.
3939 *
3940 * - recovering auth must pick initial scatterlock state based on
3941 * (weak|strong) rejoins.
3942 * - always assimilate scatterlock data (it can't hurt)
3943 * - any surviving replica in SCATTER state -> SCATTER. otherwise, SYNC.
3944 * - include base inode in ack for all inodes that saw scatterlock content
3945 *
3946 * also, for scatter gather,
3947 *
3948 * - auth increments {frag,r}stat.version on completion of any gather.
3949 *
3950 * - auth incorporates changes in a gather _only_ if the version
3951 * matches.
3952 *
3953 * - replica discards changes any time the scatterlock syncs, and
3954 * after recovery.
3955 */
3956
3957 void MDCache::dump_rejoin_status(Formatter *f) const
3958 {
3959 f->open_object_section("rejoin_status");
3960 f->dump_stream("rejoin_gather") << rejoin_gather;
3961 f->dump_stream("rejoin_ack_gather") << rejoin_ack_gather;
3962 f->dump_unsigned("num_opening_inodes", cap_imports_num_opening);
3963 f->close_section();
3964 }
3965
3966 void MDCache::rejoin_start(MDSContext *rejoin_done_)
3967 {
3968 dout(10) << "rejoin_start" << dendl;
3969 ceph_assert(!rejoin_done);
3970 rejoin_done.reset(rejoin_done_);
3971
3972 rejoin_gather = recovery_set;
3973 // need finish opening cap inodes before sending cache rejoins
3974 rejoin_gather.insert(mds->get_nodeid());
3975 process_imported_caps();
3976 }
3977
3978 /*
3979 * rejoin phase!
3980 *
3981 * this initiates rejoin. it should be called before we get any
3982 * rejoin or rejoin_ack messages (or else mdsmap distribution is broken).
3983 *
3984 * we start out by sending rejoins to everyone in the recovery set.
3985 *
3986 * if we are rejoin, send for all regions in our cache.
3987 * if we are active|stopping, send only to nodes that are rejoining.
3988 */
3989 void MDCache::rejoin_send_rejoins()
3990 {
3991 dout(10) << "rejoin_send_rejoins with recovery_set " << recovery_set << dendl;
3992
3993 if (rejoin_gather.count(mds->get_nodeid())) {
3994 dout(7) << "rejoin_send_rejoins still processing imported caps, delaying" << dendl;
3995 rejoins_pending = true;
3996 return;
3997 }
3998 if (!resolve_gather.empty()) {
3999 dout(7) << "rejoin_send_rejoins still waiting for resolves ("
4000 << resolve_gather << ")" << dendl;
4001 rejoins_pending = true;
4002 return;
4003 }
4004
4005 ceph_assert(!migrator->is_importing());
4006 ceph_assert(!migrator->is_exporting());
4007
4008 if (!mds->is_rejoin()) {
4009 disambiguate_other_imports();
4010 }
4011
4012 map<mds_rank_t, ref_t<MMDSCacheRejoin>> rejoins;
4013
4014
4015 // if i am rejoining, send a rejoin to everyone.
4016 // otherwise, just send to others who are rejoining.
4017 for (const auto& rank : recovery_set) {
4018 if (rank == mds->get_nodeid()) continue; // nothing to myself!
4019 if (rejoin_sent.count(rank)) continue; // already sent a rejoin to this node!
4020 if (mds->is_rejoin())
4021 rejoins[rank] = make_message<MMDSCacheRejoin>(MMDSCacheRejoin::OP_WEAK);
4022 else if (mds->mdsmap->is_rejoin(rank))
4023 rejoins[rank] = make_message<MMDSCacheRejoin>(MMDSCacheRejoin::OP_STRONG);
4024 }
4025
4026 if (mds->is_rejoin()) {
4027 map<client_t, pair<Session*, set<mds_rank_t> > > client_exports;
4028 for (auto& p : cap_exports) {
4029 mds_rank_t target = p.second.first;
4030 if (rejoins.count(target) == 0)
4031 continue;
4032 for (auto q = p.second.second.begin(); q != p.second.second.end(); ) {
4033 Session *session = nullptr;
4034 auto it = client_exports.find(q->first);
4035 if (it != client_exports.end()) {
4036 session = it->second.first;
4037 if (session)
4038 it->second.second.insert(target);
4039 } else {
4040 session = mds->sessionmap.get_session(entity_name_t::CLIENT(q->first.v));
4041 auto& r = client_exports[q->first];
4042 r.first = session;
4043 if (session)
4044 r.second.insert(target);
4045 }
4046 if (session) {
4047 ++q;
4048 } else {
4049 // remove reconnect with no session
4050 p.second.second.erase(q++);
4051 }
4052 }
4053 rejoins[target]->cap_exports[p.first] = p.second.second;
4054 }
4055 for (auto& p : client_exports) {
4056 Session *session = p.second.first;
4057 for (auto& q : p.second.second) {
4058 auto rejoin = rejoins[q];
4059 rejoin->client_map[p.first] = session->info.inst;
4060 rejoin->client_metadata_map[p.first] = session->info.client_metadata;
4061 }
4062 }
4063 }
4064
4065
4066 // check all subtrees
4067 for (map<CDir*, set<CDir*> >::iterator p = subtrees.begin();
4068 p != subtrees.end();
4069 ++p) {
4070 CDir *dir = p->first;
4071 ceph_assert(dir->is_subtree_root());
4072 if (dir->is_ambiguous_dir_auth()) {
4073 // exporter is recovering, importer is survivor.
4074 ceph_assert(rejoins.count(dir->authority().first));
4075 ceph_assert(!rejoins.count(dir->authority().second));
4076 continue;
4077 }
4078
4079 // my subtree?
4080 if (dir->is_auth())
4081 continue; // skip my own regions!
4082
4083 mds_rank_t auth = dir->get_dir_auth().first;
4084 ceph_assert(auth >= 0);
4085 if (rejoins.count(auth) == 0)
4086 continue; // don't care about this node's subtrees
4087
4088 rejoin_walk(dir, rejoins[auth]);
4089 }
4090
4091 // rejoin root inodes, too
4092 for (auto &p : rejoins) {
4093 if (mds->is_rejoin()) {
4094 // weak
4095 if (p.first == 0 && root) {
4096 p.second->add_weak_inode(root->vino());
4097 if (root->is_dirty_scattered()) {
4098 dout(10) << " sending scatterlock state on root " << *root << dendl;
4099 p.second->add_scatterlock_state(root);
4100 }
4101 }
4102 if (CInode *in = get_inode(MDS_INO_MDSDIR(p.first))) {
4103 if (in)
4104 p.second->add_weak_inode(in->vino());
4105 }
4106 } else {
4107 // strong
4108 if (p.first == 0 && root) {
4109 p.second->add_strong_inode(root->vino(),
4110 root->get_replica_nonce(),
4111 root->get_caps_wanted(),
4112 root->filelock.get_state(),
4113 root->nestlock.get_state(),
4114 root->dirfragtreelock.get_state());
4115 root->state_set(CInode::STATE_REJOINING);
4116 if (root->is_dirty_scattered()) {
4117 dout(10) << " sending scatterlock state on root " << *root << dendl;
4118 p.second->add_scatterlock_state(root);
4119 }
4120 }
4121
4122 if (CInode *in = get_inode(MDS_INO_MDSDIR(p.first))) {
4123 p.second->add_strong_inode(in->vino(),
4124 in->get_replica_nonce(),
4125 in->get_caps_wanted(),
4126 in->filelock.get_state(),
4127 in->nestlock.get_state(),
4128 in->dirfragtreelock.get_state());
4129 in->state_set(CInode::STATE_REJOINING);
4130 }
4131 }
4132 }
4133
4134 if (!mds->is_rejoin()) {
4135 // i am survivor. send strong rejoin.
4136 // note request remote_auth_pins, xlocks
4137 for (ceph::unordered_map<metareqid_t, MDRequestRef>::iterator p = active_requests.begin();
4138 p != active_requests.end();
4139 ++p) {
4140 MDRequestRef& mdr = p->second;
4141 if (mdr->is_peer())
4142 continue;
4143 // auth pins
4144 for (const auto& q : mdr->object_states) {
4145 if (q.second.remote_auth_pinned == MDS_RANK_NONE)
4146 continue;
4147 if (!q.first->is_auth()) {
4148 mds_rank_t target = q.second.remote_auth_pinned;
4149 ceph_assert(target == q.first->authority().first);
4150 if (rejoins.count(target) == 0) continue;
4151 const auto& rejoin = rejoins[target];
4152
4153 dout(15) << " " << *mdr << " authpin on " << *q.first << dendl;
4154 MDSCacheObjectInfo i;
4155 q.first->set_object_info(i);
4156 if (i.ino)
4157 rejoin->add_inode_authpin(vinodeno_t(i.ino, i.snapid), mdr->reqid, mdr->attempt);
4158 else
4159 rejoin->add_dentry_authpin(i.dirfrag, i.dname, i.snapid, mdr->reqid, mdr->attempt);
4160
4161 if (mdr->has_more() && mdr->more()->is_remote_frozen_authpin &&
4162 mdr->more()->rename_inode == q.first)
4163 rejoin->add_inode_frozen_authpin(vinodeno_t(i.ino, i.snapid),
4164 mdr->reqid, mdr->attempt);
4165 }
4166 }
4167 // xlocks
4168 for (const auto& q : mdr->locks) {
4169 auto lock = q.lock;
4170 auto obj = lock->get_parent();
4171 if (q.is_xlock() && !obj->is_auth()) {
4172 mds_rank_t who = obj->authority().first;
4173 if (rejoins.count(who) == 0) continue;
4174 const auto& rejoin = rejoins[who];
4175
4176 dout(15) << " " << *mdr << " xlock on " << *lock << " " << *obj << dendl;
4177 MDSCacheObjectInfo i;
4178 obj->set_object_info(i);
4179 if (i.ino)
4180 rejoin->add_inode_xlock(vinodeno_t(i.ino, i.snapid), lock->get_type(),
4181 mdr->reqid, mdr->attempt);
4182 else
4183 rejoin->add_dentry_xlock(i.dirfrag, i.dname, i.snapid,
4184 mdr->reqid, mdr->attempt);
4185 } else if (q.is_remote_wrlock()) {
4186 mds_rank_t who = q.wrlock_target;
4187 if (rejoins.count(who) == 0) continue;
4188 const auto& rejoin = rejoins[who];
4189
4190 dout(15) << " " << *mdr << " wrlock on " << *lock << " " << *obj << dendl;
4191 MDSCacheObjectInfo i;
4192 obj->set_object_info(i);
4193 ceph_assert(i.ino);
4194 rejoin->add_inode_wrlock(vinodeno_t(i.ino, i.snapid), lock->get_type(),
4195 mdr->reqid, mdr->attempt);
4196 }
4197 }
4198 }
4199 }
4200
4201 // send the messages
4202 for (auto &p : rejoins) {
4203 ceph_assert(rejoin_sent.count(p.first) == 0);
4204 ceph_assert(rejoin_ack_gather.count(p.first) == 0);
4205 rejoin_sent.insert(p.first);
4206 rejoin_ack_gather.insert(p.first);
4207 mds->send_message_mds(p.second, p.first);
4208 }
4209 rejoin_ack_gather.insert(mds->get_nodeid()); // we need to complete rejoin_gather_finish, too
4210 rejoins_pending = false;
4211
4212 // nothing?
4213 if (mds->is_rejoin() && rejoin_gather.empty()) {
4214 dout(10) << "nothing to rejoin" << dendl;
4215 rejoin_gather_finish();
4216 }
4217 }
4218
4219
4220 /**
4221 * rejoin_walk - build rejoin declarations for a subtree
4222 *
4223 * @param dir subtree root
4224 * @param rejoin rejoin message
4225 *
4226 * from a rejoining node:
4227 * weak dirfrag
4228 * weak dentries (w/ connectivity)
4229 *
4230 * from a surviving node:
4231 * strong dirfrag
4232 * strong dentries (no connectivity!)
4233 * strong inodes
4234 */
4235 void MDCache::rejoin_walk(CDir *dir, const ref_t<MMDSCacheRejoin> &rejoin)
4236 {
4237 dout(10) << "rejoin_walk " << *dir << dendl;
4238
4239 std::vector<CDir*> nested; // finish this dir, then do nested items
4240
4241 if (mds->is_rejoin()) {
4242 // WEAK
4243 rejoin->add_weak_dirfrag(dir->dirfrag());
4244 for (auto &p : dir->items) {
4245 CDentry *dn = p.second;
4246 ceph_assert(dn->last == CEPH_NOSNAP);
4247 CDentry::linkage_t *dnl = dn->get_linkage();
4248 dout(15) << " add_weak_primary_dentry " << *dn << dendl;
4249 ceph_assert(dnl->is_primary());
4250 CInode *in = dnl->get_inode();
4251 ceph_assert(dnl->get_inode()->is_dir());
4252 rejoin->add_weak_primary_dentry(dir->ino(), dn->get_name(), dn->first, dn->last, in->ino());
4253 {
4254 auto&& dirs = in->get_nested_dirfrags();
4255 nested.insert(std::end(nested), std::begin(dirs), std::end(dirs));
4256 }
4257 if (in->is_dirty_scattered()) {
4258 dout(10) << " sending scatterlock state on " << *in << dendl;
4259 rejoin->add_scatterlock_state(in);
4260 }
4261 }
4262 } else {
4263 // STRONG
4264 dout(15) << " add_strong_dirfrag " << *dir << dendl;
4265 rejoin->add_strong_dirfrag(dir->dirfrag(), dir->get_replica_nonce(), dir->get_dir_rep());
4266 dir->state_set(CDir::STATE_REJOINING);
4267
4268 for (auto it = dir->items.begin(); it != dir->items.end(); ) {
4269 CDentry *dn = it->second;
4270 ++it;
4271 dn->state_set(CDentry::STATE_REJOINING);
4272 CDentry::linkage_t *dnl = dn->get_linkage();
4273 CInode *in = dnl->is_primary() ? dnl->get_inode() : NULL;
4274
4275 // trim snap dentries. because they may have been pruned by
4276 // their auth mds (snap deleted)
4277 if (dn->last != CEPH_NOSNAP) {
4278 if (in && !in->remote_parents.empty()) {
4279 // unlink any stale remote snap dentry.
4280 for (auto it2 = in->remote_parents.begin(); it2 != in->remote_parents.end(); ) {
4281 CDentry *remote_dn = *it2;
4282 ++it2;
4283 ceph_assert(remote_dn->last != CEPH_NOSNAP);
4284 remote_dn->unlink_remote(remote_dn->get_linkage());
4285 }
4286 }
4287 if (dn->lru_is_expireable()) {
4288 if (!dnl->is_null())
4289 dir->unlink_inode(dn, false);
4290 if (in)
4291 remove_inode(in);
4292 dir->remove_dentry(dn);
4293 continue;
4294 } else {
4295 // Inventing null/remote dentry shouldn't cause problem
4296 ceph_assert(!dnl->is_primary());
4297 }
4298 }
4299
4300 dout(15) << " add_strong_dentry " << *dn << dendl;
4301 rejoin->add_strong_dentry(dir->dirfrag(), dn->get_name(), dn->get_alternate_name(),
4302 dn->first, dn->last,
4303 dnl->is_primary() ? dnl->get_inode()->ino():inodeno_t(0),
4304 dnl->is_remote() ? dnl->get_remote_ino():inodeno_t(0),
4305 dnl->is_remote() ? dnl->get_remote_d_type():0,
4306 dn->get_replica_nonce(),
4307 dn->lock.get_state());
4308 dn->state_set(CDentry::STATE_REJOINING);
4309 if (dnl->is_primary()) {
4310 CInode *in = dnl->get_inode();
4311 dout(15) << " add_strong_inode " << *in << dendl;
4312 rejoin->add_strong_inode(in->vino(),
4313 in->get_replica_nonce(),
4314 in->get_caps_wanted(),
4315 in->filelock.get_state(),
4316 in->nestlock.get_state(),
4317 in->dirfragtreelock.get_state());
4318 in->state_set(CInode::STATE_REJOINING);
4319 {
4320 auto&& dirs = in->get_nested_dirfrags();
4321 nested.insert(std::end(nested), std::begin(dirs), std::end(dirs));
4322 }
4323 if (in->is_dirty_scattered()) {
4324 dout(10) << " sending scatterlock state on " << *in << dendl;
4325 rejoin->add_scatterlock_state(in);
4326 }
4327 }
4328 }
4329 }
4330
4331 // recurse into nested dirs
4332 for (const auto& dir : nested) {
4333 rejoin_walk(dir, rejoin);
4334 }
4335 }
4336
4337
4338 /*
4339 * i got a rejoin.
4340 * - reply with the lockstate
4341 *
4342 * if i am active|stopping,
4343 * - remove source from replica list for everything not referenced here.
4344 */
4345 void MDCache::handle_cache_rejoin(const cref_t<MMDSCacheRejoin> &m)
4346 {
4347 dout(7) << "handle_cache_rejoin " << *m << " from " << m->get_source()
4348 << " (" << m->get_payload().length() << " bytes)"
4349 << dendl;
4350
4351 switch (m->op) {
4352 case MMDSCacheRejoin::OP_WEAK:
4353 handle_cache_rejoin_weak(m);
4354 break;
4355 case MMDSCacheRejoin::OP_STRONG:
4356 handle_cache_rejoin_strong(m);
4357 break;
4358 case MMDSCacheRejoin::OP_ACK:
4359 handle_cache_rejoin_ack(m);
4360 break;
4361
4362 default:
4363 ceph_abort();
4364 }
4365 }
4366
4367
4368 /*
4369 * handle_cache_rejoin_weak
4370 *
4371 * the sender
4372 * - is recovering from their journal.
4373 * - may have incorrect (out of date) inode contents
4374 * - will include weak dirfrag if sender is dirfrag auth and parent inode auth is recipient
4375 *
4376 * if the sender didn't trim_non_auth(), they
4377 * - may have incorrect (out of date) dentry/inode linkage
4378 * - may have deleted/purged inodes
4379 * and i may have to go to disk to get accurate inode contents. yuck.
4380 */
4381 void MDCache::handle_cache_rejoin_weak(const cref_t<MMDSCacheRejoin> &weak)
4382 {
4383 mds_rank_t from = mds_rank_t(weak->get_source().num());
4384
4385 // possible response(s)
4386 ref_t<MMDSCacheRejoin> ack; // if survivor
4387 set<vinodeno_t> acked_inodes; // if survivor
4388 set<SimpleLock *> gather_locks; // if survivor
4389 bool survivor = false; // am i a survivor?
4390
4391 if (mds->is_clientreplay() || mds->is_active() || mds->is_stopping()) {
4392 survivor = true;
4393 dout(10) << "i am a surivivor, and will ack immediately" << dendl;
4394 ack = make_message<MMDSCacheRejoin>(MMDSCacheRejoin::OP_ACK);
4395
4396 map<inodeno_t,map<client_t,Capability::Import> > imported_caps;
4397
4398 // check cap exports
4399 for (auto p = weak->cap_exports.begin(); p != weak->cap_exports.end(); ++p) {
4400 CInode *in = get_inode(p->first);
4401 ceph_assert(!in || in->is_auth());
4402 for (auto q = p->second.begin(); q != p->second.end(); ++q) {
4403 dout(10) << " claiming cap import " << p->first << " client." << q->first << " on " << *in << dendl;
4404 Capability *cap = rejoin_import_cap(in, q->first, q->second, from);
4405 Capability::Import& im = imported_caps[p->first][q->first];
4406 if (cap) {
4407 im.cap_id = cap->get_cap_id();
4408 im.issue_seq = cap->get_last_seq();
4409 im.mseq = cap->get_mseq();
4410 } else {
4411 // all are zero
4412 }
4413 }
4414 mds->locker->eval(in, CEPH_CAP_LOCKS, true);
4415 }
4416
4417 encode(imported_caps, ack->imported_caps);
4418 } else {
4419 ceph_assert(mds->is_rejoin());
4420
4421 // we may have already received a strong rejoin from the sender.
4422 rejoin_scour_survivor_replicas(from, NULL, acked_inodes, gather_locks);
4423 ceph_assert(gather_locks.empty());
4424
4425 // check cap exports.
4426 rejoin_client_map.insert(weak->client_map.begin(), weak->client_map.end());
4427 rejoin_client_metadata_map.insert(weak->client_metadata_map.begin(),
4428 weak->client_metadata_map.end());
4429
4430 for (auto p = weak->cap_exports.begin(); p != weak->cap_exports.end(); ++p) {
4431 CInode *in = get_inode(p->first);
4432 ceph_assert(!in || in->is_auth());
4433 // note
4434 for (auto q = p->second.begin(); q != p->second.end(); ++q) {
4435 dout(10) << " claiming cap import " << p->first << " client." << q->first << dendl;
4436 cap_imports[p->first][q->first][from] = q->second;
4437 }
4438 }
4439 }
4440
4441 // assimilate any potentially dirty scatterlock state
4442 for (const auto &p : weak->inode_scatterlocks) {
4443 CInode *in = get_inode(p.first);
4444 ceph_assert(in);
4445 in->decode_lock_state(CEPH_LOCK_IFILE, p.second.file);
4446 in->decode_lock_state(CEPH_LOCK_INEST, p.second.nest);
4447 in->decode_lock_state(CEPH_LOCK_IDFT, p.second.dft);
4448 if (!survivor)
4449 rejoin_potential_updated_scatterlocks.insert(in);
4450 }
4451
4452 // recovering peer may send incorrect dirfrags here. we need to
4453 // infer which dirfrag they meant. the ack will include a
4454 // strong_dirfrag that will set them straight on the fragmentation.
4455
4456 // walk weak map
4457 set<CDir*> dirs_to_share;
4458 for (const auto &p : weak->weak_dirfrags) {
4459 CInode *diri = get_inode(p.ino);
4460 if (!diri)
4461 dout(0) << " missing dir ino " << p.ino << dendl;
4462 ceph_assert(diri);
4463
4464 frag_vec_t leaves;
4465 if (diri->dirfragtree.is_leaf(p.frag)) {
4466 leaves.push_back(p.frag);
4467 } else {
4468 diri->dirfragtree.get_leaves_under(p.frag, leaves);
4469 if (leaves.empty())
4470 leaves.push_back(diri->dirfragtree[p.frag.value()]);
4471 }
4472 for (const auto& leaf : leaves) {
4473 CDir *dir = diri->get_dirfrag(leaf);
4474 if (!dir) {
4475 dout(0) << " missing dir for " << p.frag << " (which maps to " << leaf << ") on " << *diri << dendl;
4476 continue;
4477 }
4478 ceph_assert(dir);
4479 if (dirs_to_share.count(dir)) {
4480 dout(10) << " already have " << p.frag << " -> " << leaf << " " << *dir << dendl;
4481 } else {
4482 dirs_to_share.insert(dir);
4483 unsigned nonce = dir->add_replica(from);
4484 dout(10) << " have " << p.frag << " -> " << leaf << " " << *dir << dendl;
4485 if (ack) {
4486 ack->add_strong_dirfrag(dir->dirfrag(), nonce, dir->dir_rep);
4487 ack->add_dirfrag_base(dir);
4488 }
4489 }
4490 }
4491 }
4492
4493 for (const auto &p : weak->weak) {
4494 CInode *diri = get_inode(p.first);
4495 if (!diri)
4496 dout(0) << " missing dir ino " << p.first << dendl;
4497 ceph_assert(diri);
4498
4499 // weak dentries
4500 CDir *dir = 0;
4501 for (const auto &q : p.second) {
4502 // locate proper dirfrag.
4503 // optimize for common case (one dirfrag) to avoid dirs_to_share set check
4504 frag_t fg = diri->pick_dirfrag(q.first.name);
4505 if (!dir || dir->get_frag() != fg) {
4506 dir = diri->get_dirfrag(fg);
4507 if (!dir)
4508 dout(0) << " missing dir frag " << fg << " on " << *diri << dendl;
4509 ceph_assert(dir);
4510 ceph_assert(dirs_to_share.count(dir));
4511 }
4512
4513 // and dentry
4514 CDentry *dn = dir->lookup(q.first.name, q.first.snapid);
4515 ceph_assert(dn);
4516 CDentry::linkage_t *dnl = dn->get_linkage();
4517 ceph_assert(dnl->is_primary());
4518
4519 if (survivor && dn->is_replica(from))
4520 dentry_remove_replica(dn, from, gather_locks);
4521 unsigned dnonce = dn->add_replica(from);
4522 dout(10) << " have " << *dn << dendl;
4523 if (ack)
4524 ack->add_strong_dentry(dir->dirfrag(), dn->get_name(), dn->get_alternate_name(),
4525 dn->first, dn->last,
4526 dnl->get_inode()->ino(), inodeno_t(0), 0,
4527 dnonce, dn->lock.get_replica_state());
4528
4529 // inode
4530 CInode *in = dnl->get_inode();
4531 ceph_assert(in);
4532
4533 if (survivor && in->is_replica(from))
4534 inode_remove_replica(in, from, true, gather_locks);
4535 unsigned inonce = in->add_replica(from);
4536 dout(10) << " have " << *in << dendl;
4537
4538 // scatter the dirlock, just in case?
4539 if (!survivor && in->is_dir() && in->has_subtree_root_dirfrag())
4540 in->filelock.set_state(LOCK_MIX);
4541
4542 if (ack) {
4543 acked_inodes.insert(in->vino());
4544 ack->add_inode_base(in, mds->mdsmap->get_up_features());
4545 bufferlist bl;
4546 in->_encode_locks_state_for_rejoin(bl, from);
4547 ack->add_inode_locks(in, inonce, bl);
4548 }
4549 }
4550 }
4551
4552 // weak base inodes? (root, stray, etc.)
4553 for (set<vinodeno_t>::iterator p = weak->weak_inodes.begin();
4554 p != weak->weak_inodes.end();
4555 ++p) {
4556 CInode *in = get_inode(*p);
4557 ceph_assert(in); // hmm fixme wrt stray?
4558 if (survivor && in->is_replica(from))
4559 inode_remove_replica(in, from, true, gather_locks);
4560 unsigned inonce = in->add_replica(from);
4561 dout(10) << " have base " << *in << dendl;
4562
4563 if (ack) {
4564 acked_inodes.insert(in->vino());
4565 ack->add_inode_base(in, mds->mdsmap->get_up_features());
4566 bufferlist bl;
4567 in->_encode_locks_state_for_rejoin(bl, from);
4568 ack->add_inode_locks(in, inonce, bl);
4569 }
4570 }
4571
4572 ceph_assert(rejoin_gather.count(from));
4573 rejoin_gather.erase(from);
4574 if (survivor) {
4575 // survivor. do everything now.
4576 for (const auto &p : weak->inode_scatterlocks) {
4577 CInode *in = get_inode(p.first);
4578 ceph_assert(in);
4579 dout(10) << " including base inode (due to potential scatterlock update) " << *in << dendl;
4580 acked_inodes.insert(in->vino());
4581 ack->add_inode_base(in, mds->mdsmap->get_up_features());
4582 }
4583
4584 rejoin_scour_survivor_replicas(from, ack, acked_inodes, gather_locks);
4585 mds->send_message(ack, weak->get_connection());
4586
4587 for (set<SimpleLock*>::iterator p = gather_locks.begin(); p != gather_locks.end(); ++p) {
4588 if (!(*p)->is_stable())
4589 mds->locker->eval_gather(*p);
4590 }
4591 } else {
4592 // done?
4593 if (rejoin_gather.empty() && rejoin_ack_gather.count(mds->get_nodeid())) {
4594 rejoin_gather_finish();
4595 } else {
4596 dout(7) << "still need rejoin from (" << rejoin_gather << ")" << dendl;
4597 }
4598 }
4599 }
4600
4601 /*
4602 * rejoin_scour_survivor_replica - remove source from replica list on unmentioned objects
4603 *
4604 * all validated replicas are acked with a strong nonce, etc. if that isn't in the
4605 * ack, the replica dne, and we can remove it from our replica maps.
4606 */
4607 void MDCache::rejoin_scour_survivor_replicas(mds_rank_t from, const cref_t<MMDSCacheRejoin> &ack,
4608 set<vinodeno_t>& acked_inodes,
4609 set<SimpleLock *>& gather_locks)
4610 {
4611 dout(10) << "rejoin_scour_survivor_replicas from mds." << from << dendl;
4612
4613 auto scour_func = [this, from, ack, &acked_inodes, &gather_locks] (CInode *in) {
4614 // inode?
4615 if (in->is_auth() &&
4616 in->is_replica(from) &&
4617 (ack == NULL || acked_inodes.count(in->vino()) == 0)) {
4618 inode_remove_replica(in, from, false, gather_locks);
4619 dout(10) << " rem " << *in << dendl;
4620 }
4621
4622 if (!in->is_dir())
4623 return;
4624
4625 const auto&& dfs = in->get_dirfrags();
4626 for (const auto& dir : dfs) {
4627 if (!dir->is_auth())
4628 continue;
4629
4630 if (dir->is_replica(from) &&
4631 (ack == NULL || ack->strong_dirfrags.count(dir->dirfrag()) == 0)) {
4632 dir->remove_replica(from);
4633 dout(10) << " rem " << *dir << dendl;
4634 }
4635
4636 // dentries
4637 for (auto &p : dir->items) {
4638 CDentry *dn = p.second;
4639
4640 if (dn->is_replica(from)) {
4641 if (ack) {
4642 const auto it = ack->strong_dentries.find(dir->dirfrag());
4643 if (it != ack->strong_dentries.end() && it->second.count(string_snap_t(dn->get_name(), dn->last)) > 0) {
4644 continue;
4645 }
4646 }
4647 dentry_remove_replica(dn, from, gather_locks);
4648 dout(10) << " rem " << *dn << dendl;
4649 }
4650 }
4651 }
4652 };
4653
4654 for (auto &p : inode_map)
4655 scour_func(p.second);
4656 for (auto &p : snap_inode_map)
4657 scour_func(p.second);
4658 }
4659
4660
4661 CInode *MDCache::rejoin_invent_inode(inodeno_t ino, snapid_t last)
4662 {
4663 CInode *in = new CInode(this, true, 2, last);
4664 in->_get_inode()->ino = ino;
4665 in->state_set(CInode::STATE_REJOINUNDEF);
4666 add_inode(in);
4667 rejoin_undef_inodes.insert(in);
4668 dout(10) << " invented " << *in << dendl;
4669 return in;
4670 }
4671
4672 CDir *MDCache::rejoin_invent_dirfrag(dirfrag_t df)
4673 {
4674 CInode *in = get_inode(df.ino);
4675 if (!in)
4676 in = rejoin_invent_inode(df.ino, CEPH_NOSNAP);
4677 if (!in->is_dir()) {
4678 ceph_assert(in->state_test(CInode::STATE_REJOINUNDEF));
4679 in->_get_inode()->mode = S_IFDIR;
4680 in->_get_inode()->dir_layout.dl_dir_hash = g_conf()->mds_default_dir_hash;
4681 }
4682 CDir *dir = in->get_or_open_dirfrag(this, df.frag);
4683 dir->state_set(CDir::STATE_REJOINUNDEF);
4684 rejoin_undef_dirfrags.insert(dir);
4685 dout(10) << " invented " << *dir << dendl;
4686 return dir;
4687 }
4688
4689 void MDCache::handle_cache_rejoin_strong(const cref_t<MMDSCacheRejoin> &strong)
4690 {
4691 mds_rank_t from = mds_rank_t(strong->get_source().num());
4692
4693 // only a recovering node will get a strong rejoin.
4694 if (!mds->is_rejoin()) {
4695 if (mds->get_want_state() == MDSMap::STATE_REJOIN) {
4696 mds->wait_for_rejoin(new C_MDS_RetryMessage(mds, strong));
4697 return;
4698 }
4699 ceph_abort_msg("got unexpected rejoin message during recovery");
4700 }
4701
4702 // assimilate any potentially dirty scatterlock state
4703 for (const auto &p : strong->inode_scatterlocks) {
4704 CInode *in = get_inode(p.first);
4705 ceph_assert(in);
4706 in->decode_lock_state(CEPH_LOCK_IFILE, p.second.file);
4707 in->decode_lock_state(CEPH_LOCK_INEST, p.second.nest);
4708 in->decode_lock_state(CEPH_LOCK_IDFT, p.second.dft);
4709 rejoin_potential_updated_scatterlocks.insert(in);
4710 }
4711
4712 rejoin_unlinked_inodes[from].clear();
4713
4714 // surviving peer may send incorrect dirfrag here (maybe they didn't
4715 // get the fragment notify, or maybe we rolled back?). we need to
4716 // infer the right frag and get them with the program. somehow.
4717 // we don't normally send ACK.. so we'll need to bundle this with
4718 // MISSING or something.
4719
4720 // strong dirfrags/dentries.
4721 // also process auth_pins, xlocks.
4722 for (const auto &p : strong->strong_dirfrags) {
4723 auto& dirfrag = p.first;
4724 CInode *diri = get_inode(dirfrag.ino);
4725 if (!diri)
4726 diri = rejoin_invent_inode(dirfrag.ino, CEPH_NOSNAP);
4727 CDir *dir = diri->get_dirfrag(dirfrag.frag);
4728 bool refragged = false;
4729 if (dir) {
4730 dout(10) << " have " << *dir << dendl;
4731 } else {
4732 if (diri->state_test(CInode::STATE_REJOINUNDEF))
4733 dir = rejoin_invent_dirfrag(dirfrag_t(diri->ino(), frag_t()));
4734 else if (diri->dirfragtree.is_leaf(dirfrag.frag))
4735 dir = rejoin_invent_dirfrag(dirfrag);
4736 }
4737 if (dir) {
4738 dir->add_replica(from, p.second.nonce);
4739 dir->dir_rep = p.second.dir_rep;
4740 } else {
4741 dout(10) << " frag " << dirfrag << " doesn't match dirfragtree " << *diri << dendl;
4742 frag_vec_t leaves;
4743 diri->dirfragtree.get_leaves_under(dirfrag.frag, leaves);
4744 if (leaves.empty())
4745 leaves.push_back(diri->dirfragtree[dirfrag.frag.value()]);
4746 dout(10) << " maps to frag(s) " << leaves << dendl;
4747 for (const auto& leaf : leaves) {
4748 CDir *dir = diri->get_dirfrag(leaf);
4749 if (!dir)
4750 dir = rejoin_invent_dirfrag(dirfrag_t(diri->ino(), leaf));
4751 else
4752 dout(10) << " have(approx) " << *dir << dendl;
4753 dir->add_replica(from, p.second.nonce);
4754 dir->dir_rep = p.second.dir_rep;
4755 }
4756 refragged = true;
4757 }
4758
4759 const auto it = strong->strong_dentries.find(dirfrag);
4760 if (it != strong->strong_dentries.end()) {
4761 const auto& dmap = it->second;
4762 for (const auto &q : dmap) {
4763 const string_snap_t& ss = q.first;
4764 const MMDSCacheRejoin::dn_strong& d = q.second;
4765 CDentry *dn;
4766 if (!refragged)
4767 dn = dir->lookup(ss.name, ss.snapid);
4768 else {
4769 frag_t fg = diri->pick_dirfrag(ss.name);
4770 dir = diri->get_dirfrag(fg);
4771 ceph_assert(dir);
4772 dn = dir->lookup(ss.name, ss.snapid);
4773 }
4774 if (!dn) {
4775 if (d.is_remote()) {
4776 dn = dir->add_remote_dentry(ss.name, d.remote_ino, d.remote_d_type, mempool::mds_co::string(d.alternate_name), d.first, ss.snapid);
4777 } else if (d.is_null()) {
4778 dn = dir->add_null_dentry(ss.name, d.first, ss.snapid);
4779 } else {
4780 CInode *in = get_inode(d.ino, ss.snapid);
4781 if (!in) in = rejoin_invent_inode(d.ino, ss.snapid);
4782 dn = dir->add_primary_dentry(ss.name, in, mempool::mds_co::string(d.alternate_name), d.first, ss.snapid);
4783 }
4784 dout(10) << " invented " << *dn << dendl;
4785 }
4786 CDentry::linkage_t *dnl = dn->get_linkage();
4787
4788 // dn auth_pin?
4789 const auto pinned_it = strong->authpinned_dentries.find(dirfrag);
4790 if (pinned_it != strong->authpinned_dentries.end()) {
4791 const auto peer_reqid_it = pinned_it->second.find(ss);
4792 if (peer_reqid_it != pinned_it->second.end()) {
4793 for (const auto &r : peer_reqid_it->second) {
4794 dout(10) << " dn authpin by " << r << " on " << *dn << dendl;
4795
4796 // get/create peer mdrequest
4797 MDRequestRef mdr;
4798 if (have_request(r.reqid))
4799 mdr = request_get(r.reqid);
4800 else
4801 mdr = request_start_peer(r.reqid, r.attempt, strong);
4802 mdr->auth_pin(dn);
4803 }
4804 }
4805 }
4806
4807 // dn xlock?
4808 const auto xlocked_it = strong->xlocked_dentries.find(dirfrag);
4809 if (xlocked_it != strong->xlocked_dentries.end()) {
4810 const auto ss_req_it = xlocked_it->second.find(ss);
4811 if (ss_req_it != xlocked_it->second.end()) {
4812 const MMDSCacheRejoin::peer_reqid& r = ss_req_it->second;
4813 dout(10) << " dn xlock by " << r << " on " << *dn << dendl;
4814 MDRequestRef mdr = request_get(r.reqid); // should have this from auth_pin above.
4815 ceph_assert(mdr->is_auth_pinned(dn));
4816 if (!mdr->is_xlocked(&dn->versionlock)) {
4817 ceph_assert(dn->versionlock.can_xlock_local());
4818 dn->versionlock.get_xlock(mdr, mdr->get_client());
4819 mdr->emplace_lock(&dn->versionlock, MutationImpl::LockOp::XLOCK);
4820 }
4821 if (dn->lock.is_stable())
4822 dn->auth_pin(&dn->lock);
4823 dn->lock.set_state(LOCK_XLOCK);
4824 dn->lock.get_xlock(mdr, mdr->get_client());
4825 mdr->emplace_lock(&dn->lock, MutationImpl::LockOp::XLOCK);
4826 }
4827 }
4828
4829 dn->add_replica(from, d.nonce);
4830 dout(10) << " have " << *dn << dendl;
4831
4832 if (dnl->is_primary()) {
4833 if (d.is_primary()) {
4834 if (vinodeno_t(d.ino, ss.snapid) != dnl->get_inode()->vino()) {
4835 // the survivor missed MDentryUnlink+MDentryLink messages ?
4836 ceph_assert(strong->strong_inodes.count(dnl->get_inode()->vino()) == 0);
4837 CInode *in = get_inode(d.ino, ss.snapid);
4838 ceph_assert(in);
4839 ceph_assert(in->get_parent_dn());
4840 rejoin_unlinked_inodes[from].insert(in);
4841 dout(7) << " sender has primary dentry but wrong inode" << dendl;
4842 }
4843 } else {
4844 // the survivor missed MDentryLink message ?
4845 ceph_assert(strong->strong_inodes.count(dnl->get_inode()->vino()) == 0);
4846 dout(7) << " sender doesn't have primay dentry" << dendl;
4847 }
4848 } else {
4849 if (d.is_primary()) {
4850 // the survivor missed MDentryUnlink message ?
4851 CInode *in = get_inode(d.ino, ss.snapid);
4852 ceph_assert(in);
4853 ceph_assert(in->get_parent_dn());
4854 rejoin_unlinked_inodes[from].insert(in);
4855 dout(7) << " sender has primary dentry but we don't" << dendl;
4856 }
4857 }
4858 }
4859 }
4860 }
4861
4862 for (const auto &p : strong->strong_inodes) {
4863 CInode *in = get_inode(p.first);
4864 ceph_assert(in);
4865 in->add_replica(from, p.second.nonce);
4866 dout(10) << " have " << *in << dendl;
4867
4868 const MMDSCacheRejoin::inode_strong& is = p.second;
4869
4870 // caps_wanted
4871 if (is.caps_wanted) {
4872 in->set_mds_caps_wanted(from, is.caps_wanted);
4873 dout(15) << " inode caps_wanted " << ccap_string(is.caps_wanted)
4874 << " on " << *in << dendl;
4875 }
4876
4877 // scatterlocks?
4878 // infer state from replica state:
4879 // * go to MIX if they might have wrlocks
4880 // * go to LOCK if they are LOCK (just bc identify_files_to_recover might start twiddling filelock)
4881 in->filelock.infer_state_from_strong_rejoin(is.filelock, !in->is_dir()); // maybe also go to LOCK
4882 in->nestlock.infer_state_from_strong_rejoin(is.nestlock, false);
4883 in->dirfragtreelock.infer_state_from_strong_rejoin(is.dftlock, false);
4884
4885 // auth pin?
4886 const auto authpinned_inodes_it = strong->authpinned_inodes.find(in->vino());
4887 if (authpinned_inodes_it != strong->authpinned_inodes.end()) {
4888 for (const auto& r : authpinned_inodes_it->second) {
4889 dout(10) << " inode authpin by " << r << " on " << *in << dendl;
4890
4891 // get/create peer mdrequest
4892 MDRequestRef mdr;
4893 if (have_request(r.reqid))
4894 mdr = request_get(r.reqid);
4895 else
4896 mdr = request_start_peer(r.reqid, r.attempt, strong);
4897 if (strong->frozen_authpin_inodes.count(in->vino())) {
4898 ceph_assert(!in->get_num_auth_pins());
4899 mdr->freeze_auth_pin(in);
4900 } else {
4901 ceph_assert(!in->is_frozen_auth_pin());
4902 }
4903 mdr->auth_pin(in);
4904 }
4905 }
4906 // xlock(s)?
4907 const auto xlocked_inodes_it = strong->xlocked_inodes.find(in->vino());
4908 if (xlocked_inodes_it != strong->xlocked_inodes.end()) {
4909 for (const auto &q : xlocked_inodes_it->second) {
4910 SimpleLock *lock = in->get_lock(q.first);
4911 dout(10) << " inode xlock by " << q.second << " on " << *lock << " on " << *in << dendl;
4912 MDRequestRef mdr = request_get(q.second.reqid); // should have this from auth_pin above.
4913 ceph_assert(mdr->is_auth_pinned(in));
4914 if (!mdr->is_xlocked(&in->versionlock)) {
4915 ceph_assert(in->versionlock.can_xlock_local());
4916 in->versionlock.get_xlock(mdr, mdr->get_client());
4917 mdr->emplace_lock(&in->versionlock, MutationImpl::LockOp::XLOCK);
4918 }
4919 if (lock->is_stable())
4920 in->auth_pin(lock);
4921 lock->set_state(LOCK_XLOCK);
4922 if (lock == &in->filelock)
4923 in->loner_cap = -1;
4924 lock->get_xlock(mdr, mdr->get_client());
4925 mdr->emplace_lock(lock, MutationImpl::LockOp::XLOCK);
4926 }
4927 }
4928 }
4929 // wrlock(s)?
4930 for (const auto &p : strong->wrlocked_inodes) {
4931 CInode *in = get_inode(p.first);
4932 for (const auto &q : p.second) {
4933 SimpleLock *lock = in->get_lock(q.first);
4934 for (const auto &r : q.second) {
4935 dout(10) << " inode wrlock by " << r << " on " << *lock << " on " << *in << dendl;
4936 MDRequestRef mdr = request_get(r.reqid); // should have this from auth_pin above.
4937 if (in->is_auth())
4938 ceph_assert(mdr->is_auth_pinned(in));
4939 lock->set_state(LOCK_MIX);
4940 if (lock == &in->filelock)
4941 in->loner_cap = -1;
4942 lock->get_wrlock(true);
4943 mdr->emplace_lock(lock, MutationImpl::LockOp::WRLOCK);
4944 }
4945 }
4946 }
4947
4948 // done?
4949 ceph_assert(rejoin_gather.count(from));
4950 rejoin_gather.erase(from);
4951 if (rejoin_gather.empty() && rejoin_ack_gather.count(mds->get_nodeid())) {
4952 rejoin_gather_finish();
4953 } else {
4954 dout(7) << "still need rejoin from (" << rejoin_gather << ")" << dendl;
4955 }
4956 }
4957
4958 void MDCache::handle_cache_rejoin_ack(const cref_t<MMDSCacheRejoin> &ack)
4959 {
4960 dout(7) << "handle_cache_rejoin_ack from " << ack->get_source() << dendl;
4961 mds_rank_t from = mds_rank_t(ack->get_source().num());
4962
4963 ceph_assert(mds->get_state() >= MDSMap::STATE_REJOIN);
4964 bool survivor = !mds->is_rejoin();
4965
4966 // for sending cache expire message
4967 set<CInode*> isolated_inodes;
4968 set<CInode*> refragged_inodes;
4969 list<pair<CInode*,int> > updated_realms;
4970
4971 // dirs
4972 for (const auto &p : ack->strong_dirfrags) {
4973 // we may have had incorrect dir fragmentation; refragment based
4974 // on what they auth tells us.
4975 CDir *dir = get_dirfrag(p.first);
4976 if (!dir) {
4977 dir = get_force_dirfrag(p.first, false);
4978 if (dir)
4979 refragged_inodes.insert(dir->get_inode());
4980 }
4981 if (!dir) {
4982 CInode *diri = get_inode(p.first.ino);
4983 if (!diri) {
4984 // barebones inode; the full inode loop below will clean up.
4985 diri = new CInode(this, false);
4986 auto _inode = diri->_get_inode();
4987 _inode->ino = p.first.ino;
4988 _inode->mode = S_IFDIR;
4989 _inode->dir_layout.dl_dir_hash = g_conf()->mds_default_dir_hash;
4990
4991 add_inode(diri);
4992 if (MDS_INO_MDSDIR(from) == p.first.ino) {
4993 diri->inode_auth = mds_authority_t(from, CDIR_AUTH_UNKNOWN);
4994 dout(10) << " add inode " << *diri << dendl;
4995 } else {
4996 diri->inode_auth = CDIR_AUTH_DEFAULT;
4997 isolated_inodes.insert(diri);
4998 dout(10) << " unconnected dirfrag " << p.first << dendl;
4999 }
5000 }
5001 // barebones dirfrag; the full dirfrag loop below will clean up.
5002 dir = diri->add_dirfrag(new CDir(diri, p.first.frag, this, false));
5003 if (MDS_INO_MDSDIR(from) == p.first.ino ||
5004 (dir->authority() != CDIR_AUTH_UNDEF &&
5005 dir->authority().first != from))
5006 adjust_subtree_auth(dir, from);
5007 dout(10) << " add dirfrag " << *dir << dendl;
5008 }
5009
5010 dir->set_replica_nonce(p.second.nonce);
5011 dir->state_clear(CDir::STATE_REJOINING);
5012 dout(10) << " got " << *dir << dendl;
5013
5014 // dentries
5015 auto it = ack->strong_dentries.find(p.first);
5016 if (it != ack->strong_dentries.end()) {
5017 for (const auto &q : it->second) {
5018 CDentry *dn = dir->lookup(q.first.name, q.first.snapid);
5019 if(!dn)
5020 dn = dir->add_null_dentry(q.first.name, q.second.first, q.first.snapid);
5021
5022 CDentry::linkage_t *dnl = dn->get_linkage();
5023
5024 ceph_assert(dn->last == q.first.snapid);
5025 if (dn->first != q.second.first) {
5026 dout(10) << " adjust dn.first " << dn->first << " -> " << q.second.first << " on " << *dn << dendl;
5027 dn->first = q.second.first;
5028 }
5029
5030 // may have bad linkage if we missed dentry link/unlink messages
5031 if (dnl->is_primary()) {
5032 CInode *in = dnl->get_inode();
5033 if (!q.second.is_primary() ||
5034 vinodeno_t(q.second.ino, q.first.snapid) != in->vino()) {
5035 dout(10) << " had bad linkage for " << *dn << ", unlinking " << *in << dendl;
5036 dir->unlink_inode(dn);
5037 }
5038 } else if (dnl->is_remote()) {
5039 if (!q.second.is_remote() ||
5040 q.second.remote_ino != dnl->get_remote_ino() ||
5041 q.second.remote_d_type != dnl->get_remote_d_type()) {
5042 dout(10) << " had bad linkage for " << *dn << dendl;
5043 dir->unlink_inode(dn);
5044 }
5045 } else {
5046 if (!q.second.is_null())
5047 dout(10) << " had bad linkage for " << *dn << dendl;
5048 }
5049
5050 // hmm, did we have the proper linkage here?
5051 if (dnl->is_null() && !q.second.is_null()) {
5052 if (q.second.is_remote()) {
5053 dn->dir->link_remote_inode(dn, q.second.remote_ino, q.second.remote_d_type);
5054 } else {
5055 CInode *in = get_inode(q.second.ino, q.first.snapid);
5056 if (!in) {
5057 // barebones inode; assume it's dir, the full inode loop below will clean up.
5058 in = new CInode(this, false, q.second.first, q.first.snapid);
5059 auto _inode = in->_get_inode();
5060 _inode->ino = q.second.ino;
5061 _inode->mode = S_IFDIR;
5062 _inode->dir_layout.dl_dir_hash = g_conf()->mds_default_dir_hash;
5063 add_inode(in);
5064 dout(10) << " add inode " << *in << dendl;
5065 } else if (in->get_parent_dn()) {
5066 dout(10) << " had bad linkage for " << *(in->get_parent_dn())
5067 << ", unlinking " << *in << dendl;
5068 in->get_parent_dir()->unlink_inode(in->get_parent_dn());
5069 }
5070 dn->dir->link_primary_inode(dn, in);
5071 isolated_inodes.erase(in);
5072 }
5073 }
5074
5075 dn->set_replica_nonce(q.second.nonce);
5076 dn->lock.set_state_rejoin(q.second.lock, rejoin_waiters, survivor);
5077 dn->state_clear(CDentry::STATE_REJOINING);
5078 dout(10) << " got " << *dn << dendl;
5079 }
5080 }
5081 }
5082
5083 for (const auto& in : refragged_inodes) {
5084 auto&& ls = in->get_nested_dirfrags();
5085 for (const auto& dir : ls) {
5086 if (dir->is_auth() || ack->strong_dirfrags.count(dir->dirfrag()))
5087 continue;
5088 ceph_assert(dir->get_num_any() == 0);
5089 in->close_dirfrag(dir->get_frag());
5090 }
5091 }
5092
5093 // full dirfrags
5094 for (const auto &p : ack->dirfrag_bases) {
5095 CDir *dir = get_dirfrag(p.first);
5096 ceph_assert(dir);
5097 auto q = p.second.cbegin();
5098 dir->_decode_base(q);
5099 dout(10) << " got dir replica " << *dir << dendl;
5100 }
5101
5102 // full inodes
5103 auto p = ack->inode_base.cbegin();
5104 while (!p.end()) {
5105 inodeno_t ino;
5106 snapid_t last;
5107 bufferlist basebl;
5108 decode(ino, p);
5109 decode(last, p);
5110 decode(basebl, p);
5111 CInode *in = get_inode(ino, last);
5112 ceph_assert(in);
5113 auto q = basebl.cbegin();
5114 snapid_t sseq = 0;
5115 if (in->snaprealm)
5116 sseq = in->snaprealm->srnode.seq;
5117 in->_decode_base(q);
5118 if (in->snaprealm && in->snaprealm->srnode.seq != sseq) {
5119 int snap_op = sseq > 0 ? CEPH_SNAP_OP_UPDATE : CEPH_SNAP_OP_SPLIT;
5120 updated_realms.push_back(pair<CInode*,int>(in, snap_op));
5121 }
5122 dout(10) << " got inode base " << *in << dendl;
5123 }
5124
5125 // inodes
5126 p = ack->inode_locks.cbegin();
5127 //dout(10) << "inode_locks len " << ack->inode_locks.length() << " is " << ack->inode_locks << dendl;
5128 while (!p.end()) {
5129 inodeno_t ino;
5130 snapid_t last;
5131 __u32 nonce;
5132 bufferlist lockbl;
5133 decode(ino, p);
5134 decode(last, p);
5135 decode(nonce, p);
5136 decode(lockbl, p);
5137
5138 CInode *in = get_inode(ino, last);
5139 ceph_assert(in);
5140 in->set_replica_nonce(nonce);
5141 auto q = lockbl.cbegin();
5142 in->_decode_locks_rejoin(q, rejoin_waiters, rejoin_eval_locks, survivor);
5143 in->state_clear(CInode::STATE_REJOINING);
5144 dout(10) << " got inode locks " << *in << dendl;
5145 }
5146
5147 // FIXME: This can happen if entire subtree, together with the inode subtree root
5148 // belongs to, were trimmed between sending cache rejoin and receiving rejoin ack.
5149 ceph_assert(isolated_inodes.empty());
5150
5151 map<inodeno_t,map<client_t,Capability::Import> > peer_imported;
5152 auto bp = ack->imported_caps.cbegin();
5153 decode(peer_imported, bp);
5154
5155 for (map<inodeno_t,map<client_t,Capability::Import> >::iterator p = peer_imported.begin();
5156 p != peer_imported.end();
5157 ++p) {
5158 auto& ex = cap_exports.at(p->first);
5159 ceph_assert(ex.first == from);
5160 for (map<client_t,Capability::Import>::iterator q = p->second.begin();
5161 q != p->second.end();
5162 ++q) {
5163 auto r = ex.second.find(q->first);
5164 ceph_assert(r != ex.second.end());
5165
5166 dout(10) << " exporting caps for client." << q->first << " ino " << p->first << dendl;
5167 Session *session = mds->sessionmap.get_session(entity_name_t::CLIENT(q->first.v));
5168 if (!session) {
5169 dout(10) << " no session for client." << p->first << dendl;
5170 ex.second.erase(r);
5171 continue;
5172 }
5173
5174 // mark client caps stale.
5175 auto m = make_message<MClientCaps>(CEPH_CAP_OP_EXPORT, p->first, 0,
5176 r->second.capinfo.cap_id, 0,
5177 mds->get_osd_epoch_barrier());
5178 m->set_cap_peer(q->second.cap_id, q->second.issue_seq, q->second.mseq,
5179 (q->second.cap_id > 0 ? from : -1), 0);
5180 mds->send_message_client_counted(m, session);
5181
5182 ex.second.erase(r);
5183 }
5184 ceph_assert(ex.second.empty());
5185 }
5186
5187 for (auto p : updated_realms) {
5188 CInode *in = p.first;
5189 bool notify_clients;
5190 if (mds->is_rejoin()) {
5191 if (!rejoin_pending_snaprealms.count(in)) {
5192 in->get(CInode::PIN_OPENINGSNAPPARENTS);
5193 rejoin_pending_snaprealms.insert(in);
5194 }
5195 notify_clients = false;
5196 } else {
5197 // notify clients if I'm survivor
5198 notify_clients = true;
5199 }
5200 do_realm_invalidate_and_update_notify(in, p.second, notify_clients);
5201 }
5202
5203 // done?
5204 ceph_assert(rejoin_ack_gather.count(from));
5205 rejoin_ack_gather.erase(from);
5206 if (!survivor) {
5207 if (rejoin_gather.empty()) {
5208 // eval unstable scatter locks after all wrlocks are rejoined.
5209 while (!rejoin_eval_locks.empty()) {
5210 SimpleLock *lock = rejoin_eval_locks.front();
5211 rejoin_eval_locks.pop_front();
5212 if (!lock->is_stable())
5213 mds->locker->eval_gather(lock);
5214 }
5215 }
5216
5217 if (rejoin_gather.empty() && // make sure we've gotten our FULL inodes, too.
5218 rejoin_ack_gather.empty()) {
5219 // finally, kickstart past snap parent opens
5220 open_snaprealms();
5221 } else {
5222 dout(7) << "still need rejoin from (" << rejoin_gather << ")"
5223 << ", rejoin_ack from (" << rejoin_ack_gather << ")" << dendl;
5224 }
5225 } else {
5226 // survivor.
5227 mds->queue_waiters(rejoin_waiters);
5228 }
5229 }
5230
5231 /**
5232 * rejoin_trim_undef_inodes() -- remove REJOINUNDEF flagged inodes
5233 *
5234 * FIXME: wait, can this actually happen? a survivor should generate cache trim
5235 * messages that clean these guys up...
5236 */
5237 void MDCache::rejoin_trim_undef_inodes()
5238 {
5239 dout(10) << "rejoin_trim_undef_inodes" << dendl;
5240
5241 while (!rejoin_undef_inodes.empty()) {
5242 set<CInode*>::iterator p = rejoin_undef_inodes.begin();
5243 CInode *in = *p;
5244 rejoin_undef_inodes.erase(p);
5245
5246 in->clear_replica_map();
5247
5248 // close out dirfrags
5249 if (in->is_dir()) {
5250 const auto&& dfls = in->get_dirfrags();
5251 for (const auto& dir : dfls) {
5252 dir->clear_replica_map();
5253
5254 for (auto &p : dir->items) {
5255 CDentry *dn = p.second;
5256 dn->clear_replica_map();
5257
5258 dout(10) << " trimming " << *dn << dendl;
5259 dir->remove_dentry(dn);
5260 }
5261
5262 dout(10) << " trimming " << *dir << dendl;
5263 in->close_dirfrag(dir->dirfrag().frag);
5264 }
5265 }
5266
5267 CDentry *dn = in->get_parent_dn();
5268 if (dn) {
5269 dn->clear_replica_map();
5270 dout(10) << " trimming " << *dn << dendl;
5271 dn->dir->remove_dentry(dn);
5272 } else {
5273 dout(10) << " trimming " << *in << dendl;
5274 remove_inode(in);
5275 }
5276 }
5277
5278 ceph_assert(rejoin_undef_inodes.empty());
5279 }
5280
5281 void MDCache::rejoin_gather_finish()
5282 {
5283 dout(10) << "rejoin_gather_finish" << dendl;
5284 ceph_assert(mds->is_rejoin());
5285 ceph_assert(rejoin_ack_gather.count(mds->get_nodeid()));
5286
5287 if (open_undef_inodes_dirfrags())
5288 return;
5289
5290 if (process_imported_caps())
5291 return;
5292
5293 choose_lock_states_and_reconnect_caps();
5294
5295 identify_files_to_recover();
5296 rejoin_send_acks();
5297
5298 // signal completion of fetches, rejoin_gather_finish, etc.
5299 rejoin_ack_gather.erase(mds->get_nodeid());
5300
5301 // did we already get our acks too?
5302 if (rejoin_ack_gather.empty()) {
5303 // finally, open snaprealms
5304 open_snaprealms();
5305 }
5306 }
5307
5308 class C_MDC_RejoinOpenInoFinish: public MDCacheContext {
5309 inodeno_t ino;
5310 public:
5311 C_MDC_RejoinOpenInoFinish(MDCache *c, inodeno_t i) : MDCacheContext(c), ino(i) {}
5312 void finish(int r) override {
5313 mdcache->rejoin_open_ino_finish(ino, r);
5314 }
5315 };
5316
5317 void MDCache::rejoin_open_ino_finish(inodeno_t ino, int ret)
5318 {
5319 dout(10) << "open_caps_inode_finish ino " << ino << " ret " << ret << dendl;
5320
5321 if (ret < 0) {
5322 cap_imports_missing.insert(ino);
5323 } else if (ret == mds->get_nodeid()) {
5324 ceph_assert(get_inode(ino));
5325 } else {
5326 auto p = cap_imports.find(ino);
5327 ceph_assert(p != cap_imports.end());
5328 for (auto q = p->second.begin(); q != p->second.end(); ++q) {
5329 ceph_assert(q->second.count(MDS_RANK_NONE));
5330 ceph_assert(q->second.size() == 1);
5331 rejoin_export_caps(p->first, q->first, q->second[MDS_RANK_NONE], ret);
5332 }
5333 cap_imports.erase(p);
5334 }
5335
5336 ceph_assert(cap_imports_num_opening > 0);
5337 cap_imports_num_opening--;
5338
5339 if (cap_imports_num_opening == 0) {
5340 if (rejoin_gather.empty() && rejoin_ack_gather.count(mds->get_nodeid()))
5341 rejoin_gather_finish();
5342 else if (rejoin_gather.count(mds->get_nodeid()))
5343 process_imported_caps();
5344 }
5345 }
5346
5347 class C_MDC_RejoinSessionsOpened : public MDCacheLogContext {
5348 public:
5349 map<client_t,pair<Session*,uint64_t> > session_map;
5350 C_MDC_RejoinSessionsOpened(MDCache *c) : MDCacheLogContext(c) {}
5351 void finish(int r) override {
5352 ceph_assert(r == 0);
5353 mdcache->rejoin_open_sessions_finish(session_map);
5354 }
5355 };
5356
5357 void MDCache::rejoin_open_sessions_finish(map<client_t,pair<Session*,uint64_t> >& session_map)
5358 {
5359 dout(10) << "rejoin_open_sessions_finish" << dendl;
5360 mds->server->finish_force_open_sessions(session_map);
5361 rejoin_session_map.swap(session_map);
5362 if (rejoin_gather.empty() && rejoin_ack_gather.count(mds->get_nodeid()))
5363 rejoin_gather_finish();
5364 }
5365
5366 void MDCache::rejoin_prefetch_ino_finish(inodeno_t ino, int ret)
5367 {
5368 auto p = cap_imports.find(ino);
5369 if (p != cap_imports.end()) {
5370 dout(10) << __func__ << " ino " << ino << " ret " << ret << dendl;
5371 if (ret < 0) {
5372 cap_imports_missing.insert(ino);
5373 } else if (ret != mds->get_nodeid()) {
5374 for (auto q = p->second.begin(); q != p->second.end(); ++q) {
5375 ceph_assert(q->second.count(MDS_RANK_NONE));
5376 ceph_assert(q->second.size() == 1);
5377 rejoin_export_caps(p->first, q->first, q->second[MDS_RANK_NONE], ret);
5378 }
5379 cap_imports.erase(p);
5380 }
5381 }
5382 }
5383
5384 bool MDCache::process_imported_caps()
5385 {
5386 dout(10) << "process_imported_caps" << dendl;
5387
5388 if (!open_file_table.is_prefetched() &&
5389 open_file_table.prefetch_inodes()) {
5390 open_file_table.wait_for_prefetch(
5391 new MDSInternalContextWrapper(mds,
5392 new LambdaContext([this](int r) {
5393 ceph_assert(rejoin_gather.count(mds->get_nodeid()));
5394 process_imported_caps();
5395 })
5396 )
5397 );
5398 return true;
5399 }
5400
5401 for (auto& p : cap_imports) {
5402 CInode *in = get_inode(p.first);
5403 if (in) {
5404 ceph_assert(in->is_auth());
5405 cap_imports_missing.erase(p.first);
5406 continue;
5407 }
5408 if (cap_imports_missing.count(p.first) > 0)
5409 continue;
5410
5411 uint64_t parent_ino = 0;
5412 std::string_view d_name;
5413 for (auto& q : p.second) {
5414 for (auto& r : q.second) {
5415 auto &icr = r.second;
5416 if (icr.capinfo.pathbase &&
5417 icr.path.length() > 0 &&
5418 icr.path.find('/') == string::npos) {
5419 parent_ino = icr.capinfo.pathbase;
5420 d_name = icr.path;
5421 break;
5422 }
5423 }
5424 if (parent_ino)
5425 break;
5426 }
5427
5428 dout(10) << " opening missing ino " << p.first << dendl;
5429 cap_imports_num_opening++;
5430 auto fin = new C_MDC_RejoinOpenInoFinish(this, p.first);
5431 if (parent_ino) {
5432 vector<inode_backpointer_t> ancestors;
5433 ancestors.push_back(inode_backpointer_t(parent_ino, string{d_name}, 0));
5434 open_ino(p.first, (int64_t)-1, fin, false, false, &ancestors);
5435 } else {
5436 open_ino(p.first, (int64_t)-1, fin, false);
5437 }
5438 if (!(cap_imports_num_opening % mds->heartbeat_reset_grace()))
5439 mds->heartbeat_reset();
5440 }
5441
5442 if (cap_imports_num_opening > 0)
5443 return true;
5444
5445 // called by rejoin_gather_finish() ?
5446 if (rejoin_gather.count(mds->get_nodeid()) == 0) {
5447 if (!rejoin_client_map.empty() &&
5448 rejoin_session_map.empty()) {
5449 C_MDC_RejoinSessionsOpened *finish = new C_MDC_RejoinSessionsOpened(this);
5450 version_t pv = mds->server->prepare_force_open_sessions(rejoin_client_map,
5451 rejoin_client_metadata_map,
5452 finish->session_map);
5453 ESessions *le = new ESessions(pv, std::move(rejoin_client_map),
5454 std::move(rejoin_client_metadata_map));
5455 mds->mdlog->start_submit_entry(le, finish);
5456 mds->mdlog->flush();
5457 rejoin_client_map.clear();
5458 rejoin_client_metadata_map.clear();
5459 return true;
5460 }
5461
5462 // process caps that were exported by peer rename
5463 for (map<inodeno_t,pair<mds_rank_t,map<client_t,Capability::Export> > >::iterator p = rejoin_peer_exports.begin();
5464 p != rejoin_peer_exports.end();
5465 ++p) {
5466 CInode *in = get_inode(p->first);
5467 ceph_assert(in);
5468 for (map<client_t,Capability::Export>::iterator q = p->second.second.begin();
5469 q != p->second.second.end();
5470 ++q) {
5471 auto r = rejoin_session_map.find(q->first);
5472 if (r == rejoin_session_map.end())
5473 continue;
5474
5475 Session *session = r->second.first;
5476 Capability *cap = in->get_client_cap(q->first);
5477 if (!cap) {
5478 cap = in->add_client_cap(q->first, session);
5479 // add empty item to reconnected_caps
5480 (void)reconnected_caps[p->first][q->first];
5481 }
5482 cap->merge(q->second, true);
5483
5484 Capability::Import& im = rejoin_imported_caps[p->second.first][p->first][q->first];
5485 ceph_assert(cap->get_last_seq() == im.issue_seq);
5486 ceph_assert(cap->get_mseq() == im.mseq);
5487 cap->set_cap_id(im.cap_id);
5488 // send cap import because we assigned a new cap ID
5489 do_cap_import(session, in, cap, q->second.cap_id, q->second.seq, q->second.mseq - 1,
5490 p->second.first, CEPH_CAP_FLAG_AUTH);
5491 }
5492 }
5493 rejoin_peer_exports.clear();
5494 rejoin_imported_caps.clear();
5495
5496 // process cap imports
5497 // ino -> client -> frommds -> capex
5498 for (auto p = cap_imports.begin(); p != cap_imports.end(); ) {
5499 CInode *in = get_inode(p->first);
5500 if (!in) {
5501 dout(10) << " still missing ino " << p->first
5502 << ", will try again after replayed client requests" << dendl;
5503 ++p;
5504 continue;
5505 }
5506 ceph_assert(in->is_auth());
5507 for (auto q = p->second.begin(); q != p->second.end(); ++q) {
5508 Session *session;
5509 {
5510 auto r = rejoin_session_map.find(q->first);
5511 session = (r != rejoin_session_map.end() ? r->second.first : nullptr);
5512 }
5513
5514 for (auto r = q->second.begin(); r != q->second.end(); ++r) {
5515 if (!session) {
5516 if (r->first >= 0)
5517 (void)rejoin_imported_caps[r->first][p->first][q->first]; // all are zero
5518 continue;
5519 }
5520
5521 Capability *cap = in->reconnect_cap(q->first, r->second, session);
5522 add_reconnected_cap(q->first, in->ino(), r->second);
5523 if (r->first >= 0) {
5524 if (cap->get_last_seq() == 0) // don't increase mseq if cap already exists
5525 cap->inc_mseq();
5526 do_cap_import(session, in, cap, r->second.capinfo.cap_id, 0, 0, r->first, 0);
5527
5528 Capability::Import& im = rejoin_imported_caps[r->first][p->first][q->first];
5529 im.cap_id = cap->get_cap_id();
5530 im.issue_seq = cap->get_last_seq();
5531 im.mseq = cap->get_mseq();
5532 }
5533 }
5534 }
5535 cap_imports.erase(p++); // remove and move on
5536 }
5537 } else {
5538 trim_non_auth();
5539
5540 ceph_assert(rejoin_gather.count(mds->get_nodeid()));
5541 rejoin_gather.erase(mds->get_nodeid());
5542 ceph_assert(!rejoin_ack_gather.count(mds->get_nodeid()));
5543 maybe_send_pending_rejoins();
5544 }
5545 return false;
5546 }
5547
5548 void MDCache::rebuild_need_snapflush(CInode *head_in, SnapRealm *realm,
5549 client_t client, snapid_t snap_follows)
5550 {
5551 dout(10) << "rebuild_need_snapflush " << snap_follows << " on " << *head_in << dendl;
5552
5553 if (!realm->has_snaps_in_range(snap_follows + 1, head_in->first - 1))
5554 return;
5555
5556 const set<snapid_t>& snaps = realm->get_snaps();
5557 snapid_t follows = snap_follows;
5558
5559 while (true) {
5560 CInode *in = pick_inode_snap(head_in, follows);
5561 if (in == head_in)
5562 break;
5563
5564 bool need_snapflush = false;
5565 for (auto p = snaps.lower_bound(std::max<snapid_t>(in->first, (follows + 1)));
5566 p != snaps.end() && *p <= in->last;
5567 ++p) {
5568 head_in->add_need_snapflush(in, *p, client);
5569 need_snapflush = true;
5570 }
5571 follows = in->last;
5572 if (!need_snapflush)
5573 continue;
5574
5575 dout(10) << " need snapflush from client." << client << " on " << *in << dendl;
5576
5577 if (in->client_snap_caps.empty()) {
5578 for (int i = 0; i < num_cinode_locks; i++) {
5579 int lockid = cinode_lock_info[i].lock;
5580 SimpleLock *lock = in->get_lock(lockid);
5581 ceph_assert(lock);
5582 in->auth_pin(lock);
5583 lock->set_state(LOCK_SNAP_SYNC);
5584 lock->get_wrlock(true);
5585 }
5586 }
5587 in->client_snap_caps.insert(client);
5588 mds->locker->mark_need_snapflush_inode(in);
5589 }
5590 }
5591
5592 /*
5593 * choose lock states based on reconnected caps
5594 */
5595 void MDCache::choose_lock_states_and_reconnect_caps()
5596 {
5597 dout(10) << "choose_lock_states_and_reconnect_caps" << dendl;
5598
5599 int count = 0;
5600 for (auto p : inode_map) {
5601 CInode *in = p.second;
5602 if (in->last != CEPH_NOSNAP)
5603 continue;
5604
5605 if (in->is_auth() && !in->is_base() && in->get_inode()->is_dirty_rstat())
5606 in->mark_dirty_rstat();
5607
5608 int dirty_caps = 0;
5609 auto q = reconnected_caps.find(in->ino());
5610 if (q != reconnected_caps.end()) {
5611 for (const auto &it : q->second)
5612 dirty_caps |= it.second.dirty_caps;
5613 }
5614 in->choose_lock_states(dirty_caps);
5615 dout(15) << " chose lock states on " << *in << dendl;
5616
5617 if (in->snaprealm && !rejoin_pending_snaprealms.count(in)) {
5618 in->get(CInode::PIN_OPENINGSNAPPARENTS);
5619 rejoin_pending_snaprealms.insert(in);
5620 }
5621
5622 if (!(++count % mds->heartbeat_reset_grace()))
5623 mds->heartbeat_reset();
5624 }
5625 }
5626
5627 void MDCache::prepare_realm_split(SnapRealm *realm, client_t client, inodeno_t ino,
5628 map<client_t,ref_t<MClientSnap>>& splits)
5629 {
5630 ref_t<MClientSnap> snap;
5631 auto it = splits.find(client);
5632 if (it != splits.end()) {
5633 snap = it->second;
5634 snap->head.op = CEPH_SNAP_OP_SPLIT;
5635 } else {
5636 snap = make_message<MClientSnap>(CEPH_SNAP_OP_SPLIT);
5637 splits.emplace(std::piecewise_construct, std::forward_as_tuple(client), std::forward_as_tuple(snap));
5638 snap->head.split = realm->inode->ino();
5639 snap->bl = realm->get_snap_trace();
5640
5641 for (const auto& child : realm->open_children)
5642 snap->split_realms.push_back(child->inode->ino());
5643 }
5644 snap->split_inos.push_back(ino);
5645 }
5646
5647 void MDCache::prepare_realm_merge(SnapRealm *realm, SnapRealm *parent_realm,
5648 map<client_t,ref_t<MClientSnap>>& splits)
5649 {
5650 ceph_assert(parent_realm);
5651
5652 vector<inodeno_t> split_inos;
5653 vector<inodeno_t> split_realms;
5654
5655 for (auto p = realm->inodes_with_caps.begin(); !p.end(); ++p)
5656 split_inos.push_back((*p)->ino());
5657 for (set<SnapRealm*>::iterator p = realm->open_children.begin();
5658 p != realm->open_children.end();
5659 ++p)
5660 split_realms.push_back((*p)->inode->ino());
5661
5662 for (const auto& p : realm->client_caps) {
5663 ceph_assert(!p.second->empty());
5664 auto em = splits.emplace(std::piecewise_construct, std::forward_as_tuple(p.first), std::forward_as_tuple());
5665 if (em.second) {
5666 auto update = make_message<MClientSnap>(CEPH_SNAP_OP_SPLIT);
5667 update->head.split = parent_realm->inode->ino();
5668 update->split_inos = split_inos;
5669 update->split_realms = split_realms;
5670 update->bl = parent_realm->get_snap_trace();
5671 em.first->second = std::move(update);
5672 }
5673 }
5674 }
5675
5676 void MDCache::send_snaps(map<client_t,ref_t<MClientSnap>>& splits)
5677 {
5678 dout(10) << "send_snaps" << dendl;
5679
5680 for (auto &p : splits) {
5681 Session *session = mds->sessionmap.get_session(entity_name_t::CLIENT(p.first.v));
5682 if (session) {
5683 dout(10) << " client." << p.first
5684 << " split " << p.second->head.split
5685 << " inos " << p.second->split_inos
5686 << dendl;
5687 mds->send_message_client_counted(p.second, session);
5688 } else {
5689 dout(10) << " no session for client." << p.first << dendl;
5690 }
5691 }
5692 splits.clear();
5693 }
5694
5695
5696 /*
5697 * remove any items from logsegment open_file lists that don't have
5698 * any caps
5699 */
5700 void MDCache::clean_open_file_lists()
5701 {
5702 dout(10) << "clean_open_file_lists" << dendl;
5703
5704 for (map<uint64_t,LogSegment*>::iterator p = mds->mdlog->segments.begin();
5705 p != mds->mdlog->segments.end();
5706 ++p) {
5707 LogSegment *ls = p->second;
5708
5709 elist<CInode*>::iterator q = ls->open_files.begin(member_offset(CInode, item_open_file));
5710 while (!q.end()) {
5711 CInode *in = *q;
5712 ++q;
5713 if (in->last == CEPH_NOSNAP) {
5714 dout(10) << " unlisting unwanted/capless inode " << *in << dendl;
5715 in->item_open_file.remove_myself();
5716 } else {
5717 if (in->client_snap_caps.empty()) {
5718 dout(10) << " unlisting flushed snap inode " << *in << dendl;
5719 in->item_open_file.remove_myself();
5720 }
5721 }
5722 }
5723 }
5724 }
5725
5726 void MDCache::dump_openfiles(Formatter *f)
5727 {
5728 f->open_array_section("openfiles");
5729 for (auto p = mds->mdlog->segments.begin();
5730 p != mds->mdlog->segments.end();
5731 ++p) {
5732 LogSegment *ls = p->second;
5733
5734 auto q = ls->open_files.begin(member_offset(CInode, item_open_file));
5735 while (!q.end()) {
5736 CInode *in = *q;
5737 ++q;
5738 if ((in->last == CEPH_NOSNAP && !in->is_any_caps_wanted())
5739 || (in->last != CEPH_NOSNAP && in->client_snap_caps.empty()))
5740 continue;
5741 f->open_object_section("file");
5742 in->dump(f, CInode::DUMP_PATH | CInode::DUMP_INODE_STORE_BASE | CInode::DUMP_CAPS);
5743 f->close_section();
5744 }
5745 }
5746 f->close_section();
5747 }
5748
5749 Capability* MDCache::rejoin_import_cap(CInode *in, client_t client, const cap_reconnect_t& icr, mds_rank_t frommds)
5750 {
5751 dout(10) << "rejoin_import_cap for client." << client << " from mds." << frommds
5752 << " on " << *in << dendl;
5753 Session *session = mds->sessionmap.get_session(entity_name_t::CLIENT(client.v));
5754 if (!session) {
5755 dout(10) << " no session for client." << client << dendl;
5756 return NULL;
5757 }
5758
5759 Capability *cap = in->reconnect_cap(client, icr, session);
5760
5761 if (frommds >= 0) {
5762 if (cap->get_last_seq() == 0) // don't increase mseq if cap already exists
5763 cap->inc_mseq();
5764 do_cap_import(session, in, cap, icr.capinfo.cap_id, 0, 0, frommds, 0);
5765 }
5766
5767 return cap;
5768 }
5769
5770 void MDCache::export_remaining_imported_caps()
5771 {
5772 dout(10) << "export_remaining_imported_caps" << dendl;
5773
5774 CachedStackStringStream css;
5775
5776 int count = 0;
5777 for (auto p = cap_imports.begin(); p != cap_imports.end(); ++p) {
5778 *css << " ino " << p->first << "\n";
5779 for (auto q = p->second.begin(); q != p->second.end(); ++q) {
5780 Session *session = mds->sessionmap.get_session(entity_name_t::CLIENT(q->first.v));
5781 if (session) {
5782 // mark client caps stale.
5783 auto stale = make_message<MClientCaps>(CEPH_CAP_OP_EXPORT, p->first,
5784 0, 0, 0,
5785 mds->get_osd_epoch_barrier());
5786 stale->set_cap_peer(0, 0, 0, -1, 0);
5787 mds->send_message_client_counted(stale, q->first);
5788 }
5789 }
5790
5791 if (!(++count % mds->heartbeat_reset_grace()))
5792 mds->heartbeat_reset();
5793 }
5794
5795 for (map<inodeno_t, MDSContext::vec >::iterator p = cap_reconnect_waiters.begin();
5796 p != cap_reconnect_waiters.end();
5797 ++p)
5798 mds->queue_waiters(p->second);
5799
5800 cap_imports.clear();
5801 cap_reconnect_waiters.clear();
5802
5803 if (css->strv().length()) {
5804 mds->clog->warn() << "failed to reconnect caps for missing inodes:"
5805 << css->strv();
5806 }
5807 }
5808
5809 Capability* MDCache::try_reconnect_cap(CInode *in, Session *session)
5810 {
5811 client_t client = session->info.get_client();
5812 Capability *cap = nullptr;
5813 const cap_reconnect_t *rc = get_replay_cap_reconnect(in->ino(), client);
5814 if (rc) {
5815 cap = in->reconnect_cap(client, *rc, session);
5816 dout(10) << "try_reconnect_cap client." << client
5817 << " reconnect wanted " << ccap_string(rc->capinfo.wanted)
5818 << " issue " << ccap_string(rc->capinfo.issued)
5819 << " on " << *in << dendl;
5820 remove_replay_cap_reconnect(in->ino(), client);
5821
5822 if (in->is_replicated()) {
5823 mds->locker->try_eval(in, CEPH_CAP_LOCKS);
5824 } else {
5825 int dirty_caps = 0;
5826 auto p = reconnected_caps.find(in->ino());
5827 if (p != reconnected_caps.end()) {
5828 auto q = p->second.find(client);
5829 if (q != p->second.end())
5830 dirty_caps = q->second.dirty_caps;
5831 }
5832 in->choose_lock_states(dirty_caps);
5833 dout(15) << " chose lock states on " << *in << dendl;
5834 }
5835
5836 map<inodeno_t, MDSContext::vec >::iterator it =
5837 cap_reconnect_waiters.find(in->ino());
5838 if (it != cap_reconnect_waiters.end()) {
5839 mds->queue_waiters(it->second);
5840 cap_reconnect_waiters.erase(it);
5841 }
5842 }
5843 return cap;
5844 }
5845
5846
5847
5848 // -------
5849 // cap imports and delayed snap parent opens
5850
5851 void MDCache::do_cap_import(Session *session, CInode *in, Capability *cap,
5852 uint64_t p_cap_id, ceph_seq_t p_seq, ceph_seq_t p_mseq,
5853 int peer, int p_flags)
5854 {
5855 SnapRealm *realm = in->find_snaprealm();
5856 dout(10) << "do_cap_import " << session->info.inst.name << " mseq " << cap->get_mseq() << " on " << *in << dendl;
5857 if (cap->get_last_seq() == 0) // reconnected cap
5858 cap->inc_last_seq();
5859 cap->set_last_issue();
5860 cap->set_last_issue_stamp(ceph_clock_now());
5861 cap->clear_new();
5862 auto reap = make_message<MClientCaps>(CEPH_CAP_OP_IMPORT,
5863 in->ino(), realm->inode->ino(), cap->get_cap_id(),
5864 cap->get_last_seq(), cap->pending(), cap->wanted(),
5865 0, cap->get_mseq(), mds->get_osd_epoch_barrier());
5866 in->encode_cap_message(reap, cap);
5867 reap->snapbl = realm->get_snap_trace();
5868 reap->set_cap_peer(p_cap_id, p_seq, p_mseq, peer, p_flags);
5869 mds->send_message_client_counted(reap, session);
5870 }
5871
5872 void MDCache::do_delayed_cap_imports()
5873 {
5874 dout(10) << "do_delayed_cap_imports" << dendl;
5875
5876 ceph_assert(delayed_imported_caps.empty());
5877 }
5878
5879 struct C_MDC_OpenSnapRealms : public MDCacheContext {
5880 explicit C_MDC_OpenSnapRealms(MDCache *c) : MDCacheContext(c) {}
5881 void finish(int r) override {
5882 mdcache->open_snaprealms();
5883 }
5884 };
5885
5886 void MDCache::open_snaprealms()
5887 {
5888 dout(10) << "open_snaprealms" << dendl;
5889
5890 auto it = rejoin_pending_snaprealms.begin();
5891 while (it != rejoin_pending_snaprealms.end()) {
5892 CInode *in = *it;
5893 SnapRealm *realm = in->snaprealm;
5894 ceph_assert(realm);
5895
5896 map<client_t,ref_t<MClientSnap>> splits;
5897 // finish off client snaprealm reconnects?
5898 auto q = reconnected_snaprealms.find(in->ino());
5899 if (q != reconnected_snaprealms.end()) {
5900 for (const auto& r : q->second)
5901 finish_snaprealm_reconnect(r.first, realm, r.second, splits);
5902 reconnected_snaprealms.erase(q);
5903 }
5904
5905 for (auto p = realm->inodes_with_caps.begin(); !p.end(); ++p) {
5906 CInode *child = *p;
5907 auto q = reconnected_caps.find(child->ino());
5908 ceph_assert(q != reconnected_caps.end());
5909 for (auto r = q->second.begin(); r != q->second.end(); ++r) {
5910 Capability *cap = child->get_client_cap(r->first);
5911 if (!cap)
5912 continue;
5913 if (r->second.snap_follows > 0) {
5914 if (r->second.snap_follows < child->first - 1) {
5915 rebuild_need_snapflush(child, realm, r->first, r->second.snap_follows);
5916 } else if (r->second.snapflush) {
5917 // When processing a cap flush message that is re-sent, it's possble
5918 // that the sender has already released all WR caps. So we should
5919 // force MDCache::cow_inode() to setup CInode::client_need_snapflush.
5920 cap->mark_needsnapflush();
5921 }
5922 }
5923 // make sure client's cap is in the correct snaprealm.
5924 if (r->second.realm_ino != in->ino()) {
5925 prepare_realm_split(realm, r->first, child->ino(), splits);
5926 }
5927 }
5928 }
5929
5930 rejoin_pending_snaprealms.erase(it++);
5931 in->put(CInode::PIN_OPENINGSNAPPARENTS);
5932
5933 send_snaps(splits);
5934 }
5935
5936 notify_global_snaprealm_update(CEPH_SNAP_OP_UPDATE);
5937
5938 if (!reconnected_snaprealms.empty()) {
5939 dout(5) << "open_snaprealms has unconnected snaprealm:" << dendl;
5940 for (auto& p : reconnected_snaprealms) {
5941 CachedStackStringStream css;
5942 *css << " " << p.first << " {";
5943 bool first = true;
5944 for (auto& q : p.second) {
5945 if (!first)
5946 *css << ", ";
5947 *css << "client." << q.first << "/" << q.second;
5948 }
5949 *css << "}";
5950 dout(5) << css->strv() << dendl;
5951 }
5952 }
5953 ceph_assert(rejoin_waiters.empty());
5954 ceph_assert(rejoin_pending_snaprealms.empty());
5955 dout(10) << "open_snaprealms - all open" << dendl;
5956 do_delayed_cap_imports();
5957
5958 ceph_assert(rejoin_done);
5959 rejoin_done.release()->complete(0);
5960 reconnected_caps.clear();
5961 }
5962
5963 bool MDCache::open_undef_inodes_dirfrags()
5964 {
5965 dout(10) << "open_undef_inodes_dirfrags "
5966 << rejoin_undef_inodes.size() << " inodes "
5967 << rejoin_undef_dirfrags.size() << " dirfrags" << dendl;
5968
5969 set<CDir*> fetch_queue = rejoin_undef_dirfrags;
5970
5971 for (set<CInode*>::iterator p = rejoin_undef_inodes.begin();
5972 p != rejoin_undef_inodes.end();
5973 ++p) {
5974 CInode *in = *p;
5975 ceph_assert(!in->is_base());
5976 ceph_assert(in->get_parent_dir());
5977 fetch_queue.insert(in->get_parent_dir());
5978 }
5979
5980 if (fetch_queue.empty())
5981 return false;
5982
5983 MDSGatherBuilder gather(g_ceph_context,
5984 new MDSInternalContextWrapper(mds,
5985 new LambdaContext([this](int r) {
5986 if (rejoin_gather.empty() && rejoin_ack_gather.count(mds->get_nodeid()))
5987 rejoin_gather_finish();
5988 })
5989 )
5990 );
5991
5992 for (set<CDir*>::iterator p = fetch_queue.begin();
5993 p != fetch_queue.end();
5994 ++p) {
5995 CDir *dir = *p;
5996 CInode *diri = dir->get_inode();
5997 if (diri->state_test(CInode::STATE_REJOINUNDEF))
5998 continue;
5999 if (dir->state_test(CDir::STATE_REJOINUNDEF))
6000 ceph_assert(diri->dirfragtree.is_leaf(dir->get_frag()));
6001 dir->fetch(gather.new_sub());
6002 }
6003 ceph_assert(gather.has_subs());
6004 gather.activate();
6005 return true;
6006 }
6007
6008 void MDCache::opened_undef_inode(CInode *in) {
6009 dout(10) << "opened_undef_inode " << *in << dendl;
6010 rejoin_undef_inodes.erase(in);
6011 if (in->is_dir()) {
6012 // FIXME: re-hash dentries if necessary
6013 ceph_assert(in->get_inode()->dir_layout.dl_dir_hash == g_conf()->mds_default_dir_hash);
6014 if (in->get_num_dirfrags() && !in->dirfragtree.is_leaf(frag_t())) {
6015 CDir *dir = in->get_dirfrag(frag_t());
6016 ceph_assert(dir);
6017 rejoin_undef_dirfrags.erase(dir);
6018 in->force_dirfrags();
6019 auto&& ls = in->get_dirfrags();
6020 for (const auto& dir : ls) {
6021 rejoin_undef_dirfrags.insert(dir);
6022 }
6023 }
6024 }
6025 }
6026
6027 void MDCache::finish_snaprealm_reconnect(client_t client, SnapRealm *realm, snapid_t seq,
6028 map<client_t,ref_t<MClientSnap>>& updates)
6029 {
6030 if (seq < realm->get_newest_seq()) {
6031 dout(10) << "finish_snaprealm_reconnect client." << client << " has old seq " << seq << " < "
6032 << realm->get_newest_seq() << " on " << *realm << dendl;
6033 auto snap = make_message<MClientSnap>(CEPH_SNAP_OP_UPDATE);
6034 snap->bl = realm->get_snap_trace();
6035 for (const auto& child : realm->open_children)
6036 snap->split_realms.push_back(child->inode->ino());
6037 updates.emplace(std::piecewise_construct, std::forward_as_tuple(client), std::forward_as_tuple(snap));
6038 } else {
6039 dout(10) << "finish_snaprealm_reconnect client." << client << " up to date"
6040 << " on " << *realm << dendl;
6041 }
6042 }
6043
6044
6045
6046 void MDCache::rejoin_send_acks()
6047 {
6048 dout(7) << "rejoin_send_acks" << dendl;
6049
6050 // replicate stray
6051 for (map<mds_rank_t, set<CInode*> >::iterator p = rejoin_unlinked_inodes.begin();
6052 p != rejoin_unlinked_inodes.end();
6053 ++p) {
6054 for (set<CInode*>::iterator q = p->second.begin();
6055 q != p->second.end();
6056 ++q) {
6057 CInode *in = *q;
6058 dout(7) << " unlinked inode " << *in << dendl;
6059 // inode expired
6060 if (!in->is_replica(p->first))
6061 continue;
6062 while (1) {
6063 CDentry *dn = in->get_parent_dn();
6064 if (dn->is_replica(p->first))
6065 break;
6066 dn->add_replica(p->first);
6067 CDir *dir = dn->get_dir();
6068 if (dir->is_replica(p->first))
6069 break;
6070 dir->add_replica(p->first);
6071 in = dir->get_inode();
6072 if (in->is_replica(p->first))
6073 break;
6074 in->add_replica(p->first);
6075 if (in->is_base())
6076 break;
6077 }
6078 }
6079 }
6080 rejoin_unlinked_inodes.clear();
6081
6082 // send acks to everyone in the recovery set
6083 map<mds_rank_t,ref_t<MMDSCacheRejoin>> acks;
6084 for (set<mds_rank_t>::iterator p = recovery_set.begin();
6085 p != recovery_set.end();
6086 ++p) {
6087 if (rejoin_ack_sent.count(*p))
6088 continue;
6089 acks[*p] = make_message<MMDSCacheRejoin>(MMDSCacheRejoin::OP_ACK);
6090 }
6091
6092 rejoin_ack_sent = recovery_set;
6093
6094 // walk subtrees
6095 for (map<CDir*,set<CDir*> >::iterator p = subtrees.begin();
6096 p != subtrees.end();
6097 ++p) {
6098 CDir *dir = p->first;
6099 if (!dir->is_auth())
6100 continue;
6101 dout(10) << "subtree " << *dir << dendl;
6102
6103 // auth items in this subtree
6104 std::queue<CDir*> dq;
6105 dq.push(dir);
6106
6107 while (!dq.empty()) {
6108 CDir *dir = dq.front();
6109 dq.pop();
6110
6111 // dir
6112 for (auto &r : dir->get_replicas()) {
6113 auto it = acks.find(r.first);
6114 if (it == acks.end())
6115 continue;
6116 it->second->add_strong_dirfrag(dir->dirfrag(), ++r.second, dir->dir_rep);
6117 it->second->add_dirfrag_base(dir);
6118 }
6119
6120 for (auto &p : dir->items) {
6121 CDentry *dn = p.second;
6122 CDentry::linkage_t *dnl = dn->get_linkage();
6123
6124 // inode
6125 CInode *in = NULL;
6126 if (dnl->is_primary())
6127 in = dnl->get_inode();
6128
6129 // dentry
6130 for (auto &r : dn->get_replicas()) {
6131 auto it = acks.find(r.first);
6132 if (it == acks.end())
6133 continue;
6134 it->second->add_strong_dentry(dir->dirfrag(), dn->get_name(), dn->get_alternate_name(),
6135 dn->first, dn->last,
6136 dnl->is_primary() ? dnl->get_inode()->ino():inodeno_t(0),
6137 dnl->is_remote() ? dnl->get_remote_ino():inodeno_t(0),
6138 dnl->is_remote() ? dnl->get_remote_d_type():0,
6139 ++r.second,
6140 dn->lock.get_replica_state());
6141 // peer missed MDentrylink message ?
6142 if (in && !in->is_replica(r.first))
6143 in->add_replica(r.first);
6144 }
6145
6146 if (!in)
6147 continue;
6148
6149 for (auto &r : in->get_replicas()) {
6150 auto it = acks.find(r.first);
6151 if (it == acks.end())
6152 continue;
6153 it->second->add_inode_base(in, mds->mdsmap->get_up_features());
6154 bufferlist bl;
6155 in->_encode_locks_state_for_rejoin(bl, r.first);
6156 it->second->add_inode_locks(in, ++r.second, bl);
6157 }
6158
6159 // subdirs in this subtree?
6160 {
6161 auto&& dirs = in->get_nested_dirfrags();
6162 for (const auto& dir : dirs) {
6163 dq.push(dir);
6164 }
6165 }
6166 }
6167 }
6168 }
6169
6170 // base inodes too
6171 if (root && root->is_auth())
6172 for (auto &r : root->get_replicas()) {
6173 auto it = acks.find(r.first);
6174 if (it == acks.end())
6175 continue;
6176 it->second->add_inode_base(root, mds->mdsmap->get_up_features());
6177 bufferlist bl;
6178 root->_encode_locks_state_for_rejoin(bl, r.first);
6179 it->second->add_inode_locks(root, ++r.second, bl);
6180 }
6181 if (myin)
6182 for (auto &r : myin->get_replicas()) {
6183 auto it = acks.find(r.first);
6184 if (it == acks.end())
6185 continue;
6186 it->second->add_inode_base(myin, mds->mdsmap->get_up_features());
6187 bufferlist bl;
6188 myin->_encode_locks_state_for_rejoin(bl, r.first);
6189 it->second->add_inode_locks(myin, ++r.second, bl);
6190 }
6191
6192 // include inode base for any inodes whose scatterlocks may have updated
6193 for (set<CInode*>::iterator p = rejoin_potential_updated_scatterlocks.begin();
6194 p != rejoin_potential_updated_scatterlocks.end();
6195 ++p) {
6196 CInode *in = *p;
6197 for (const auto &r : in->get_replicas()) {
6198 auto it = acks.find(r.first);
6199 if (it == acks.end())
6200 continue;
6201 it->second->add_inode_base(in, mds->mdsmap->get_up_features());
6202 }
6203 }
6204
6205 // send acks
6206 for (auto p = acks.begin(); p != acks.end(); ++p) {
6207 encode(rejoin_imported_caps[p->first], p->second->imported_caps);
6208 mds->send_message_mds(p->second, p->first);
6209 }
6210
6211 rejoin_imported_caps.clear();
6212 }
6213
6214 class C_MDC_ReIssueCaps : public MDCacheContext {
6215 CInode *in;
6216 public:
6217 C_MDC_ReIssueCaps(MDCache *mdc, CInode *i) :
6218 MDCacheContext(mdc), in(i)
6219 {
6220 in->get(CInode::PIN_PTRWAITER);
6221 }
6222 void finish(int r) override {
6223 if (!mdcache->mds->locker->eval(in, CEPH_CAP_LOCKS))
6224 mdcache->mds->locker->issue_caps(in);
6225 in->put(CInode::PIN_PTRWAITER);
6226 }
6227 };
6228
6229 void MDCache::reissue_all_caps()
6230 {
6231 dout(10) << "reissue_all_caps" << dendl;
6232
6233 int count = 0;
6234 for (auto &p : inode_map) {
6235 int n = 1;
6236 CInode *in = p.second;
6237 if (in->is_head() && in->is_any_caps()) {
6238 // called by MDSRank::active_start(). There shouldn't be any frozen subtree.
6239 if (in->is_frozen_inode()) {
6240 in->add_waiter(CInode::WAIT_UNFREEZE, new C_MDC_ReIssueCaps(this, in));
6241 continue;
6242 }
6243 if (!mds->locker->eval(in, CEPH_CAP_LOCKS))
6244 n += mds->locker->issue_caps(in);
6245 }
6246
6247 if ((count % mds->heartbeat_reset_grace()) + n >= mds->heartbeat_reset_grace())
6248 mds->heartbeat_reset();
6249 count += n;
6250 }
6251 }
6252
6253
6254 // ===============================================================================
6255
6256 struct C_MDC_QueuedCow : public MDCacheContext {
6257 CInode *in;
6258 MutationRef mut;
6259 C_MDC_QueuedCow(MDCache *mdc, CInode *i, MutationRef& m) :
6260 MDCacheContext(mdc), in(i), mut(m) {}
6261 void finish(int r) override {
6262 mdcache->_queued_file_recover_cow(in, mut);
6263 }
6264 };
6265
6266
6267 void MDCache::queue_file_recover(CInode *in)
6268 {
6269 dout(10) << "queue_file_recover " << *in << dendl;
6270 ceph_assert(in->is_auth());
6271
6272 // cow?
6273 /*
6274 SnapRealm *realm = in->find_snaprealm();
6275 set<snapid_t> s = realm->get_snaps();
6276 while (!s.empty() && *s.begin() < in->first)
6277 s.erase(s.begin());
6278 while (!s.empty() && *s.rbegin() > in->last)
6279 s.erase(*s.rbegin());
6280 dout(10) << " snaps in [" << in->first << "," << in->last << "] are " << s << dendl;
6281 if (s.size() > 1) {
6282 auto pi = in->project_inode(mut);
6283 pi.inode.version = in->pre_dirty();
6284
6285 auto mut(std::make_shared<MutationImpl>());
6286 mut->ls = mds->mdlog->get_current_segment();
6287 EUpdate *le = new EUpdate(mds->mdlog, "queue_file_recover cow");
6288 mds->mdlog->start_entry(le);
6289 predirty_journal_parents(mut, &le->metablob, in, 0, PREDIRTY_PRIMARY);
6290
6291 s.erase(*s.begin());
6292 while (!s.empty()) {
6293 snapid_t snapid = *s.begin();
6294 CInode *cow_inode = 0;
6295 journal_cow_inode(mut, &le->metablob, in, snapid-1, &cow_inode);
6296 ceph_assert(cow_inode);
6297 recovery_queue.enqueue(cow_inode);
6298 s.erase(*s.begin());
6299 }
6300
6301 in->parent->first = in->first;
6302 le->metablob.add_primary_dentry(in->parent, in, true);
6303 mds->mdlog->submit_entry(le, new C_MDC_QueuedCow(this, in, mut));
6304 mds->mdlog->flush();
6305 }
6306 */
6307
6308 recovery_queue.enqueue(in);
6309 }
6310
6311 void MDCache::_queued_file_recover_cow(CInode *in, MutationRef& mut)
6312 {
6313 mut->apply();
6314 mds->locker->drop_locks(mut.get());
6315 mut->cleanup();
6316 }
6317
6318
6319 /*
6320 * called after recovery to recover file sizes for previously opened (for write)
6321 * files. that is, those where max_size > size.
6322 */
6323 void MDCache::identify_files_to_recover()
6324 {
6325 dout(10) << "identify_files_to_recover" << dendl;
6326 int count = 0;
6327
6328 // Clear the recover and check queues in case the monitor sends rejoin mdsmap twice.
6329 rejoin_recover_q.clear();
6330 rejoin_check_q.clear();
6331
6332 for (auto &p : inode_map) {
6333 CInode *in = p.second;
6334 if (!in->is_auth())
6335 continue;
6336
6337 if (in->last != CEPH_NOSNAP)
6338 continue;
6339
6340 // Only normal files need file size recovery
6341 if (!in->is_file()) {
6342 continue;
6343 }
6344
6345 bool recover = false;
6346 const auto& client_ranges = in->get_projected_inode()->client_ranges;
6347 if (!client_ranges.empty()) {
6348 in->mark_clientwriteable();
6349 for (auto& p : client_ranges) {
6350 Capability *cap = in->get_client_cap(p.first);
6351 if (cap) {
6352 cap->mark_clientwriteable();
6353 } else {
6354 dout(10) << " client." << p.first << " has range " << p.second << " but no cap on " << *in << dendl;
6355 recover = true;
6356 break;
6357 }
6358 }
6359 }
6360
6361 if (recover) {
6362 if (in->filelock.is_stable()) {
6363 in->auth_pin(&in->filelock);
6364 } else {
6365 ceph_assert(in->filelock.get_state() == LOCK_XLOCKSNAP);
6366 }
6367 in->filelock.set_state(LOCK_PRE_SCAN);
6368 rejoin_recover_q.push_back(in);
6369 } else {
6370 rejoin_check_q.push_back(in);
6371 }
6372
6373 if (!(++count % mds->heartbeat_reset_grace()))
6374 mds->heartbeat_reset();
6375 }
6376 }
6377
6378 void MDCache::start_files_to_recover()
6379 {
6380 int count = 0;
6381 for (CInode *in : rejoin_check_q) {
6382 if (in->filelock.get_state() == LOCK_XLOCKSNAP)
6383 mds->locker->issue_caps(in);
6384 mds->locker->check_inode_max_size(in);
6385 if (!(++count % mds->heartbeat_reset_grace()))
6386 mds->heartbeat_reset();
6387 }
6388 rejoin_check_q.clear();
6389 for (CInode *in : rejoin_recover_q) {
6390 mds->locker->file_recover(&in->filelock);
6391 if (!(++count % mds->heartbeat_reset_grace()))
6392 mds->heartbeat_reset();
6393 }
6394 if (!rejoin_recover_q.empty()) {
6395 rejoin_recover_q.clear();
6396 do_file_recover();
6397 }
6398 }
6399
6400 void MDCache::do_file_recover()
6401 {
6402 recovery_queue.advance();
6403 }
6404
6405 // ===============================================================================
6406
6407
6408 // ----------------------------
6409 // truncate
6410
6411 class C_MDC_RetryTruncate : public MDCacheContext {
6412 CInode *in;
6413 LogSegment *ls;
6414 public:
6415 C_MDC_RetryTruncate(MDCache *c, CInode *i, LogSegment *l) :
6416 MDCacheContext(c), in(i), ls(l) {}
6417 void finish(int r) override {
6418 mdcache->_truncate_inode(in, ls);
6419 }
6420 };
6421
6422 void MDCache::truncate_inode(CInode *in, LogSegment *ls)
6423 {
6424 const auto& pi = in->get_projected_inode();
6425 dout(10) << "truncate_inode "
6426 << pi->truncate_from << " -> " << pi->truncate_size
6427 << " on " << *in
6428 << dendl;
6429
6430 ls->truncating_inodes.insert(in);
6431 in->get(CInode::PIN_TRUNCATING);
6432 in->auth_pin(this);
6433
6434 if (!in->client_need_snapflush.empty() &&
6435 (in->get_caps_issued() & CEPH_CAP_FILE_BUFFER)) {
6436 ceph_assert(in->filelock.is_xlocked());
6437 in->filelock.set_xlock_snap_sync(new C_MDC_RetryTruncate(this, in, ls));
6438 mds->locker->issue_caps(in);
6439 return;
6440 }
6441
6442 _truncate_inode(in, ls);
6443 }
6444
6445 struct C_IO_MDC_TruncateFinish : public MDCacheIOContext {
6446 CInode *in;
6447 LogSegment *ls;
6448 C_IO_MDC_TruncateFinish(MDCache *c, CInode *i, LogSegment *l) :
6449 MDCacheIOContext(c, false), in(i), ls(l) {
6450 }
6451 void finish(int r) override {
6452 ceph_assert(r == 0 || r == -CEPHFS_ENOENT);
6453 mdcache->truncate_inode_finish(in, ls);
6454 }
6455 void print(ostream& out) const override {
6456 out << "file_truncate(" << in->ino() << ")";
6457 }
6458 };
6459
6460 void MDCache::_truncate_inode(CInode *in, LogSegment *ls)
6461 {
6462 const auto& pi = in->get_inode();
6463 dout(10) << "_truncate_inode "
6464 << pi->truncate_from << " -> " << pi->truncate_size
6465 << " on " << *in << dendl;
6466
6467 ceph_assert(pi->is_truncating());
6468 ceph_assert(pi->truncate_size < (1ULL << 63));
6469 ceph_assert(pi->truncate_from < (1ULL << 63));
6470 ceph_assert(pi->truncate_size < pi->truncate_from);
6471
6472
6473 SnapRealm *realm = in->find_snaprealm();
6474 SnapContext nullsnap;
6475 const SnapContext *snapc;
6476 if (realm) {
6477 dout(10) << " realm " << *realm << dendl;
6478 snapc = &realm->get_snap_context();
6479 } else {
6480 dout(10) << " NO realm, using null context" << dendl;
6481 snapc = &nullsnap;
6482 ceph_assert(in->last == CEPH_NOSNAP);
6483 }
6484 dout(10) << "_truncate_inode snapc " << snapc << " on " << *in << dendl;
6485 auto layout = pi->layout;
6486 filer.truncate(in->ino(), &layout, *snapc,
6487 pi->truncate_size, pi->truncate_from-pi->truncate_size,
6488 pi->truncate_seq, ceph::real_time::min(), 0,
6489 new C_OnFinisher(new C_IO_MDC_TruncateFinish(this, in, ls),
6490 mds->finisher));
6491 }
6492
6493 struct C_MDC_TruncateLogged : public MDCacheLogContext {
6494 CInode *in;
6495 MutationRef mut;
6496 C_MDC_TruncateLogged(MDCache *m, CInode *i, MutationRef& mu) :
6497 MDCacheLogContext(m), in(i), mut(mu) {}
6498 void finish(int r) override {
6499 mdcache->truncate_inode_logged(in, mut);
6500 }
6501 };
6502
6503 void MDCache::truncate_inode_finish(CInode *in, LogSegment *ls)
6504 {
6505 dout(10) << "truncate_inode_finish " << *in << dendl;
6506
6507 set<CInode*>::iterator p = ls->truncating_inodes.find(in);
6508 ceph_assert(p != ls->truncating_inodes.end());
6509 ls->truncating_inodes.erase(p);
6510
6511 MutationRef mut(new MutationImpl());
6512 mut->ls = mds->mdlog->get_current_segment();
6513
6514 // update
6515 auto pi = in->project_inode(mut);
6516 pi.inode->version = in->pre_dirty();
6517 pi.inode->truncate_from = 0;
6518 pi.inode->truncate_pending--;
6519
6520 EUpdate *le = new EUpdate(mds->mdlog, "truncate finish");
6521 mds->mdlog->start_entry(le);
6522
6523 predirty_journal_parents(mut, &le->metablob, in, 0, PREDIRTY_PRIMARY);
6524 journal_dirty_inode(mut.get(), &le->metablob, in);
6525 le->metablob.add_truncate_finish(in->ino(), ls->seq);
6526 mds->mdlog->submit_entry(le, new C_MDC_TruncateLogged(this, in, mut));
6527
6528 // flush immediately if there are readers/writers waiting
6529 if (in->is_waiter_for(CInode::WAIT_TRUNC) ||
6530 (in->get_caps_wanted() & (CEPH_CAP_FILE_RD|CEPH_CAP_FILE_WR)))
6531 mds->mdlog->flush();
6532 }
6533
6534 void MDCache::truncate_inode_logged(CInode *in, MutationRef& mut)
6535 {
6536 dout(10) << "truncate_inode_logged " << *in << dendl;
6537 mut->apply();
6538 mds->locker->drop_locks(mut.get());
6539 mut->cleanup();
6540
6541 in->put(CInode::PIN_TRUNCATING);
6542 in->auth_unpin(this);
6543
6544 MDSContext::vec waiters;
6545 in->take_waiting(CInode::WAIT_TRUNC, waiters);
6546 mds->queue_waiters(waiters);
6547 }
6548
6549
6550 void MDCache::add_recovered_truncate(CInode *in, LogSegment *ls)
6551 {
6552 dout(20) << "add_recovered_truncate " << *in << " in log segment "
6553 << ls->seq << "/" << ls->offset << dendl;
6554 ls->truncating_inodes.insert(in);
6555 in->get(CInode::PIN_TRUNCATING);
6556 }
6557
6558 void MDCache::remove_recovered_truncate(CInode *in, LogSegment *ls)
6559 {
6560 dout(20) << "remove_recovered_truncate " << *in << " in log segment "
6561 << ls->seq << "/" << ls->offset << dendl;
6562 // if we have the logseg the truncate started in, it must be in our list.
6563 set<CInode*>::iterator p = ls->truncating_inodes.find(in);
6564 ceph_assert(p != ls->truncating_inodes.end());
6565 ls->truncating_inodes.erase(p);
6566 in->put(CInode::PIN_TRUNCATING);
6567 }
6568
6569 void MDCache::start_recovered_truncates()
6570 {
6571 dout(10) << "start_recovered_truncates" << dendl;
6572 for (map<uint64_t,LogSegment*>::iterator p = mds->mdlog->segments.begin();
6573 p != mds->mdlog->segments.end();
6574 ++p) {
6575 LogSegment *ls = p->second;
6576 for (set<CInode*>::iterator q = ls->truncating_inodes.begin();
6577 q != ls->truncating_inodes.end();
6578 ++q) {
6579 CInode *in = *q;
6580 in->auth_pin(this);
6581
6582 if (!in->client_need_snapflush.empty() &&
6583 (in->get_caps_issued() & CEPH_CAP_FILE_BUFFER)) {
6584 ceph_assert(in->filelock.is_stable());
6585 in->filelock.set_state(LOCK_XLOCKDONE);
6586 in->auth_pin(&in->filelock);
6587 in->filelock.set_xlock_snap_sync(new C_MDC_RetryTruncate(this, in, ls));
6588 // start_files_to_recover will revoke caps
6589 continue;
6590 }
6591 _truncate_inode(in, ls);
6592 }
6593 }
6594 }
6595
6596
6597 class C_MDS_purge_completed_finish : public MDCacheLogContext {
6598 interval_set<inodeno_t> inos;
6599 LogSegment *ls;
6600 version_t inotablev;
6601 public:
6602 C_MDS_purge_completed_finish(MDCache *m, const interval_set<inodeno_t>& _inos,
6603 LogSegment *_ls, version_t iv)
6604 : MDCacheLogContext(m), inos(_inos), ls(_ls), inotablev(iv) {}
6605 void finish(int r) override {
6606 ceph_assert(r == 0);
6607 if (inotablev) {
6608 get_mds()->inotable->apply_release_ids(inos);
6609 ceph_assert(get_mds()->inotable->get_version() == inotablev);
6610 }
6611 ls->purge_inodes_finish(inos);
6612 }
6613 };
6614
6615 void MDCache::start_purge_inodes(){
6616 dout(10) << "start_purge_inodes" << dendl;
6617 for (auto& p : mds->mdlog->segments){
6618 LogSegment *ls = p.second;
6619 if (ls->purging_inodes.size()){
6620 purge_inodes(ls->purging_inodes, ls);
6621 }
6622 }
6623 }
6624
6625 void MDCache::purge_inodes(const interval_set<inodeno_t>& inos, LogSegment *ls)
6626 {
6627 dout(10) << __func__ << " purging inos " << inos << " logseg " << ls->seq << dendl;
6628 // FIXME: handle non-default data pool and namespace
6629
6630 auto cb = new LambdaContext([this, inos, ls](int r){
6631 ceph_assert(r == 0 || r == -2);
6632 mds->inotable->project_release_ids(inos);
6633 version_t piv = mds->inotable->get_projected_version();
6634 ceph_assert(piv != 0);
6635 mds->mdlog->start_submit_entry(new EPurged(inos, ls->seq, piv),
6636 new C_MDS_purge_completed_finish(this, inos, ls, piv));
6637 mds->mdlog->flush();
6638 });
6639
6640 C_GatherBuilder gather(g_ceph_context,
6641 new C_OnFinisher(new MDSIOContextWrapper(mds, cb), mds->finisher));
6642 SnapContext nullsnapc;
6643 for (const auto& [start, len] : inos) {
6644 for (auto i = start; i < start + len ; i += 1) {
6645 filer.purge_range(i, &default_file_layout, nullsnapc, 0, 1,
6646 ceph::real_clock::now(), 0, gather.new_sub());
6647 }
6648 }
6649 gather.activate();
6650 }
6651
6652 // ================================================================================
6653 // cache trimming
6654
6655 std::pair<bool, uint64_t> MDCache::trim_lru(uint64_t count, expiremap& expiremap)
6656 {
6657 bool is_standby_replay = mds->is_standby_replay();
6658 std::vector<CDentry *> unexpirables;
6659 uint64_t trimmed = 0;
6660
6661 auto trim_threshold = g_conf().get_val<Option::size_t>("mds_cache_trim_threshold");
6662
6663 dout(7) << "trim_lru trimming " << count
6664 << " items from LRU"
6665 << " size=" << lru.lru_get_size()
6666 << " mid=" << lru.lru_get_top()
6667 << " pintail=" << lru.lru_get_pintail()
6668 << " pinned=" << lru.lru_get_num_pinned()
6669 << dendl;
6670
6671 const uint64_t trim_counter_start = trim_counter.get();
6672 bool throttled = false;
6673 while (1) {
6674 throttled |= trim_counter_start+trimmed >= trim_threshold;
6675 if (throttled) break;
6676 CDentry *dn = static_cast<CDentry*>(bottom_lru.lru_expire());
6677 if (!dn)
6678 break;
6679 if (trim_dentry(dn, expiremap)) {
6680 unexpirables.push_back(dn);
6681 } else {
6682 trimmed++;
6683 }
6684 }
6685
6686 for (auto &dn : unexpirables) {
6687 bottom_lru.lru_insert_mid(dn);
6688 }
6689 unexpirables.clear();
6690
6691 // trim dentries from the LRU until count is reached
6692 // if mds is in standby_replay and skip trimming the inodes
6693 while (!throttled && (cache_toofull() || count > 0 || is_standby_replay)) {
6694 throttled |= trim_counter_start+trimmed >= trim_threshold;
6695 if (throttled) break;
6696 CDentry *dn = static_cast<CDentry*>(lru.lru_expire());
6697 if (!dn) {
6698 break;
6699 }
6700 if (is_standby_replay && dn->get_linkage()->inode) {
6701 // we move the inodes that need to be trimmed to the end of the lru queue.
6702 // refer to MDCache::standby_trim_segment
6703 lru.lru_insert_bot(dn);
6704 break;
6705 } else if (trim_dentry(dn, expiremap)) {
6706 unexpirables.push_back(dn);
6707 } else {
6708 trimmed++;
6709 if (count > 0) count--;
6710 }
6711 }
6712 trim_counter.hit(trimmed);
6713
6714 for (auto &dn : unexpirables) {
6715 lru.lru_insert_mid(dn);
6716 }
6717 unexpirables.clear();
6718
6719 dout(7) << "trim_lru trimmed " << trimmed << " items" << dendl;
6720 return std::pair<bool, uint64_t>(throttled, trimmed);
6721 }
6722
6723 /*
6724 * note: only called while MDS is active or stopping... NOT during recovery.
6725 * however, we may expire a replica whose authority is recovering.
6726 *
6727 * @param count is number of dentries to try to expire
6728 */
6729 std::pair<bool, uint64_t> MDCache::trim(uint64_t count)
6730 {
6731 uint64_t used = cache_size();
6732 uint64_t limit = cache_memory_limit;
6733 expiremap expiremap;
6734
6735 dout(7) << "trim bytes_used=" << bytes2str(used)
6736 << " limit=" << bytes2str(limit)
6737 << " reservation=" << cache_reservation
6738 << "% count=" << count << dendl;
6739
6740 // process delayed eval_stray()
6741 stray_manager.advance_delayed();
6742
6743 auto result = trim_lru(count, expiremap);
6744 auto& trimmed = result.second;
6745
6746 // trim non-auth, non-bound subtrees
6747 for (auto p = subtrees.begin(); p != subtrees.end();) {
6748 CDir *dir = p->first;
6749 ++p;
6750 CInode *diri = dir->get_inode();
6751 if (dir->is_auth()) {
6752 if (diri->is_auth() && !diri->is_base()) {
6753 /* this situation should correspond to an export pin */
6754 if (dir->get_num_head_items() == 0 && dir->get_num_ref() == 1) {
6755 /* pinned empty subtree, try to drop */
6756 if (dir->state_test(CDir::STATE_AUXSUBTREE)) {
6757 dout(20) << "trimming empty pinned subtree " << *dir << dendl;
6758 dir->state_clear(CDir::STATE_AUXSUBTREE);
6759 remove_subtree(dir);
6760 diri->close_dirfrag(dir->dirfrag().frag);
6761 }
6762 }
6763 } else if (!diri->is_auth() && !diri->is_base() && dir->get_num_head_items() == 0) {
6764 if (dir->state_test(CDir::STATE_EXPORTING) ||
6765 !(mds->is_active() || mds->is_stopping()) ||
6766 dir->is_freezing() || dir->is_frozen())
6767 continue;
6768
6769 migrator->export_empty_import(dir);
6770 ++trimmed;
6771 }
6772 } else if (!diri->is_auth() && dir->get_num_ref() <= 1) {
6773 // only subtree pin
6774 if (diri->get_num_ref() > diri->get_num_subtree_roots()) {
6775 continue;
6776 }
6777
6778 // don't trim subtree root if its auth MDS is recovering.
6779 // This simplify the cache rejoin code.
6780 if (dir->is_subtree_root() && rejoin_ack_gather.count(dir->get_dir_auth().first))
6781 continue;
6782 trim_dirfrag(dir, 0, expiremap);
6783 ++trimmed;
6784 }
6785 }
6786
6787 // trim root?
6788 if (mds->is_stopping() && root) {
6789 auto&& ls = root->get_dirfrags();
6790 for (const auto& dir : ls) {
6791 if (dir->get_num_ref() == 1) { // subtree pin
6792 trim_dirfrag(dir, 0, expiremap);
6793 ++trimmed;
6794 }
6795 }
6796 if (root->get_num_ref() == 0) {
6797 trim_inode(0, root, 0, expiremap);
6798 ++trimmed;
6799 }
6800 }
6801
6802 std::set<mds_rank_t> stopping;
6803 mds->mdsmap->get_mds_set(stopping, MDSMap::STATE_STOPPING);
6804 stopping.erase(mds->get_nodeid());
6805 for (auto rank : stopping) {
6806 CInode* mdsdir_in = get_inode(MDS_INO_MDSDIR(rank));
6807 if (!mdsdir_in)
6808 continue;
6809
6810 auto em = expiremap.emplace(std::piecewise_construct, std::forward_as_tuple(rank), std::forward_as_tuple());
6811 if (em.second) {
6812 em.first->second = make_message<MCacheExpire>(mds->get_nodeid());
6813 }
6814
6815 dout(20) << __func__ << ": try expiring " << *mdsdir_in << " for stopping mds." << mds->get_nodeid() << dendl;
6816
6817 const bool aborted = expire_recursive(mdsdir_in, expiremap);
6818 if (!aborted) {
6819 dout(20) << __func__ << ": successfully expired mdsdir" << dendl;
6820 auto&& ls = mdsdir_in->get_dirfrags();
6821 for (auto dir : ls) {
6822 if (dir->get_num_ref() == 1) { // subtree pin
6823 trim_dirfrag(dir, dir, expiremap);
6824 ++trimmed;
6825 }
6826 }
6827 if (mdsdir_in->get_num_ref() == 0) {
6828 trim_inode(NULL, mdsdir_in, NULL, expiremap);
6829 ++trimmed;
6830 }
6831 } else {
6832 dout(20) << __func__ << ": some unexpirable contents in mdsdir" << dendl;
6833 }
6834 }
6835
6836 // Other rank's base inodes (when I'm stopping)
6837 if (mds->is_stopping()) {
6838 for (set<CInode*>::iterator p = base_inodes.begin();
6839 p != base_inodes.end();) {
6840 CInode *base_in = *p;
6841 ++p;
6842 if (MDS_INO_IS_MDSDIR(base_in->ino()) &&
6843 MDS_INO_MDSDIR_OWNER(base_in->ino()) != mds->get_nodeid()) {
6844 dout(20) << __func__ << ": maybe trimming base: " << *base_in << dendl;
6845 if (base_in->get_num_ref() == 0) {
6846 trim_inode(NULL, base_in, NULL, expiremap);
6847 ++trimmed;
6848 }
6849 }
6850 }
6851 }
6852
6853 // send any expire messages
6854 send_expire_messages(expiremap);
6855
6856 return result;
6857 }
6858
6859 void MDCache::send_expire_messages(expiremap& expiremap)
6860 {
6861 // send expires
6862 for (const auto &p : expiremap) {
6863 if (mds->is_cluster_degraded() &&
6864 (mds->mdsmap->get_state(p.first) < MDSMap::STATE_REJOIN ||
6865 (mds->mdsmap->get_state(p.first) == MDSMap::STATE_REJOIN &&
6866 rejoin_sent.count(p.first) == 0))) {
6867 continue;
6868 }
6869 dout(7) << "sending cache_expire to " << p.first << dendl;
6870 mds->send_message_mds(p.second, p.first);
6871 }
6872 expiremap.clear();
6873 }
6874
6875
6876 bool MDCache::trim_dentry(CDentry *dn, expiremap& expiremap)
6877 {
6878 dout(12) << "trim_dentry " << *dn << dendl;
6879
6880 CDentry::linkage_t *dnl = dn->get_linkage();
6881
6882 CDir *dir = dn->get_dir();
6883 ceph_assert(dir);
6884
6885 CDir *con = get_subtree_root(dir);
6886 if (con)
6887 dout(12) << " in container " << *con << dendl;
6888 else {
6889 dout(12) << " no container; under a not-yet-linked dir" << dendl;
6890 ceph_assert(dn->is_auth());
6891 }
6892
6893 // If replica dentry is not readable, it's likely we will receive
6894 // MDentryLink/MDentryUnlink message soon (It's possible we first
6895 // receive a MDentryUnlink message, then MDentryLink message)
6896 // MDentryLink message only replicates an inode, so we should
6897 // avoid trimming the inode's parent dentry. This is because that
6898 // unconnected replicas are problematic for subtree migration.
6899 if (!dn->is_auth() && !dn->lock.can_read(-1) &&
6900 !dn->get_dir()->get_inode()->is_stray())
6901 return true;
6902
6903 // adjust the dir state
6904 // NOTE: we can safely remove a clean, null dentry without effecting
6905 // directory completeness.
6906 // (check this _before_ we unlink the inode, below!)
6907 bool clear_complete = false;
6908 if (!(dnl->is_null() && dn->is_clean()))
6909 clear_complete = true;
6910
6911 // unlink the dentry
6912 if (dnl->is_remote()) {
6913 // just unlink.
6914 dir->unlink_inode(dn, false);
6915 } else if (dnl->is_primary()) {
6916 // expire the inode, too.
6917 CInode *in = dnl->get_inode();
6918 ceph_assert(in);
6919 if (trim_inode(dn, in, con, expiremap))
6920 return true; // purging stray instead of trimming
6921 } else {
6922 ceph_assert(dnl->is_null());
6923 }
6924
6925 if (!dn->is_auth()) {
6926 // notify dentry authority.
6927 mds_authority_t auth = dn->authority();
6928
6929 for (int p=0; p<2; p++) {
6930 mds_rank_t a = auth.first;
6931 if (p) a = auth.second;
6932 if (a < 0 || (p == 1 && auth.second == auth.first)) break;
6933 if (mds->get_nodeid() == auth.second &&
6934 con->is_importing()) break; // don't send any expire while importing.
6935 if (a == mds->get_nodeid()) continue; // on export, ignore myself.
6936
6937 dout(12) << " sending expire to mds." << a << " on " << *dn << dendl;
6938 ceph_assert(a != mds->get_nodeid());
6939 auto em = expiremap.emplace(std::piecewise_construct, std::forward_as_tuple(a), std::forward_as_tuple());
6940 if (em.second)
6941 em.first->second = make_message<MCacheExpire>(mds->get_nodeid());
6942 em.first->second->add_dentry(con->dirfrag(), dir->dirfrag(), dn->get_name(), dn->last, dn->get_replica_nonce());
6943 }
6944 }
6945
6946 // remove dentry
6947 if (dn->last == CEPH_NOSNAP && dir->is_auth())
6948 dir->add_to_bloom(dn);
6949 dir->remove_dentry(dn);
6950
6951 if (clear_complete)
6952 dir->state_clear(CDir::STATE_COMPLETE);
6953
6954 if (mds->logger) mds->logger->inc(l_mds_inodes_expired);
6955 return false;
6956 }
6957
6958
6959 void MDCache::trim_dirfrag(CDir *dir, CDir *con, expiremap& expiremap)
6960 {
6961 dout(15) << "trim_dirfrag " << *dir << dendl;
6962
6963 if (dir->is_subtree_root()) {
6964 ceph_assert(!dir->is_auth() ||
6965 (!dir->is_replicated() && dir->inode->is_base()));
6966 remove_subtree(dir); // remove from subtree map
6967 }
6968 ceph_assert(dir->get_num_ref() == 0);
6969
6970 CInode *in = dir->get_inode();
6971
6972 if (!dir->is_auth()) {
6973 mds_authority_t auth = dir->authority();
6974
6975 // was this an auth delegation? (if so, slightly modified container)
6976 dirfrag_t condf;
6977 if (dir->is_subtree_root()) {
6978 dout(12) << " subtree root, container is " << *dir << dendl;
6979 con = dir;
6980 condf = dir->dirfrag();
6981 } else {
6982 condf = con->dirfrag();
6983 }
6984
6985 for (int p=0; p<2; p++) {
6986 mds_rank_t a = auth.first;
6987 if (p) a = auth.second;
6988 if (a < 0 || (p == 1 && auth.second == auth.first)) break;
6989 if (mds->get_nodeid() == auth.second &&
6990 con->is_importing()) break; // don't send any expire while importing.
6991 if (a == mds->get_nodeid()) continue; // on export, ignore myself.
6992
6993 dout(12) << " sending expire to mds." << a << " on " << *dir << dendl;
6994 ceph_assert(a != mds->get_nodeid());
6995 auto em = expiremap.emplace(std::piecewise_construct, std::forward_as_tuple(a), std::forward_as_tuple());
6996 if (em.second)
6997 em.first->second = make_message<MCacheExpire>(mds->get_nodeid()); /* new */
6998 em.first->second->add_dir(condf, dir->dirfrag(), dir->replica_nonce);
6999 }
7000 }
7001
7002 in->close_dirfrag(dir->dirfrag().frag);
7003 }
7004
7005 /**
7006 * Try trimming an inode from the cache
7007 *
7008 * @return true if the inode is still in cache, else false if it was trimmed
7009 */
7010 bool MDCache::trim_inode(CDentry *dn, CInode *in, CDir *con, expiremap& expiremap)
7011 {
7012 dout(15) << "trim_inode " << *in << dendl;
7013 ceph_assert(in->get_num_ref() == 0);
7014
7015 if (in->is_dir()) {
7016 // If replica inode's dirfragtreelock is not readable, it's likely
7017 // some dirfrags of the inode are being fragmented and we will receive
7018 // MMDSFragmentNotify soon. MMDSFragmentNotify only replicates the new
7019 // dirfrags, so we should avoid trimming these dirfrags' parent inode.
7020 // This is because that unconnected replicas are problematic for
7021 // subtree migration.
7022 //
7023 if (!in->is_auth() && !mds->locker->rdlock_try(&in->dirfragtreelock, -1)) {
7024 return true;
7025 }
7026
7027 // DIR
7028 auto&& dfls = in->get_dirfrags();
7029 for (const auto& dir : dfls) {
7030 ceph_assert(!dir->is_subtree_root());
7031 trim_dirfrag(dir, con ? con:dir, expiremap); // if no container (e.g. root dirfrag), use *p
7032 }
7033 }
7034
7035 // INODE
7036 if (in->is_auth()) {
7037 // eval stray after closing dirfrags
7038 if (dn && !dn->state_test(CDentry::STATE_PURGING)) {
7039 maybe_eval_stray(in);
7040 if (dn->state_test(CDentry::STATE_PURGING) || dn->get_num_ref() > 0)
7041 return true;
7042 }
7043 } else {
7044 mds_authority_t auth = in->authority();
7045
7046 dirfrag_t df;
7047 if (con)
7048 df = con->dirfrag();
7049 else
7050 df = dirfrag_t(0,frag_t()); // must be a root or stray inode.
7051
7052 for (int p=0; p<2; p++) {
7053 mds_rank_t a = auth.first;
7054 if (p) a = auth.second;
7055 if (a < 0 || (p == 1 && auth.second == auth.first)) break;
7056 if (con && mds->get_nodeid() == auth.second &&
7057 con->is_importing()) break; // don't send any expire while importing.
7058 if (a == mds->get_nodeid()) continue; // on export, ignore myself.
7059
7060 dout(12) << " sending expire to mds." << a << " on " << *in << dendl;
7061 ceph_assert(a != mds->get_nodeid());
7062 auto em = expiremap.emplace(std::piecewise_construct, std::forward_as_tuple(a), std::forward_as_tuple());
7063 if (em.second)
7064 em.first->second = make_message<MCacheExpire>(mds->get_nodeid()); /* new */
7065 em.first->second->add_inode(df, in->vino(), in->get_replica_nonce());
7066 }
7067 }
7068
7069 /*
7070 if (in->is_auth()) {
7071 if (in->hack_accessed)
7072 mds->logger->inc("outt");
7073 else {
7074 mds->logger->inc("outut");
7075 mds->logger->fset("oututl", ceph_clock_now() - in->hack_load_stamp);
7076 }
7077 }
7078 */
7079
7080 // unlink
7081 if (dn)
7082 dn->get_dir()->unlink_inode(dn, false);
7083 remove_inode(in);
7084 return false;
7085 }
7086
7087
7088 /**
7089 * trim_non_auth - remove any non-auth items from our cache
7090 *
7091 * this reduces the amount of non-auth metadata in our cache, reducing the
7092 * load incurred by the rejoin phase.
7093 *
7094 * the only non-auth items that remain are those that are needed to
7095 * attach our own subtrees to the root.
7096 *
7097 * when we are done, all dentries will be in the top bit of the lru.
7098 *
7099 * why we have to do this:
7100 * we may not have accurate linkage for non-auth items. which means we will
7101 * know which subtree it falls into, and can not be sure to declare it to the
7102 * correct authority.
7103 */
7104 void MDCache::trim_non_auth()
7105 {
7106 dout(7) << "trim_non_auth" << dendl;
7107
7108 // temporarily pin all subtree roots
7109 for (map<CDir*, set<CDir*> >::iterator p = subtrees.begin();
7110 p != subtrees.end();
7111 ++p)
7112 p->first->get(CDir::PIN_SUBTREETEMP);
7113
7114 list<CDentry*> auth_list;
7115
7116 // trim non-auth items from the lru
7117 for (;;) {
7118 CDentry *dn = NULL;
7119 if (bottom_lru.lru_get_size() > 0)
7120 dn = static_cast<CDentry*>(bottom_lru.lru_expire());
7121 if (!dn && lru.lru_get_size() > 0)
7122 dn = static_cast<CDentry*>(lru.lru_expire());
7123 if (!dn)
7124 break;
7125
7126 CDentry::linkage_t *dnl = dn->get_linkage();
7127
7128 if (dn->is_auth()) {
7129 // add back into lru (at the top)
7130 auth_list.push_back(dn);
7131
7132 if (dnl->is_remote() && dnl->get_inode() && !dnl->get_inode()->is_auth())
7133 dn->unlink_remote(dnl);
7134 } else {
7135 // non-auth. expire.
7136 CDir *dir = dn->get_dir();
7137 ceph_assert(dir);
7138
7139 // unlink the dentry
7140 dout(10) << " removing " << *dn << dendl;
7141 if (dnl->is_remote()) {
7142 dir->unlink_inode(dn, false);
7143 }
7144 else if (dnl->is_primary()) {
7145 CInode *in = dnl->get_inode();
7146 dout(10) << " removing " << *in << dendl;
7147 auto&& ls = in->get_dirfrags();
7148 for (const auto& subdir : ls) {
7149 ceph_assert(!subdir->is_subtree_root());
7150 in->close_dirfrag(subdir->dirfrag().frag);
7151 }
7152 dir->unlink_inode(dn, false);
7153 remove_inode(in);
7154 }
7155 else {
7156 ceph_assert(dnl->is_null());
7157 }
7158
7159 ceph_assert(!dir->has_bloom());
7160 dir->remove_dentry(dn);
7161 // adjust the dir state
7162 dir->state_clear(CDir::STATE_COMPLETE); // dir incomplete!
7163 // close empty non-auth dirfrag
7164 if (!dir->is_subtree_root() && dir->get_num_any() == 0)
7165 dir->inode->close_dirfrag(dir->get_frag());
7166 }
7167 }
7168
7169 for (const auto& dn : auth_list) {
7170 if (dn->state_test(CDentry::STATE_BOTTOMLRU))
7171 bottom_lru.lru_insert_mid(dn);
7172 else
7173 lru.lru_insert_top(dn);
7174 }
7175
7176 // move everything in the pintail to the top bit of the lru.
7177 lru.lru_touch_entire_pintail();
7178
7179 // unpin all subtrees
7180 for (map<CDir*, set<CDir*> >::iterator p = subtrees.begin();
7181 p != subtrees.end();
7182 ++p)
7183 p->first->put(CDir::PIN_SUBTREETEMP);
7184
7185 if (lru.lru_get_size() == 0 &&
7186 bottom_lru.lru_get_size() == 0) {
7187 // root, stray, etc.?
7188 auto p = inode_map.begin();
7189 while (p != inode_map.end()) {
7190 CInode *in = p->second;
7191 ++p;
7192 if (!in->is_auth()) {
7193 auto&& ls = in->get_dirfrags();
7194 for (const auto& dir : ls) {
7195 dout(10) << " removing " << *dir << dendl;
7196 ceph_assert(dir->get_num_ref() == 1); // SUBTREE
7197 remove_subtree(dir);
7198 in->close_dirfrag(dir->dirfrag().frag);
7199 }
7200 dout(10) << " removing " << *in << dendl;
7201 ceph_assert(!in->get_parent_dn());
7202 ceph_assert(in->get_num_ref() == 0);
7203 remove_inode(in);
7204 }
7205 }
7206 }
7207
7208 show_subtrees();
7209 }
7210
7211 /**
7212 * Recursively trim the subtree rooted at directory to remove all
7213 * CInodes/CDentrys/CDirs that aren't links to remote MDSes, or ancestors
7214 * of those links. This is used to clear invalid data out of the cache.
7215 * Note that it doesn't clear the passed-in directory, since that's not
7216 * always safe.
7217 */
7218 bool MDCache::trim_non_auth_subtree(CDir *dir)
7219 {
7220 dout(10) << "trim_non_auth_subtree(" << dir << ") " << *dir << dendl;
7221
7222 bool keep_dir = !can_trim_non_auth_dirfrag(dir);
7223
7224 auto j = dir->begin();
7225 auto i = j;
7226 while (j != dir->end()) {
7227 i = j++;
7228 CDentry *dn = i->second;
7229 dout(10) << "trim_non_auth_subtree(" << dir << ") Checking dentry " << dn << dendl;
7230 CDentry::linkage_t *dnl = dn->get_linkage();
7231 if (dnl->is_primary()) { // check for subdirectories, etc
7232 CInode *in = dnl->get_inode();
7233 bool keep_inode = false;
7234 if (in->is_dir()) {
7235 auto&& subdirs = in->get_dirfrags();
7236 for (const auto& subdir : subdirs) {
7237 if (subdir->is_subtree_root()) {
7238 keep_inode = true;
7239 dout(10) << "trim_non_auth_subtree(" << dir << ") keeping " << *subdir << dendl;
7240 } else {
7241 if (trim_non_auth_subtree(subdir))
7242 keep_inode = true;
7243 else {
7244 in->close_dirfrag(subdir->get_frag());
7245 dir->state_clear(CDir::STATE_COMPLETE); // now incomplete!
7246 }
7247 }
7248 }
7249
7250 }
7251 if (!keep_inode) { // remove it!
7252 dout(20) << "trim_non_auth_subtree(" << dir << ") removing inode " << in << " with dentry" << dn << dendl;
7253 dir->unlink_inode(dn, false);
7254 remove_inode(in);
7255 ceph_assert(!dir->has_bloom());
7256 dir->remove_dentry(dn);
7257 } else {
7258 dout(20) << "trim_non_auth_subtree(" << dir << ") keeping inode " << in << " with dentry " << dn <<dendl;
7259 dn->state_clear(CDentry::STATE_AUTH);
7260 in->state_clear(CInode::STATE_AUTH);
7261 }
7262 } else if (keep_dir && dnl->is_null()) { // keep null dentry for peer rollback
7263 dout(20) << "trim_non_auth_subtree(" << dir << ") keeping dentry " << dn <<dendl;
7264 } else { // just remove it
7265 dout(20) << "trim_non_auth_subtree(" << dir << ") removing dentry " << dn << dendl;
7266 if (dnl->is_remote())
7267 dir->unlink_inode(dn, false);
7268 dir->remove_dentry(dn);
7269 }
7270 }
7271 dir->state_clear(CDir::STATE_AUTH);
7272 /**
7273 * We've now checked all our children and deleted those that need it.
7274 * Now return to caller, and tell them if *we're* a keeper.
7275 */
7276 return keep_dir || dir->get_num_any();
7277 }
7278
7279 /*
7280 * during replay, when we determine a subtree is no longer ours, we
7281 * try to trim it from our cache. because subtrees must be connected
7282 * to the root, the fact that we can trim this tree may mean that our
7283 * children or parents can also be trimmed.
7284 */
7285 void MDCache::try_trim_non_auth_subtree(CDir *dir)
7286 {
7287 dout(10) << "try_trim_nonauth_subtree " << *dir << dendl;
7288
7289 // can we now trim child subtrees?
7290 set<CDir*> bounds;
7291 get_subtree_bounds(dir, bounds);
7292 for (set<CDir*>::iterator p = bounds.begin(); p != bounds.end(); ++p) {
7293 CDir *bd = *p;
7294 if (bd->get_dir_auth().first != mds->get_nodeid() && // we are not auth
7295 bd->get_num_any() == 0 && // and empty
7296 can_trim_non_auth_dirfrag(bd)) {
7297 CInode *bi = bd->get_inode();
7298 dout(10) << " closing empty non-auth child subtree " << *bd << dendl;
7299 remove_subtree(bd);
7300 bd->mark_clean();
7301 bi->close_dirfrag(bd->get_frag());
7302 }
7303 }
7304
7305 if (trim_non_auth_subtree(dir)) {
7306 // keep
7307 try_subtree_merge(dir);
7308 } else {
7309 // can we trim this subtree (and possibly our ancestors) too?
7310 while (true) {
7311 CInode *diri = dir->get_inode();
7312 if (diri->is_base()) {
7313 if (!diri->is_root() && diri->authority().first != mds->get_nodeid()) {
7314 dout(10) << " closing empty non-auth subtree " << *dir << dendl;
7315 remove_subtree(dir);
7316 dir->mark_clean();
7317 diri->close_dirfrag(dir->get_frag());
7318
7319 dout(10) << " removing " << *diri << dendl;
7320 ceph_assert(!diri->get_parent_dn());
7321 ceph_assert(diri->get_num_ref() == 0);
7322 remove_inode(diri);
7323 }
7324 break;
7325 }
7326
7327 CDir *psub = get_subtree_root(diri->get_parent_dir());
7328 dout(10) << " parent subtree is " << *psub << dendl;
7329 if (psub->get_dir_auth().first == mds->get_nodeid())
7330 break; // we are auth, keep.
7331
7332 dout(10) << " closing empty non-auth subtree " << *dir << dendl;
7333 remove_subtree(dir);
7334 dir->mark_clean();
7335 diri->close_dirfrag(dir->get_frag());
7336
7337 dout(10) << " parent subtree also non-auth: " << *psub << dendl;
7338 if (trim_non_auth_subtree(psub))
7339 break;
7340 dir = psub;
7341 }
7342 }
7343
7344 show_subtrees();
7345 }
7346
7347 void MDCache::standby_trim_segment(LogSegment *ls)
7348 {
7349 auto try_trim_inode = [this](CInode *in) {
7350 if (in->get_num_ref() == 0 &&
7351 !in->item_open_file.is_on_list() &&
7352 in->parent != NULL &&
7353 in->parent->get_num_ref() == 0){
7354 touch_dentry_bottom(in->parent);
7355 }
7356 };
7357
7358 auto try_trim_dentry = [this](CDentry *dn) {
7359 if (dn->get_num_ref() > 0)
7360 return;
7361 auto in = dn->get_linkage()->inode;
7362 if(in && in->item_open_file.is_on_list())
7363 return;
7364 touch_dentry_bottom(dn);
7365 };
7366
7367 ls->new_dirfrags.clear_list();
7368 ls->open_files.clear_list();
7369
7370 while (!ls->dirty_dirfrags.empty()) {
7371 CDir *dir = ls->dirty_dirfrags.front();
7372 dir->mark_clean();
7373 if (dir->inode)
7374 try_trim_inode(dir->inode);
7375 }
7376 while (!ls->dirty_inodes.empty()) {
7377 CInode *in = ls->dirty_inodes.front();
7378 in->mark_clean();
7379 try_trim_inode(in);
7380 }
7381 while (!ls->dirty_dentries.empty()) {
7382 CDentry *dn = ls->dirty_dentries.front();
7383 dn->mark_clean();
7384 try_trim_dentry(dn);
7385 }
7386 while (!ls->dirty_parent_inodes.empty()) {
7387 CInode *in = ls->dirty_parent_inodes.front();
7388 in->clear_dirty_parent();
7389 try_trim_inode(in);
7390 }
7391 while (!ls->dirty_dirfrag_dir.empty()) {
7392 CInode *in = ls->dirty_dirfrag_dir.front();
7393 in->filelock.remove_dirty();
7394 try_trim_inode(in);
7395 }
7396 while (!ls->dirty_dirfrag_nest.empty()) {
7397 CInode *in = ls->dirty_dirfrag_nest.front();
7398 in->nestlock.remove_dirty();
7399 try_trim_inode(in);
7400 }
7401 while (!ls->dirty_dirfrag_dirfragtree.empty()) {
7402 CInode *in = ls->dirty_dirfrag_dirfragtree.front();
7403 in->dirfragtreelock.remove_dirty();
7404 try_trim_inode(in);
7405 }
7406 while (!ls->truncating_inodes.empty()) {
7407 auto it = ls->truncating_inodes.begin();
7408 CInode *in = *it;
7409 ls->truncating_inodes.erase(it);
7410 in->put(CInode::PIN_TRUNCATING);
7411 try_trim_inode(in);
7412 }
7413 }
7414
7415 void MDCache::handle_cache_expire(const cref_t<MCacheExpire> &m)
7416 {
7417 mds_rank_t from = mds_rank_t(m->get_from());
7418
7419 dout(7) << "cache_expire from mds." << from << dendl;
7420
7421 if (mds->get_state() < MDSMap::STATE_REJOIN) {
7422 return;
7423 }
7424
7425 set<SimpleLock *> gather_locks;
7426 // loop over realms
7427 for (const auto &p : m->realms) {
7428 // check container?
7429 if (p.first.ino > 0) {
7430 CInode *expired_inode = get_inode(p.first.ino);
7431 ceph_assert(expired_inode); // we had better have this.
7432 CDir *parent_dir = expired_inode->get_approx_dirfrag(p.first.frag);
7433 ceph_assert(parent_dir);
7434
7435 int export_state = -1;
7436 if (parent_dir->is_auth() && parent_dir->is_exporting()) {
7437 export_state = migrator->get_export_state(parent_dir);
7438 ceph_assert(export_state >= 0);
7439 }
7440
7441 if (!parent_dir->is_auth() ||
7442 (export_state != -1 &&
7443 ((export_state == Migrator::EXPORT_WARNING &&
7444 migrator->export_has_warned(parent_dir,from)) ||
7445 export_state == Migrator::EXPORT_EXPORTING ||
7446 export_state == Migrator::EXPORT_LOGGINGFINISH ||
7447 (export_state == Migrator::EXPORT_NOTIFYING &&
7448 !migrator->export_has_notified(parent_dir,from))))) {
7449
7450 // not auth.
7451 dout(7) << "delaying nonauth|warned expires for " << *parent_dir << dendl;
7452 ceph_assert(parent_dir->is_frozen_tree_root());
7453
7454 // make a message container
7455
7456 auto em = delayed_expire[parent_dir].emplace(std::piecewise_construct, std::forward_as_tuple(from), std::forward_as_tuple());
7457 if (em.second)
7458 em.first->second = make_message<MCacheExpire>(from); /* new */
7459
7460 // merge these expires into it
7461 em.first->second->add_realm(p.first, p.second);
7462 continue;
7463 }
7464 ceph_assert(export_state <= Migrator::EXPORT_PREPPING ||
7465 (export_state == Migrator::EXPORT_WARNING &&
7466 !migrator->export_has_warned(parent_dir, from)));
7467
7468 dout(7) << "expires for " << *parent_dir << dendl;
7469 } else {
7470 dout(7) << "containerless expires (root, stray inodes)" << dendl;
7471 }
7472
7473 // INODES
7474 for (const auto &q : p.second.inodes) {
7475 CInode *in = get_inode(q.first);
7476 unsigned nonce = q.second;
7477
7478 if (!in) {
7479 dout(0) << " inode expire on " << q.first << " from " << from
7480 << ", don't have it" << dendl;
7481 ceph_assert(in);
7482 }
7483 ceph_assert(in->is_auth());
7484 dout(20) << __func__ << ": expiring inode " << *in << dendl;
7485
7486 // check nonce
7487 if (nonce == in->get_replica_nonce(from)) {
7488 // remove from our cached_by
7489 dout(7) << " inode expire on " << *in << " from mds." << from
7490 << " cached_by was " << in->get_replicas() << dendl;
7491 inode_remove_replica(in, from, false, gather_locks);
7492 }
7493 else {
7494 // this is an old nonce, ignore expire.
7495 dout(7) << " inode expire on " << *in << " from mds." << from
7496 << " with old nonce " << nonce
7497 << " (current " << in->get_replica_nonce(from) << "), dropping"
7498 << dendl;
7499 }
7500 }
7501
7502 // DIRS
7503 for (const auto &q : p.second.dirs) {
7504 CDir *dir = get_dirfrag(q.first);
7505 unsigned nonce = q.second;
7506
7507 if (!dir) {
7508 CInode *diri = get_inode(q.first.ino);
7509 if (diri) {
7510 if (mds->is_rejoin() &&
7511 rejoin_ack_gather.count(mds->get_nodeid()) && // haven't sent rejoin ack yet
7512 !diri->is_replica(from)) {
7513 auto&& ls = diri->get_nested_dirfrags();
7514 dout(7) << " dir expire on dirfrag " << q.first << " from mds." << from
7515 << " while rejoining, inode isn't replicated" << dendl;
7516 for (const auto& d : ls) {
7517 dir = d;
7518 if (dir->is_replica(from)) {
7519 dout(7) << " dir expire on " << *dir << " from mds." << from << dendl;
7520 dir->remove_replica(from);
7521 }
7522 }
7523 continue;
7524 }
7525 CDir *other = diri->get_approx_dirfrag(q.first.frag);
7526 if (other) {
7527 dout(7) << " dir expire on dirfrag " << q.first << " from mds." << from
7528 << " have " << *other << ", mismatched frags, dropping" << dendl;
7529 continue;
7530 }
7531 }
7532 dout(0) << " dir expire on " << q.first << " from " << from
7533 << ", don't have it" << dendl;
7534 ceph_assert(dir);
7535 }
7536 dout(20) << __func__ << ": expiring dirfrag " << *dir << dendl;
7537
7538 ceph_assert(dir->is_auth());
7539
7540 // check nonce
7541 if (nonce == dir->get_replica_nonce(from)) {
7542 // remove from our cached_by
7543 dout(7) << " dir expire on " << *dir << " from mds." << from
7544 << " replicas was " << dir->get_replicas() << dendl;
7545 dir->remove_replica(from);
7546 }
7547 else {
7548 // this is an old nonce, ignore expire.
7549 dout(7) << " dir expire on " << *dir << " from mds." << from
7550 << " with old nonce " << nonce << " (current " << dir->get_replica_nonce(from)
7551 << "), dropping" << dendl;
7552 }
7553 }
7554
7555 // DENTRIES
7556 for (const auto &pd : p.second.dentries) {
7557 dout(10) << " dn expires in dir " << pd.first << dendl;
7558 CInode *diri = get_inode(pd.first.ino);
7559 ceph_assert(diri);
7560 CDir *dir = diri->get_dirfrag(pd.first.frag);
7561
7562 if (!dir) {
7563 dout(0) << " dn expires on " << pd.first << " from " << from
7564 << ", must have refragmented" << dendl;
7565 } else {
7566 ceph_assert(dir->is_auth());
7567 }
7568
7569 for (const auto &p : pd.second) {
7570 unsigned nonce = p.second;
7571 CDentry *dn;
7572
7573 if (dir) {
7574 dn = dir->lookup(p.first.first, p.first.second);
7575 } else {
7576 // which dirfrag for this dentry?
7577 CDir *dir = diri->get_dirfrag(diri->pick_dirfrag(p.first.first));
7578 ceph_assert(dir);
7579 ceph_assert(dir->is_auth());
7580 dn = dir->lookup(p.first.first, p.first.second);
7581 }
7582
7583 if (!dn) {
7584 if (dir)
7585 dout(0) << " missing dentry for " << p.first.first << " snap " << p.first.second << " in " << *dir << dendl;
7586 else
7587 dout(0) << " missing dentry for " << p.first.first << " snap " << p.first.second << dendl;
7588 }
7589 ceph_assert(dn);
7590
7591 if (nonce == dn->get_replica_nonce(from)) {
7592 dout(7) << " dentry_expire on " << *dn << " from mds." << from << dendl;
7593 dentry_remove_replica(dn, from, gather_locks);
7594 }
7595 else {
7596 dout(7) << " dentry_expire on " << *dn << " from mds." << from
7597 << " with old nonce " << nonce << " (current " << dn->get_replica_nonce(from)
7598 << "), dropping" << dendl;
7599 }
7600 }
7601 }
7602 }
7603
7604 for (set<SimpleLock*>::iterator p = gather_locks.begin(); p != gather_locks.end(); ++p) {
7605 if (!(*p)->is_stable())
7606 mds->locker->eval_gather(*p);
7607 }
7608 }
7609
7610 void MDCache::process_delayed_expire(CDir *dir)
7611 {
7612 dout(7) << "process_delayed_expire on " << *dir << dendl;
7613 for (const auto &p : delayed_expire[dir]) {
7614 handle_cache_expire(p.second);
7615 }
7616 delayed_expire.erase(dir);
7617 }
7618
7619 void MDCache::discard_delayed_expire(CDir *dir)
7620 {
7621 dout(7) << "discard_delayed_expire on " << *dir << dendl;
7622 delayed_expire.erase(dir);
7623 }
7624
7625 void MDCache::inode_remove_replica(CInode *in, mds_rank_t from, bool rejoin,
7626 set<SimpleLock *>& gather_locks)
7627 {
7628 in->remove_replica(from);
7629 in->set_mds_caps_wanted(from, 0);
7630
7631 // note: this code calls _eval more often than it needs to!
7632 // fix lock
7633 if (in->authlock.remove_replica(from)) gather_locks.insert(&in->authlock);
7634 if (in->linklock.remove_replica(from)) gather_locks.insert(&in->linklock);
7635 if (in->snaplock.remove_replica(from)) gather_locks.insert(&in->snaplock);
7636 if (in->xattrlock.remove_replica(from)) gather_locks.insert(&in->xattrlock);
7637 if (in->flocklock.remove_replica(from)) gather_locks.insert(&in->flocklock);
7638 if (in->policylock.remove_replica(from)) gather_locks.insert(&in->policylock);
7639
7640 // If 'rejoin' is true and the scatter lock is in LOCK_MIX_* state.
7641 // Don't remove the recovering mds from lock's gathering list because
7642 // it may hold rejoined wrlocks.
7643 if (in->dirfragtreelock.remove_replica(from, rejoin)) gather_locks.insert(&in->dirfragtreelock);
7644 if (in->filelock.remove_replica(from, rejoin)) gather_locks.insert(&in->filelock);
7645 if (in->nestlock.remove_replica(from, rejoin)) gather_locks.insert(&in->nestlock);
7646 }
7647
7648 void MDCache::dentry_remove_replica(CDentry *dn, mds_rank_t from, set<SimpleLock *>& gather_locks)
7649 {
7650 dn->remove_replica(from);
7651
7652 // fix lock
7653 if (dn->lock.remove_replica(from))
7654 gather_locks.insert(&dn->lock);
7655
7656 // Replicated strays might now be elegible for purge
7657 CDentry::linkage_t *dnl = dn->get_projected_linkage();
7658 if (dnl->is_primary()) {
7659 maybe_eval_stray(dnl->get_inode());
7660 }
7661 }
7662
7663 void MDCache::trim_client_leases()
7664 {
7665 utime_t now = ceph_clock_now();
7666
7667 dout(10) << "trim_client_leases" << dendl;
7668
7669 std::size_t pool = 0;
7670 for (const auto& list : client_leases) {
7671 pool += 1;
7672 if (list.empty())
7673 continue;
7674
7675 auto before = list.size();
7676 while (!list.empty()) {
7677 ClientLease *r = list.front();
7678 if (r->ttl > now) break;
7679 CDentry *dn = static_cast<CDentry*>(r->parent);
7680 dout(10) << " expiring client." << r->client << " lease of " << *dn << dendl;
7681 dn->remove_client_lease(r, mds->locker);
7682 }
7683 auto after = list.size();
7684 dout(10) << "trim_client_leases pool " << pool << " trimmed "
7685 << (before-after) << " leases, " << after << " left" << dendl;
7686 }
7687 }
7688
7689 void MDCache::check_memory_usage()
7690 {
7691 static MemoryModel mm(g_ceph_context);
7692 static MemoryModel::snap last;
7693 mm.sample(&last);
7694 static MemoryModel::snap baseline = last;
7695
7696 // check client caps
7697 ceph_assert(CInode::count() == inode_map.size() + snap_inode_map.size() + num_shadow_inodes);
7698 double caps_per_inode = 0.0;
7699 if (CInode::count())
7700 caps_per_inode = (double)Capability::count() / (double)CInode::count();
7701
7702 dout(2) << "Memory usage: "
7703 << " total " << last.get_total()
7704 << ", rss " << last.get_rss()
7705 << ", heap " << last.get_heap()
7706 << ", baseline " << baseline.get_heap()
7707 << ", " << num_inodes_with_caps << " / " << CInode::count() << " inodes have caps"
7708 << ", " << Capability::count() << " caps, " << caps_per_inode << " caps per inode"
7709 << dendl;
7710
7711 mds->update_mlogger();
7712 mds->mlogger->set(l_mdm_rss, last.get_rss());
7713 mds->mlogger->set(l_mdm_heap, last.get_heap());
7714 }
7715
7716
7717
7718 // =========================================================================================
7719 // shutdown
7720
7721 class C_MDC_ShutdownCheck : public MDCacheContext {
7722 public:
7723 explicit C_MDC_ShutdownCheck(MDCache *m) : MDCacheContext(m) {}
7724 void finish(int) override {
7725 mdcache->shutdown_check();
7726 }
7727 };
7728
7729 void MDCache::shutdown_check()
7730 {
7731 dout(0) << "shutdown_check at " << ceph_clock_now() << dendl;
7732
7733 // cache
7734 char old_val[32] = { 0 };
7735 char *o = old_val;
7736 g_conf().get_val("debug_mds", &o, sizeof(old_val));
7737 g_conf().set_val("debug_mds", "10");
7738 g_conf().apply_changes(nullptr);
7739 show_cache();
7740 g_conf().set_val("debug_mds", old_val);
7741 g_conf().apply_changes(nullptr);
7742 mds->timer.add_event_after(g_conf()->mds_shutdown_check, new C_MDC_ShutdownCheck(this));
7743
7744 // this
7745 dout(0) << "lru size now " << lru.lru_get_size() << "/" << bottom_lru.lru_get_size() << dendl;
7746 dout(0) << "log len " << mds->mdlog->get_num_events() << dendl;
7747
7748
7749 if (mds->objecter->is_active()) {
7750 dout(0) << "objecter still active" << dendl;
7751 mds->objecter->dump_active();
7752 }
7753 }
7754
7755
7756 void MDCache::shutdown_start()
7757 {
7758 dout(5) << "shutdown_start" << dendl;
7759
7760 if (g_conf()->mds_shutdown_check)
7761 mds->timer.add_event_after(g_conf()->mds_shutdown_check, new C_MDC_ShutdownCheck(this));
7762
7763 // g_conf()->debug_mds = 10;
7764 }
7765
7766
7767
7768 bool MDCache::shutdown_pass()
7769 {
7770 dout(7) << "shutdown_pass" << dendl;
7771
7772 if (mds->is_stopped()) {
7773 dout(7) << " already shut down" << dendl;
7774 show_cache();
7775 show_subtrees();
7776 return true;
7777 }
7778
7779 // empty stray dir
7780 bool strays_all_exported = shutdown_export_strays();
7781
7782 // trim cache
7783 trim(UINT64_MAX);
7784 dout(5) << "lru size now " << lru.lru_get_size() << "/" << bottom_lru.lru_get_size() << dendl;
7785
7786 // Export all subtrees to another active (usually rank 0) if not rank 0
7787 int num_auth_subtree = 0;
7788 if (!subtrees.empty() && mds->get_nodeid() != 0) {
7789 dout(7) << "looking for subtrees to export" << dendl;
7790 std::vector<CDir*> ls;
7791 for (auto& [dir, bounds] : subtrees) {
7792 dout(10) << " examining " << *dir << " bounds " << bounds << dendl;
7793 if (dir->get_inode()->is_mdsdir() || !dir->is_auth())
7794 continue;
7795 num_auth_subtree++;
7796 if (dir->is_frozen() ||
7797 dir->is_freezing() ||
7798 dir->is_ambiguous_dir_auth() ||
7799 dir->state_test(CDir::STATE_EXPORTING) ||
7800 dir->get_inode()->is_ephemerally_pinned()) {
7801 continue;
7802 }
7803 ls.push_back(dir);
7804 }
7805
7806 migrator->clear_export_queue();
7807 // stopping mds does not call MDBalancer::tick()
7808 mds->balancer->handle_export_pins();
7809 for (const auto& dir : ls) {
7810 mds_rank_t dest = dir->get_inode()->authority().first;
7811 if (dest > 0 && !mds->mdsmap->is_active(dest))
7812 dest = 0;
7813 dout(7) << "sending " << *dir << " back to mds." << dest << dendl;
7814 migrator->export_dir_nicely(dir, dest);
7815 }
7816 }
7817
7818 if (!strays_all_exported) {
7819 dout(7) << "waiting for strays to migrate" << dendl;
7820 return false;
7821 }
7822
7823 if (num_auth_subtree > 0) {
7824 ceph_assert(mds->get_nodeid() > 0);
7825 dout(7) << "still have " << num_auth_subtree << " auth subtrees" << dendl;
7826 show_subtrees();
7827 return false;
7828 }
7829
7830 // close out any sessions (and open files!) before we try to trim the log, etc.
7831 if (mds->sessionmap.have_unclosed_sessions()) {
7832 if (!mds->server->terminating_sessions)
7833 mds->server->terminate_sessions();
7834 return false;
7835 }
7836
7837 // Fully trim the log so that all objects in cache are clean and may be
7838 // trimmed by a future MDCache::trim. Note that MDSRank::tick does not
7839 // trim the log such that the cache eventually becomes clean.
7840 if (mds->mdlog->get_num_segments() > 0) {
7841 auto ls = mds->mdlog->get_current_segment();
7842 if (ls->num_events > 1 || !ls->dirty_dirfrags.empty()) {
7843 // Current segment contains events other than subtreemap or
7844 // there are dirty dirfrags (see CDir::log_mark_dirty())
7845 mds->mdlog->start_new_segment();
7846 mds->mdlog->flush();
7847 }
7848 }
7849 mds->mdlog->trim_all();
7850 if (mds->mdlog->get_num_segments() > 1) {
7851 dout(7) << "still >1 segments, waiting for log to trim" << dendl;
7852 return false;
7853 }
7854
7855 // drop our reference to our stray dir inode
7856 for (int i = 0; i < NUM_STRAY; ++i) {
7857 if (strays[i] &&
7858 strays[i]->state_test(CInode::STATE_STRAYPINNED)) {
7859 strays[i]->state_clear(CInode::STATE_STRAYPINNED);
7860 strays[i]->put(CInode::PIN_STRAY);
7861 strays[i]->put_stickydirs();
7862 }
7863 }
7864
7865 CDir *mydir = myin ? myin->get_dirfrag(frag_t()) : NULL;
7866 if (mydir && !mydir->is_subtree_root())
7867 mydir = NULL;
7868
7869 // subtrees map not empty yet?
7870 if (subtrees.size() > (mydir ? 1 : 0)) {
7871 dout(7) << "still have " << num_subtrees() << " subtrees" << dendl;
7872 show_subtrees();
7873 migrator->show_importing();
7874 migrator->show_exporting();
7875 if (!migrator->is_importing() && !migrator->is_exporting())
7876 show_cache();
7877 return false;
7878 }
7879 ceph_assert(!migrator->is_exporting());
7880 ceph_assert(!migrator->is_importing());
7881
7882 // replicas may dirty scatter locks
7883 if (myin && myin->is_replicated()) {
7884 dout(7) << "still have replicated objects" << dendl;
7885 return false;
7886 }
7887
7888 if ((myin && myin->get_num_auth_pins()) ||
7889 (mydir && (mydir->get_auth_pins() || mydir->get_dir_auth_pins()))) {
7890 dout(7) << "still have auth pinned objects" << dendl;
7891 return false;
7892 }
7893
7894 // (only do this once!)
7895 if (!mds->mdlog->is_capped()) {
7896 dout(7) << "capping the mdlog" << dendl;
7897 mds->mdlog->cap();
7898 }
7899
7900 if (!mds->mdlog->empty())
7901 mds->mdlog->trim(0);
7902
7903 if (!mds->mdlog->empty()) {
7904 dout(7) << "waiting for log to flush.. " << mds->mdlog->get_num_events()
7905 << " in " << mds->mdlog->get_num_segments() << " segments" << dendl;
7906 return false;
7907 }
7908
7909 if (!did_shutdown_log_cap) {
7910 // flush journal header
7911 dout(7) << "writing header for (now-empty) journal" << dendl;
7912 ceph_assert(mds->mdlog->empty());
7913 mds->mdlog->write_head(0);
7914 // NOTE: filer active checker below will block us until this completes.
7915 did_shutdown_log_cap = true;
7916 return false;
7917 }
7918
7919 // filer active?
7920 if (mds->objecter->is_active()) {
7921 dout(7) << "objecter still active" << dendl;
7922 mds->objecter->dump_active();
7923 return false;
7924 }
7925
7926 // trim what we can from the cache
7927 if (lru.lru_get_size() > 0 || bottom_lru.lru_get_size() > 0) {
7928 dout(7) << "there's still stuff in the cache: " << lru.lru_get_size() << "/" << bottom_lru.lru_get_size() << dendl;
7929 show_cache();
7930 //dump();
7931 return false;
7932 }
7933
7934 // make mydir subtree go away
7935 if (mydir) {
7936 if (mydir->get_num_ref() > 1) { // subtree pin
7937 dout(7) << "there's still reference to mydir " << *mydir << dendl;
7938 show_cache();
7939 return false;
7940 }
7941
7942 remove_subtree(mydir);
7943 myin->close_dirfrag(mydir->get_frag());
7944 }
7945 ceph_assert(subtrees.empty());
7946
7947 if (myin) {
7948 remove_inode(myin);
7949 ceph_assert(!myin);
7950 }
7951
7952 if (global_snaprealm) {
7953 remove_inode(global_snaprealm->inode);
7954 global_snaprealm = nullptr;
7955 }
7956
7957 // done!
7958 dout(5) << "shutdown done." << dendl;
7959 return true;
7960 }
7961
7962 bool MDCache::shutdown_export_strays()
7963 {
7964 static const unsigned MAX_EXPORTING = 100;
7965
7966 if (mds->get_nodeid() == 0)
7967 return true;
7968
7969 if (shutdown_exporting_strays.size() * 3 >= MAX_EXPORTING * 2)
7970 return false;
7971
7972 dout(10) << "shutdown_export_strays " << shutdown_export_next.first
7973 << " '" << shutdown_export_next.second << "'" << dendl;
7974
7975 bool mds0_active = mds->mdsmap->is_active(mds_rank_t(0));
7976 bool all_exported = false;
7977
7978 again:
7979 auto next = shutdown_export_next;
7980
7981 for (int i = 0; i < NUM_STRAY; ++i) {
7982 CInode *strayi = strays[i];
7983 if (!strayi ||
7984 !strayi->state_test(CInode::STATE_STRAYPINNED))
7985 continue;
7986 if (strayi->ino() < next.first.ino)
7987 continue;
7988
7989 deque<CDir*> dfls;
7990 strayi->get_dirfrags(dfls);
7991
7992 while (!dfls.empty()) {
7993 CDir *dir = dfls.front();
7994 dfls.pop_front();
7995
7996 if (dir->dirfrag() < next.first)
7997 continue;
7998 if (next.first < dir->dirfrag()) {
7999 next.first = dir->dirfrag();
8000 next.second.clear();
8001 }
8002
8003 if (!dir->is_complete()) {
8004 MDSContext *fin = nullptr;
8005 if (shutdown_exporting_strays.empty()) {
8006 fin = new MDSInternalContextWrapper(mds,
8007 new LambdaContext([this](int r) {
8008 shutdown_export_strays();
8009 })
8010 );
8011 }
8012 dir->fetch(fin);
8013 goto done;
8014 }
8015
8016 CDir::dentry_key_map::iterator it;
8017 if (next.second.empty()) {
8018 it = dir->begin();
8019 } else {
8020 auto hash = ceph_frag_value(strayi->hash_dentry_name(next.second));
8021 it = dir->lower_bound(dentry_key_t(0, next.second, hash));
8022 }
8023
8024 for (; it != dir->end(); ++it) {
8025 CDentry *dn = it->second;
8026 CDentry::linkage_t *dnl = dn->get_projected_linkage();
8027 if (dnl->is_null())
8028 continue;
8029
8030 if (!mds0_active && !dn->state_test(CDentry::STATE_PURGING)) {
8031 next.second = it->first.name;
8032 goto done;
8033 }
8034
8035 auto ret = shutdown_exporting_strays.insert(dnl->get_inode()->ino());
8036 if (!ret.second) {
8037 dout(10) << "already exporting/purging " << *dn << dendl;
8038 continue;
8039 }
8040
8041 // Don't try to migrate anything that is actually
8042 // being purged right now
8043 if (!dn->state_test(CDentry::STATE_PURGING))
8044 stray_manager.migrate_stray(dn, mds_rank_t(0)); // send to root!
8045
8046 if (shutdown_exporting_strays.size() >= MAX_EXPORTING) {
8047 ++it;
8048 if (it != dir->end()) {
8049 next.second = it->first.name;
8050 } else {
8051 if (dfls.empty())
8052 next.first.ino.val++;
8053 else
8054 next.first = dfls.front()->dirfrag();
8055 next.second.clear();
8056 }
8057 goto done;
8058 }
8059 }
8060 }
8061 }
8062
8063 if (shutdown_exporting_strays.empty()) {
8064 dirfrag_t first_df(MDS_INO_STRAY(mds->get_nodeid(), 0), 0);
8065 if (first_df < shutdown_export_next.first ||
8066 !shutdown_export_next.second.empty()) {
8067 shutdown_export_next.first = first_df;
8068 shutdown_export_next.second.clear();
8069 goto again;
8070 }
8071 all_exported = true;
8072 }
8073
8074 done:
8075 shutdown_export_next = next;
8076 return all_exported;
8077 }
8078
8079 // ========= messaging ==============
8080
8081 void MDCache::dispatch(const cref_t<Message> &m)
8082 {
8083 switch (m->get_type()) {
8084
8085 // RESOLVE
8086 case MSG_MDS_RESOLVE:
8087 handle_resolve(ref_cast<MMDSResolve>(m));
8088 break;
8089 case MSG_MDS_RESOLVEACK:
8090 handle_resolve_ack(ref_cast<MMDSResolveAck>(m));
8091 break;
8092
8093 // REJOIN
8094 case MSG_MDS_CACHEREJOIN:
8095 handle_cache_rejoin(ref_cast<MMDSCacheRejoin>(m));
8096 break;
8097
8098 case MSG_MDS_DISCOVER:
8099 handle_discover(ref_cast<MDiscover>(m));
8100 break;
8101 case MSG_MDS_DISCOVERREPLY:
8102 handle_discover_reply(ref_cast<MDiscoverReply>(m));
8103 break;
8104
8105 case MSG_MDS_DIRUPDATE:
8106 handle_dir_update(ref_cast<MDirUpdate>(m));
8107 break;
8108
8109 case MSG_MDS_CACHEEXPIRE:
8110 handle_cache_expire(ref_cast<MCacheExpire>(m));
8111 break;
8112
8113 case MSG_MDS_DENTRYLINK:
8114 handle_dentry_link(ref_cast<MDentryLink>(m));
8115 break;
8116 case MSG_MDS_DENTRYUNLINK:
8117 handle_dentry_unlink(ref_cast<MDentryUnlink>(m));
8118 break;
8119
8120 case MSG_MDS_FRAGMENTNOTIFY:
8121 handle_fragment_notify(ref_cast<MMDSFragmentNotify>(m));
8122 break;
8123 case MSG_MDS_FRAGMENTNOTIFYACK:
8124 handle_fragment_notify_ack(ref_cast<MMDSFragmentNotifyAck>(m));
8125 break;
8126
8127 case MSG_MDS_FINDINO:
8128 handle_find_ino(ref_cast<MMDSFindIno>(m));
8129 break;
8130 case MSG_MDS_FINDINOREPLY:
8131 handle_find_ino_reply(ref_cast<MMDSFindInoReply>(m));
8132 break;
8133
8134 case MSG_MDS_OPENINO:
8135 handle_open_ino(ref_cast<MMDSOpenIno>(m));
8136 break;
8137 case MSG_MDS_OPENINOREPLY:
8138 handle_open_ino_reply(ref_cast<MMDSOpenInoReply>(m));
8139 break;
8140
8141 case MSG_MDS_SNAPUPDATE:
8142 handle_snap_update(ref_cast<MMDSSnapUpdate>(m));
8143 break;
8144
8145 default:
8146 derr << "cache unknown message " << m->get_type() << dendl;
8147 ceph_abort_msg("cache unknown message");
8148 }
8149 }
8150
8151 int MDCache::path_traverse(MDRequestRef& mdr, MDSContextFactory& cf,
8152 const filepath& path, int flags,
8153 vector<CDentry*> *pdnvec, CInode **pin)
8154 {
8155 bool discover = (flags & MDS_TRAVERSE_DISCOVER);
8156 bool forward = !discover;
8157 bool path_locked = (flags & MDS_TRAVERSE_PATH_LOCKED);
8158 bool want_dentry = (flags & MDS_TRAVERSE_WANT_DENTRY);
8159 bool want_auth = (flags & MDS_TRAVERSE_WANT_AUTH);
8160 bool rdlock_snap = (flags & (MDS_TRAVERSE_RDLOCK_SNAP | MDS_TRAVERSE_RDLOCK_SNAP2));
8161 bool rdlock_path = (flags & MDS_TRAVERSE_RDLOCK_PATH);
8162 bool xlock_dentry = (flags & MDS_TRAVERSE_XLOCK_DENTRY);
8163 bool rdlock_authlock = (flags & MDS_TRAVERSE_RDLOCK_AUTHLOCK);
8164
8165 if (forward)
8166 ceph_assert(mdr); // forward requires a request
8167
8168 snapid_t snapid = CEPH_NOSNAP;
8169 if (mdr)
8170 mdr->snapid = snapid;
8171
8172 client_t client = mdr ? mdr->get_client() : -1;
8173
8174 if (mds->logger) mds->logger->inc(l_mds_traverse);
8175
8176 dout(7) << "traverse: opening base ino " << path.get_ino() << " snap " << snapid << dendl;
8177 CInode *cur = get_inode(path.get_ino());
8178 if (!cur) {
8179 if (MDS_INO_IS_MDSDIR(path.get_ino())) {
8180 open_foreign_mdsdir(path.get_ino(), cf.build());
8181 return 1;
8182 }
8183 if (MDS_INO_IS_STRAY(path.get_ino())) {
8184 mds_rank_t rank = MDS_INO_STRAY_OWNER(path.get_ino());
8185 unsigned idx = MDS_INO_STRAY_INDEX(path.get_ino());
8186 filepath path(strays[idx]->get_parent_dn()->get_name(),
8187 MDS_INO_MDSDIR(rank));
8188 MDRequestRef null_ref;
8189 return path_traverse(null_ref, cf, path, MDS_TRAVERSE_DISCOVER, nullptr);
8190 }
8191 return -CEPHFS_ESTALE;
8192 }
8193 if (cur->state_test(CInode::STATE_PURGING))
8194 return -CEPHFS_ESTALE;
8195
8196 if (flags & MDS_TRAVERSE_CHECK_LOCKCACHE)
8197 mds->locker->find_and_attach_lock_cache(mdr, cur);
8198
8199 if (mdr && mdr->lock_cache) {
8200 if (flags & MDS_TRAVERSE_WANT_DIRLAYOUT)
8201 mdr->dir_layout = mdr->lock_cache->get_dir_layout();
8202 } else if (rdlock_snap) {
8203 int n = (flags & MDS_TRAVERSE_RDLOCK_SNAP2) ? 1 : 0;
8204 if ((n == 0 && !(mdr->locking_state & MutationImpl::SNAP_LOCKED)) ||
8205 (n == 1 && !(mdr->locking_state & MutationImpl::SNAP2_LOCKED))) {
8206 bool want_layout = (flags & MDS_TRAVERSE_WANT_DIRLAYOUT);
8207 if (!mds->locker->try_rdlock_snap_layout(cur, mdr, n, want_layout))
8208 return 1;
8209 }
8210 }
8211
8212 // start trace
8213 if (pdnvec)
8214 pdnvec->clear();
8215 if (pin)
8216 *pin = cur;
8217
8218 MutationImpl::LockOpVec lov;
8219
8220 for (unsigned depth = 0; depth < path.depth(); ) {
8221 dout(12) << "traverse: path seg depth " << depth << " '" << path[depth]
8222 << "' snapid " << snapid << dendl;
8223
8224 if (!cur->is_dir()) {
8225 dout(7) << "traverse: " << *cur << " not a dir " << dendl;
8226 return -CEPHFS_ENOTDIR;
8227 }
8228
8229 // walk into snapdir?
8230 if (path[depth].length() == 0) {
8231 dout(10) << "traverse: snapdir" << dendl;
8232 if (!mdr || depth > 0) // snapdir must be the first component
8233 return -CEPHFS_EINVAL;
8234 snapid = CEPH_SNAPDIR;
8235 mdr->snapid = snapid;
8236 depth++;
8237 continue;
8238 }
8239 // walk thru snapdir?
8240 if (snapid == CEPH_SNAPDIR) {
8241 if (!mdr)
8242 return -CEPHFS_EINVAL;
8243 SnapRealm *realm = cur->find_snaprealm();
8244 snapid = realm->resolve_snapname(path[depth], cur->ino());
8245 dout(10) << "traverse: snap " << path[depth] << " -> " << snapid << dendl;
8246 if (!snapid) {
8247 if (pdnvec)
8248 pdnvec->clear(); // do not confuse likes of rdlock_path_pin_ref();
8249 return -CEPHFS_ENOENT;
8250 }
8251 mdr->snapid = snapid;
8252 depth++;
8253 continue;
8254 }
8255
8256 // open dir
8257 frag_t fg = cur->pick_dirfrag(path[depth]);
8258 CDir *curdir = cur->get_dirfrag(fg);
8259 if (!curdir) {
8260 if (cur->is_auth()) {
8261 // parent dir frozen_dir?
8262 if (cur->is_frozen()) {
8263 dout(7) << "traverse: " << *cur << " is frozen, waiting" << dendl;
8264 cur->add_waiter(CDir::WAIT_UNFREEZE, cf.build());
8265 return 1;
8266 }
8267 curdir = cur->get_or_open_dirfrag(this, fg);
8268 } else {
8269 // discover?
8270 dout(10) << "traverse: need dirfrag " << fg << ", doing discover from " << *cur << dendl;
8271 discover_path(cur, snapid, path.postfixpath(depth), cf.build(),
8272 path_locked);
8273 if (mds->logger) mds->logger->inc(l_mds_traverse_discover);
8274 return 1;
8275 }
8276 }
8277 ceph_assert(curdir);
8278
8279 #ifdef MDS_VERIFY_FRAGSTAT
8280 if (curdir->is_complete())
8281 curdir->verify_fragstat();
8282 #endif
8283
8284 // frozen?
8285 /*
8286 if (curdir->is_frozen()) {
8287 // doh!
8288 // FIXME: traverse is allowed?
8289 dout(7) << "traverse: " << *curdir << " is frozen, waiting" << dendl;
8290 curdir->add_waiter(CDir::WAIT_UNFREEZE, _get_waiter(mdr, req, fin));
8291 if (onfinish) delete onfinish;
8292 return 1;
8293 }
8294 */
8295
8296 if (want_auth && want_dentry && depth == path.depth() - 1) {
8297 if (curdir->is_ambiguous_auth()) {
8298 dout(10) << "waiting for single auth on " << *curdir << dendl;
8299 curdir->add_waiter(CInode::WAIT_SINGLEAUTH, cf.build());
8300 return 1;
8301 }
8302 if (!curdir->is_auth()) {
8303 dout(10) << "fw to auth for " << *curdir << dendl;
8304 request_forward(mdr, curdir->authority().first);
8305 return 2;
8306 }
8307 }
8308
8309 // Before doing dirfrag->dn lookup, compare with DamageTable's
8310 // record of which dentries were unreadable
8311 if (mds->damage_table.is_dentry_damaged(curdir, path[depth], snapid)) {
8312 dout(4) << "traverse: stopped lookup at damaged dentry "
8313 << *curdir << "/" << path[depth] << " snap=" << snapid << dendl;
8314 return -CEPHFS_EIO;
8315 }
8316
8317 // dentry
8318 CDentry *dn = curdir->lookup(path[depth], snapid);
8319 if (dn) {
8320 if (dn->state_test(CDentry::STATE_PURGING))
8321 return -CEPHFS_ENOENT;
8322
8323 if (rdlock_path) {
8324 lov.clear();
8325 if (xlock_dentry && depth == path.depth() - 1) {
8326 if (depth > 0 || !mdr->lock_cache) {
8327 lov.add_wrlock(&cur->filelock);
8328 lov.add_wrlock(&cur->nestlock);
8329 if (rdlock_authlock)
8330 lov.add_rdlock(&cur->authlock);
8331 }
8332 lov.add_xlock(&dn->lock);
8333 } else {
8334 // force client to flush async dir operation if necessary
8335 if (cur->filelock.is_cached())
8336 lov.add_wrlock(&cur->filelock);
8337 lov.add_rdlock(&dn->lock);
8338 }
8339 if (!mds->locker->acquire_locks(mdr, lov)) {
8340 dout(10) << "traverse: failed to rdlock " << dn->lock << " " << *dn << dendl;
8341 return 1;
8342 }
8343 } else if (!path_locked &&
8344 !dn->lock.can_read(client) &&
8345 !(dn->lock.is_xlocked() && dn->lock.get_xlock_by() == mdr)) {
8346 dout(10) << "traverse: non-readable dentry at " << *dn << dendl;
8347 dn->lock.add_waiter(SimpleLock::WAIT_RD, cf.build());
8348 if (mds->logger)
8349 mds->logger->inc(l_mds_traverse_lock);
8350 if (dn->is_auth() && dn->lock.is_unstable_and_locked())
8351 mds->mdlog->flush();
8352 return 1;
8353 }
8354
8355 if (pdnvec)
8356 pdnvec->push_back(dn);
8357
8358 CDentry::linkage_t *dnl = dn->get_projected_linkage();
8359 // can we conclude CEPHFS_ENOENT?
8360 if (dnl->is_null()) {
8361 dout(10) << "traverse: null+readable dentry at " << *dn << dendl;
8362 if (depth == path.depth() - 1) {
8363 if (want_dentry)
8364 break;
8365 } else {
8366 if (pdnvec)
8367 pdnvec->clear(); // do not confuse likes of rdlock_path_pin_ref();
8368 }
8369 return -CEPHFS_ENOENT;
8370 }
8371
8372 // do we have inode?
8373 CInode *in = dnl->get_inode();
8374 if (!in) {
8375 ceph_assert(dnl->is_remote());
8376 // do i have it?
8377 in = get_inode(dnl->get_remote_ino());
8378 if (in) {
8379 dout(7) << "linking in remote in " << *in << dendl;
8380 dn->link_remote(dnl, in);
8381 } else {
8382 dout(7) << "remote link to " << dnl->get_remote_ino() << ", which i don't have" << dendl;
8383 ceph_assert(mdr); // we shouldn't hit non-primary dentries doing a non-mdr traversal!
8384 if (mds->damage_table.is_remote_damaged(dnl->get_remote_ino())) {
8385 dout(4) << "traverse: remote dentry points to damaged ino "
8386 << *dn << dendl;
8387 return -CEPHFS_EIO;
8388 }
8389 open_remote_dentry(dn, true, cf.build(),
8390 (path_locked && depth == path.depth() - 1));
8391 if (mds->logger) mds->logger->inc(l_mds_traverse_remote_ino);
8392 return 1;
8393 }
8394 }
8395
8396 cur = in;
8397
8398 if (rdlock_snap && !(want_dentry && depth == path.depth() - 1)) {
8399 lov.clear();
8400 lov.add_rdlock(&cur->snaplock);
8401 if (!mds->locker->acquire_locks(mdr, lov)) {
8402 dout(10) << "traverse: failed to rdlock " << cur->snaplock << " " << *cur << dendl;
8403 return 1;
8404 }
8405 }
8406
8407 // add to trace, continue.
8408 touch_inode(cur);
8409 if (pin)
8410 *pin = cur;
8411 depth++;
8412 continue;
8413 }
8414
8415 ceph_assert(!dn);
8416
8417 // MISS. dentry doesn't exist.
8418 dout(12) << "traverse: miss on dentry " << path[depth] << " in " << *curdir << dendl;
8419
8420 if (curdir->is_auth()) {
8421 // dentry is mine.
8422 if (curdir->is_complete() ||
8423 (snapid == CEPH_NOSNAP &&
8424 curdir->has_bloom() &&
8425 !curdir->is_in_bloom(path[depth]))) {
8426 // file not found
8427 if (pdnvec) {
8428 // instantiate a null dn?
8429 if (depth < path.depth() - 1) {
8430 dout(20) << " didn't traverse full path; not returning pdnvec" << dendl;
8431 } else if (snapid < CEPH_MAXSNAP) {
8432 dout(20) << " not adding null for snapid " << snapid << dendl;
8433 } else if (curdir->is_frozen()) {
8434 dout(7) << "traverse: " << *curdir << " is frozen, waiting" << dendl;
8435 curdir->add_waiter(CDir::WAIT_UNFREEZE, cf.build());
8436 return 1;
8437 } else {
8438 // create a null dentry
8439 dn = curdir->add_null_dentry(path[depth]);
8440 dout(20) << " added null " << *dn << dendl;
8441
8442 if (rdlock_path) {
8443 lov.clear();
8444 if (xlock_dentry) {
8445 if (depth > 0 || !mdr->lock_cache) {
8446 lov.add_wrlock(&cur->filelock);
8447 lov.add_wrlock(&cur->nestlock);
8448 if (rdlock_authlock)
8449 lov.add_rdlock(&cur->authlock);
8450 }
8451 lov.add_xlock(&dn->lock);
8452 } else {
8453 // force client to flush async dir operation if necessary
8454 if (cur->filelock.is_cached())
8455 lov.add_wrlock(&cur->filelock);
8456 lov.add_rdlock(&dn->lock);
8457 }
8458 if (!mds->locker->acquire_locks(mdr, lov)) {
8459 dout(10) << "traverse: failed to rdlock " << dn->lock << " " << *dn << dendl;
8460 return 1;
8461 }
8462 }
8463 }
8464 if (dn) {
8465 pdnvec->push_back(dn);
8466 if (want_dentry)
8467 break;
8468 } else {
8469 pdnvec->clear(); // do not confuse likes of rdlock_path_pin_ref();
8470 }
8471 }
8472 return -CEPHFS_ENOENT;
8473 } else {
8474
8475 // Check DamageTable for missing fragments before trying to fetch
8476 // this
8477 if (mds->damage_table.is_dirfrag_damaged(curdir)) {
8478 dout(4) << "traverse: damaged dirfrag " << *curdir
8479 << ", blocking fetch" << dendl;
8480 return -CEPHFS_EIO;
8481 }
8482
8483 // directory isn't complete; reload
8484 dout(7) << "traverse: incomplete dir contents for " << *cur << ", fetching" << dendl;
8485 touch_inode(cur);
8486 curdir->fetch(cf.build(), path[depth]);
8487 if (mds->logger) mds->logger->inc(l_mds_traverse_dir_fetch);
8488 return 1;
8489 }
8490 } else {
8491 // dirfrag/dentry is not mine.
8492 mds_authority_t dauth = curdir->authority();
8493
8494 if (forward &&
8495 mdr && mdr->client_request &&
8496 (int)depth < mdr->client_request->get_num_fwd()){
8497 dout(7) << "traverse: snap " << snapid << " and depth " << depth
8498 << " < fwd " << mdr->client_request->get_num_fwd()
8499 << ", discovering instead of forwarding" << dendl;
8500 discover = true;
8501 }
8502
8503 if ((discover)) {
8504 dout(7) << "traverse: discover from " << path[depth] << " from " << *curdir << dendl;
8505 discover_path(curdir, snapid, path.postfixpath(depth), cf.build(),
8506 path_locked);
8507 if (mds->logger) mds->logger->inc(l_mds_traverse_discover);
8508 return 1;
8509 }
8510 if (forward) {
8511 // forward
8512 dout(7) << "traverse: not auth for " << path << " in " << *curdir << dendl;
8513
8514 if (curdir->is_ambiguous_auth()) {
8515 // wait
8516 dout(7) << "traverse: waiting for single auth in " << *curdir << dendl;
8517 curdir->add_waiter(CDir::WAIT_SINGLEAUTH, cf.build());
8518 return 1;
8519 }
8520
8521 dout(7) << "traverse: forwarding, not auth for " << *curdir << dendl;
8522
8523 request_forward(mdr, dauth.first);
8524
8525 if (mds->logger) mds->logger->inc(l_mds_traverse_forward);
8526 return 2;
8527 }
8528 }
8529
8530 ceph_abort(); // i shouldn't get here
8531 }
8532
8533 if (want_auth && !want_dentry) {
8534 if (cur->is_ambiguous_auth()) {
8535 dout(10) << "waiting for single auth on " << *cur << dendl;
8536 cur->add_waiter(CInode::WAIT_SINGLEAUTH, cf.build());
8537 return 1;
8538 }
8539 if (!cur->is_auth()) {
8540 dout(10) << "fw to auth for " << *cur << dendl;
8541 request_forward(mdr, cur->authority().first);
8542 return 2;
8543 }
8544 }
8545
8546 // success.
8547 if (mds->logger) mds->logger->inc(l_mds_traverse_hit);
8548 dout(10) << "path_traverse finish on snapid " << snapid << dendl;
8549 if (mdr)
8550 ceph_assert(mdr->snapid == snapid);
8551
8552 if (flags & MDS_TRAVERSE_RDLOCK_SNAP)
8553 mdr->locking_state |= MutationImpl::SNAP_LOCKED;
8554 else if (flags & MDS_TRAVERSE_RDLOCK_SNAP2)
8555 mdr->locking_state |= MutationImpl::SNAP2_LOCKED;
8556
8557 if (rdlock_path)
8558 mdr->locking_state |= MutationImpl::PATH_LOCKED;
8559
8560 return 0;
8561 }
8562
8563 CInode *MDCache::cache_traverse(const filepath& fp)
8564 {
8565 dout(10) << "cache_traverse " << fp << dendl;
8566
8567 CInode *in;
8568 unsigned depth = 0;
8569 char mdsdir_name[16];
8570 sprintf(mdsdir_name, "~mds%d", mds->get_nodeid());
8571
8572 if (fp.get_ino()) {
8573 in = get_inode(fp.get_ino());
8574 } else if (fp.depth() > 0 && (fp[0] == "~mdsdir" || fp[0] == mdsdir_name)) {
8575 in = myin;
8576 depth = 1;
8577 } else {
8578 in = root;
8579 }
8580 if (!in)
8581 return NULL;
8582
8583 for (; depth < fp.depth(); depth++) {
8584 std::string_view dname = fp[depth];
8585 frag_t fg = in->pick_dirfrag(dname);
8586 dout(20) << " " << depth << " " << dname << " frag " << fg << " from " << *in << dendl;
8587 CDir *curdir = in->get_dirfrag(fg);
8588 if (!curdir)
8589 return NULL;
8590 CDentry *dn = curdir->lookup(dname, CEPH_NOSNAP);
8591 if (!dn)
8592 return NULL;
8593 in = dn->get_linkage()->get_inode();
8594 if (!in)
8595 return NULL;
8596 }
8597 dout(10) << " got " << *in << dendl;
8598 return in;
8599 }
8600
8601
8602 /**
8603 * open_remote_dir -- open up a remote dirfrag
8604 *
8605 * @param diri base inode
8606 * @param approxfg approximate fragment.
8607 * @param fin completion callback
8608 */
8609 void MDCache::open_remote_dirfrag(CInode *diri, frag_t approxfg, MDSContext *fin)
8610 {
8611 dout(10) << "open_remote_dir on " << *diri << dendl;
8612 ceph_assert(diri->is_dir());
8613 ceph_assert(!diri->is_auth());
8614 ceph_assert(diri->get_dirfrag(approxfg) == 0);
8615
8616 discover_dir_frag(diri, approxfg, fin);
8617 }
8618
8619
8620 /**
8621 * get_dentry_inode - get or open inode
8622 *
8623 * @param dn the dentry
8624 * @param mdr current request
8625 *
8626 * will return inode for primary, or link up/open up remote link's inode as necessary.
8627 * If it's not available right now, puts mdr on wait list and returns null.
8628 */
8629 CInode *MDCache::get_dentry_inode(CDentry *dn, MDRequestRef& mdr, bool projected)
8630 {
8631 CDentry::linkage_t *dnl;
8632 if (projected)
8633 dnl = dn->get_projected_linkage();
8634 else
8635 dnl = dn->get_linkage();
8636
8637 ceph_assert(!dnl->is_null());
8638
8639 if (dnl->is_primary())
8640 return dnl->inode;
8641
8642 ceph_assert(dnl->is_remote());
8643 CInode *in = get_inode(dnl->get_remote_ino());
8644 if (in) {
8645 dout(7) << "get_dentry_inode linking in remote in " << *in << dendl;
8646 dn->link_remote(dnl, in);
8647 return in;
8648 } else {
8649 dout(10) << "get_dentry_inode on remote dn, opening inode for " << *dn << dendl;
8650 open_remote_dentry(dn, projected, new C_MDS_RetryRequest(this, mdr));
8651 return 0;
8652 }
8653 }
8654
8655 struct C_MDC_OpenRemoteDentry : public MDCacheContext {
8656 CDentry *dn;
8657 inodeno_t ino;
8658 MDSContext *onfinish;
8659 bool want_xlocked;
8660 C_MDC_OpenRemoteDentry(MDCache *m, CDentry *d, inodeno_t i, MDSContext *f, bool wx) :
8661 MDCacheContext(m), dn(d), ino(i), onfinish(f), want_xlocked(wx) {
8662 dn->get(MDSCacheObject::PIN_PTRWAITER);
8663 }
8664 void finish(int r) override {
8665 mdcache->_open_remote_dentry_finish(dn, ino, onfinish, want_xlocked, r);
8666 dn->put(MDSCacheObject::PIN_PTRWAITER);
8667 }
8668 };
8669
8670 void MDCache::open_remote_dentry(CDentry *dn, bool projected, MDSContext *fin, bool want_xlocked)
8671 {
8672 dout(10) << "open_remote_dentry " << *dn << dendl;
8673 CDentry::linkage_t *dnl = projected ? dn->get_projected_linkage() : dn->get_linkage();
8674 inodeno_t ino = dnl->get_remote_ino();
8675 int64_t pool = dnl->get_remote_d_type() == DT_DIR ? mds->get_metadata_pool() : -1;
8676 open_ino(ino, pool,
8677 new C_MDC_OpenRemoteDentry(this, dn, ino, fin, want_xlocked), true, want_xlocked); // backtrace
8678 }
8679
8680 void MDCache::_open_remote_dentry_finish(CDentry *dn, inodeno_t ino, MDSContext *fin,
8681 bool want_xlocked, int r)
8682 {
8683 if (r < 0) {
8684 CDentry::linkage_t *dnl = dn->get_projected_linkage();
8685 if (dnl->is_remote() && dnl->get_remote_ino() == ino) {
8686 dout(0) << "open_remote_dentry_finish bad remote dentry " << *dn << dendl;
8687 dn->state_set(CDentry::STATE_BADREMOTEINO);
8688
8689 std::string path;
8690 CDir *dir = dn->get_dir();
8691 if (dir) {
8692 dir->get_inode()->make_path_string(path);
8693 path += "/";
8694 path += dn->get_name();
8695 }
8696
8697 bool fatal = mds->damage_table.notify_remote_damaged(ino, path);
8698 if (fatal) {
8699 mds->damaged();
8700 ceph_abort(); // unreachable, damaged() respawns us
8701 }
8702 } else {
8703 r = 0;
8704 }
8705 }
8706 fin->complete(r < 0 ? r : 0);
8707 }
8708
8709
8710 void MDCache::make_trace(vector<CDentry*>& trace, CInode *in)
8711 {
8712 // empty trace if we're a base inode
8713 if (in->is_base())
8714 return;
8715
8716 CInode *parent = in->get_parent_inode();
8717 ceph_assert(parent);
8718 make_trace(trace, parent);
8719
8720 CDentry *dn = in->get_parent_dn();
8721 dout(15) << "make_trace adding " << *dn << dendl;
8722 trace.push_back(dn);
8723 }
8724
8725
8726 // -------------------------------------------------------------------------------
8727 // Open inode by inode number
8728
8729 class C_IO_MDC_OpenInoBacktraceFetched : public MDCacheIOContext {
8730 inodeno_t ino;
8731 public:
8732 bufferlist bl;
8733 C_IO_MDC_OpenInoBacktraceFetched(MDCache *c, inodeno_t i) :
8734 MDCacheIOContext(c), ino(i) {}
8735 void finish(int r) override {
8736 mdcache->_open_ino_backtrace_fetched(ino, bl, r);
8737 }
8738 void print(ostream& out) const override {
8739 out << "openino_backtrace_fetch" << ino << ")";
8740 }
8741 };
8742
8743 struct C_MDC_OpenInoTraverseDir : public MDCacheContext {
8744 inodeno_t ino;
8745 cref_t<MMDSOpenIno> msg;
8746 bool parent;
8747 public:
8748 C_MDC_OpenInoTraverseDir(MDCache *c, inodeno_t i, const cref_t<MMDSOpenIno> &m, bool p) :
8749 MDCacheContext(c), ino(i), msg(m), parent(p) {}
8750 void finish(int r) override {
8751 if (r < 0 && !parent)
8752 r = -CEPHFS_EAGAIN;
8753 if (msg) {
8754 mdcache->handle_open_ino(msg, r);
8755 return;
8756 }
8757 auto& info = mdcache->opening_inodes.at(ino);
8758 mdcache->_open_ino_traverse_dir(ino, info, r);
8759 }
8760 };
8761
8762 struct C_MDC_OpenInoParentOpened : public MDCacheContext {
8763 inodeno_t ino;
8764 public:
8765 C_MDC_OpenInoParentOpened(MDCache *c, inodeno_t i) : MDCacheContext(c), ino(i) {}
8766 void finish(int r) override {
8767 mdcache->_open_ino_parent_opened(ino, r);
8768 }
8769 };
8770
8771 void MDCache::_open_ino_backtrace_fetched(inodeno_t ino, bufferlist& bl, int err)
8772 {
8773 dout(10) << "_open_ino_backtrace_fetched ino " << ino << " errno " << err << dendl;
8774
8775 open_ino_info_t& info = opening_inodes.at(ino);
8776
8777 CInode *in = get_inode(ino);
8778 if (in) {
8779 dout(10) << " found cached " << *in << dendl;
8780 open_ino_finish(ino, info, in->authority().first);
8781 return;
8782 }
8783
8784 inode_backtrace_t backtrace;
8785 if (err == 0) {
8786 try {
8787 decode(backtrace, bl);
8788 } catch (const buffer::error &decode_exc) {
8789 derr << "corrupt backtrace on ino x0" << std::hex << ino
8790 << std::dec << ": " << decode_exc.what() << dendl;
8791 open_ino_finish(ino, info, -CEPHFS_EIO);
8792 return;
8793 }
8794 if (backtrace.pool != info.pool && backtrace.pool != -1) {
8795 dout(10) << " old object in pool " << info.pool
8796 << ", retrying pool " << backtrace.pool << dendl;
8797 info.pool = backtrace.pool;
8798 C_IO_MDC_OpenInoBacktraceFetched *fin =
8799 new C_IO_MDC_OpenInoBacktraceFetched(this, ino);
8800 fetch_backtrace(ino, info.pool, fin->bl,
8801 new C_OnFinisher(fin, mds->finisher));
8802 return;
8803 }
8804 } else if (err == -CEPHFS_ENOENT) {
8805 int64_t meta_pool = mds->get_metadata_pool();
8806 if (info.pool != meta_pool) {
8807 dout(10) << " no object in pool " << info.pool
8808 << ", retrying pool " << meta_pool << dendl;
8809 info.pool = meta_pool;
8810 C_IO_MDC_OpenInoBacktraceFetched *fin =
8811 new C_IO_MDC_OpenInoBacktraceFetched(this, ino);
8812 fetch_backtrace(ino, info.pool, fin->bl,
8813 new C_OnFinisher(fin, mds->finisher));
8814 return;
8815 }
8816 err = 0; // backtrace.ancestors.empty() is checked below
8817 }
8818
8819 if (err == 0) {
8820 if (backtrace.ancestors.empty()) {
8821 dout(10) << " got empty backtrace " << dendl;
8822 err = -CEPHFS_ESTALE;
8823 } else if (!info.ancestors.empty()) {
8824 if (info.ancestors[0] == backtrace.ancestors[0]) {
8825 dout(10) << " got same parents " << info.ancestors[0] << " 2 times" << dendl;
8826 err = -CEPHFS_EINVAL;
8827 } else {
8828 info.last_err = 0;
8829 }
8830 }
8831 }
8832 if (err) {
8833 dout(0) << " failed to open ino " << ino << " err " << err << "/" << info.last_err << dendl;
8834 if (info.last_err)
8835 err = info.last_err;
8836 open_ino_finish(ino, info, err);
8837 return;
8838 }
8839
8840 dout(10) << " got backtrace " << backtrace << dendl;
8841 info.ancestors = backtrace.ancestors;
8842
8843 _open_ino_traverse_dir(ino, info, 0);
8844 }
8845
8846 void MDCache::_open_ino_parent_opened(inodeno_t ino, int ret)
8847 {
8848 dout(10) << "_open_ino_parent_opened ino " << ino << " ret " << ret << dendl;
8849
8850 open_ino_info_t& info = opening_inodes.at(ino);
8851
8852 CInode *in = get_inode(ino);
8853 if (in) {
8854 dout(10) << " found cached " << *in << dendl;
8855 open_ino_finish(ino, info, in->authority().first);
8856 return;
8857 }
8858
8859 if (ret == mds->get_nodeid()) {
8860 _open_ino_traverse_dir(ino, info, 0);
8861 } else {
8862 if (ret >= 0) {
8863 mds_rank_t checked_rank = mds_rank_t(ret);
8864 info.check_peers = true;
8865 info.auth_hint = checked_rank;
8866 info.checked.erase(checked_rank);
8867 }
8868 do_open_ino(ino, info, ret);
8869 }
8870 }
8871
8872 void MDCache::_open_ino_traverse_dir(inodeno_t ino, open_ino_info_t& info, int ret)
8873 {
8874 dout(10) << __func__ << ": ino " << ino << " ret " << ret << dendl;
8875
8876 CInode *in = get_inode(ino);
8877 if (in) {
8878 dout(10) << " found cached " << *in << dendl;
8879 open_ino_finish(ino, info, in->authority().first);
8880 return;
8881 }
8882
8883 if (ret) {
8884 do_open_ino(ino, info, ret);
8885 return;
8886 }
8887
8888 mds_rank_t hint = info.auth_hint;
8889 ret = open_ino_traverse_dir(ino, NULL, info.ancestors,
8890 info.discover, info.want_xlocked, &hint);
8891 if (ret > 0)
8892 return;
8893 if (hint != mds->get_nodeid())
8894 info.auth_hint = hint;
8895 do_open_ino(ino, info, ret);
8896 }
8897
8898 void MDCache::_open_ino_fetch_dir(inodeno_t ino, const cref_t<MMDSOpenIno> &m, CDir *dir, bool parent)
8899 {
8900 if (dir->state_test(CDir::STATE_REJOINUNDEF))
8901 ceph_assert(dir->get_inode()->dirfragtree.is_leaf(dir->get_frag()));
8902 dir->fetch(new C_MDC_OpenInoTraverseDir(this, ino, m, parent));
8903 if (mds->logger)
8904 mds->logger->inc(l_mds_openino_dir_fetch);
8905 }
8906
8907 int MDCache::open_ino_traverse_dir(inodeno_t ino, const cref_t<MMDSOpenIno> &m,
8908 const vector<inode_backpointer_t>& ancestors,
8909 bool discover, bool want_xlocked, mds_rank_t *hint)
8910 {
8911 dout(10) << "open_ino_traverse_dir ino " << ino << " " << ancestors << dendl;
8912 int err = 0;
8913 for (unsigned i = 0; i < ancestors.size(); i++) {
8914 const auto& ancestor = ancestors.at(i);
8915 CInode *diri = get_inode(ancestor.dirino);
8916
8917 if (!diri) {
8918 if (discover && MDS_INO_IS_MDSDIR(ancestor.dirino)) {
8919 open_foreign_mdsdir(ancestor.dirino, new C_MDC_OpenInoTraverseDir(this, ino, m, i == 0));
8920 return 1;
8921 }
8922 continue;
8923 }
8924
8925 if (diri->state_test(CInode::STATE_REJOINUNDEF)) {
8926 CDir *dir = diri->get_parent_dir();
8927 while (dir->state_test(CDir::STATE_REJOINUNDEF) &&
8928 dir->get_inode()->state_test(CInode::STATE_REJOINUNDEF))
8929 dir = dir->get_inode()->get_parent_dir();
8930 _open_ino_fetch_dir(ino, m, dir, i == 0);
8931 return 1;
8932 }
8933
8934 if (!diri->is_dir()) {
8935 dout(10) << " " << *diri << " is not dir" << dendl;
8936 if (i == 0)
8937 err = -CEPHFS_ENOTDIR;
8938 break;
8939 }
8940
8941 const string& name = ancestor.dname;
8942 frag_t fg = diri->pick_dirfrag(name);
8943 CDir *dir = diri->get_dirfrag(fg);
8944 if (!dir) {
8945 if (diri->is_auth()) {
8946 if (diri->is_frozen()) {
8947 dout(10) << " " << *diri << " is frozen, waiting " << dendl;
8948 diri->add_waiter(CDir::WAIT_UNFREEZE, new C_MDC_OpenInoTraverseDir(this, ino, m, i == 0));
8949 return 1;
8950 }
8951 dir = diri->get_or_open_dirfrag(this, fg);
8952 } else if (discover) {
8953 open_remote_dirfrag(diri, fg, new C_MDC_OpenInoTraverseDir(this, ino, m, i == 0));
8954 return 1;
8955 }
8956 }
8957 if (dir) {
8958 inodeno_t next_ino = i > 0 ? ancestors.at(i-1).dirino : ino;
8959 CDentry *dn = dir->lookup(name);
8960 CDentry::linkage_t *dnl = dn ? dn->get_linkage() : NULL;
8961 if (dir->is_auth()) {
8962 if (dnl && dnl->is_primary() &&
8963 dnl->get_inode()->state_test(CInode::STATE_REJOINUNDEF)) {
8964 dout(10) << " fetching undef " << *dnl->get_inode() << dendl;
8965 _open_ino_fetch_dir(ino, m, dir, i == 0);
8966 return 1;
8967 }
8968
8969 if (!dnl && !dir->is_complete() &&
8970 (!dir->has_bloom() || dir->is_in_bloom(name))) {
8971 dout(10) << " fetching incomplete " << *dir << dendl;
8972 _open_ino_fetch_dir(ino, m, dir, i == 0);
8973 return 1;
8974 }
8975
8976 dout(10) << " no ino " << next_ino << " in " << *dir << dendl;
8977 if (i == 0)
8978 err = -CEPHFS_ENOENT;
8979 } else if (discover) {
8980 if (!dnl) {
8981 filepath path(name, 0);
8982 discover_path(dir, CEPH_NOSNAP, path, new C_MDC_OpenInoTraverseDir(this, ino, m, i == 0),
8983 (i == 0 && want_xlocked));
8984 return 1;
8985 }
8986 if (dnl->is_null() && !dn->lock.can_read(-1)) {
8987 dout(10) << " null " << *dn << " is not readable, waiting" << dendl;
8988 dn->lock.add_waiter(SimpleLock::WAIT_RD, new C_MDC_OpenInoTraverseDir(this, ino, m, i == 0));
8989 return 1;
8990 }
8991 dout(10) << " no ino " << next_ino << " in " << *dir << dendl;
8992 if (i == 0)
8993 err = -CEPHFS_ENOENT;
8994 }
8995 }
8996 if (hint && i == 0)
8997 *hint = dir ? dir->authority().first : diri->authority().first;
8998 break;
8999 }
9000 return err;
9001 }
9002
9003 void MDCache::open_ino_finish(inodeno_t ino, open_ino_info_t& info, int ret)
9004 {
9005 dout(10) << "open_ino_finish ino " << ino << " ret " << ret << dendl;
9006
9007 MDSContext::vec waiters;
9008 waiters.swap(info.waiters);
9009 opening_inodes.erase(ino);
9010 finish_contexts(g_ceph_context, waiters, ret);
9011 }
9012
9013 void MDCache::do_open_ino(inodeno_t ino, open_ino_info_t& info, int err)
9014 {
9015 if (err < 0 && err != -CEPHFS_EAGAIN) {
9016 info.checked.clear();
9017 info.checking = MDS_RANK_NONE;
9018 info.check_peers = true;
9019 info.fetch_backtrace = true;
9020 if (info.discover) {
9021 info.discover = false;
9022 info.ancestors.clear();
9023 }
9024 if (err != -CEPHFS_ENOENT && err != -CEPHFS_ENOTDIR)
9025 info.last_err = err;
9026 }
9027
9028 if (info.check_peers || info.discover) {
9029 if (info.discover) {
9030 // got backtrace from peer, but failed to find inode. re-check peers
9031 info.discover = false;
9032 info.ancestors.clear();
9033 info.checked.clear();
9034 }
9035 info.check_peers = false;
9036 info.checking = MDS_RANK_NONE;
9037 do_open_ino_peer(ino, info);
9038 } else if (info.fetch_backtrace) {
9039 info.check_peers = true;
9040 info.fetch_backtrace = false;
9041 info.checking = mds->get_nodeid();
9042 info.checked.clear();
9043 C_IO_MDC_OpenInoBacktraceFetched *fin =
9044 new C_IO_MDC_OpenInoBacktraceFetched(this, ino);
9045 fetch_backtrace(ino, info.pool, fin->bl,
9046 new C_OnFinisher(fin, mds->finisher));
9047 } else {
9048 ceph_assert(!info.ancestors.empty());
9049 info.checking = mds->get_nodeid();
9050 open_ino(info.ancestors[0].dirino, mds->get_metadata_pool(),
9051 new C_MDC_OpenInoParentOpened(this, ino), info.want_replica);
9052 }
9053 }
9054
9055 void MDCache::do_open_ino_peer(inodeno_t ino, open_ino_info_t& info)
9056 {
9057 set<mds_rank_t> all, active;
9058 mds->mdsmap->get_mds_set(all);
9059 if (mds->get_state() == MDSMap::STATE_REJOIN)
9060 mds->mdsmap->get_mds_set_lower_bound(active, MDSMap::STATE_REJOIN);
9061 else
9062 mds->mdsmap->get_mds_set_lower_bound(active, MDSMap::STATE_CLIENTREPLAY);
9063
9064 dout(10) << "do_open_ino_peer " << ino << " active " << active
9065 << " all " << all << " checked " << info.checked << dendl;
9066
9067 mds_rank_t whoami = mds->get_nodeid();
9068 mds_rank_t peer = MDS_RANK_NONE;
9069 if (info.auth_hint >= 0 && info.auth_hint != whoami) {
9070 if (active.count(info.auth_hint)) {
9071 peer = info.auth_hint;
9072 info.auth_hint = MDS_RANK_NONE;
9073 }
9074 } else {
9075 for (set<mds_rank_t>::iterator p = active.begin(); p != active.end(); ++p)
9076 if (*p != whoami && info.checked.count(*p) == 0) {
9077 peer = *p;
9078 break;
9079 }
9080 }
9081 if (peer < 0) {
9082 all.erase(whoami);
9083 if (all != info.checked) {
9084 dout(10) << " waiting for more peers to be active" << dendl;
9085 } else {
9086 dout(10) << " all MDS peers have been checked " << dendl;
9087 do_open_ino(ino, info, 0);
9088 }
9089 } else {
9090 info.checking = peer;
9091 vector<inode_backpointer_t> *pa = NULL;
9092 // got backtrace from peer or backtrace just fetched
9093 if (info.discover || !info.fetch_backtrace)
9094 pa = &info.ancestors;
9095 mds->send_message_mds(make_message<MMDSOpenIno>(info.tid, ino, pa), peer);
9096 if (mds->logger)
9097 mds->logger->inc(l_mds_openino_peer_discover);
9098 }
9099 }
9100
9101 void MDCache::handle_open_ino(const cref_t<MMDSOpenIno> &m, int err)
9102 {
9103 if (mds->get_state() < MDSMap::STATE_REJOIN &&
9104 mds->get_want_state() != CEPH_MDS_STATE_REJOIN) {
9105 return;
9106 }
9107
9108 dout(10) << "handle_open_ino " << *m << " err " << err << dendl;
9109
9110 auto from = mds_rank_t(m->get_source().num());
9111 inodeno_t ino = m->ino;
9112 ref_t<MMDSOpenInoReply> reply;
9113 CInode *in = get_inode(ino);
9114 if (in) {
9115 dout(10) << " have " << *in << dendl;
9116 reply = make_message<MMDSOpenInoReply>(m->get_tid(), ino, mds_rank_t(0));
9117 if (in->is_auth()) {
9118 touch_inode(in);
9119 while (1) {
9120 CDentry *pdn = in->get_parent_dn();
9121 if (!pdn)
9122 break;
9123 CInode *diri = pdn->get_dir()->get_inode();
9124 reply->ancestors.push_back(inode_backpointer_t(diri->ino(), pdn->get_name(),
9125 in->get_version()));
9126 in = diri;
9127 }
9128 } else {
9129 reply->hint = in->authority().first;
9130 }
9131 } else if (err < 0) {
9132 reply = make_message<MMDSOpenInoReply>(m->get_tid(), ino, MDS_RANK_NONE, err);
9133 } else {
9134 mds_rank_t hint = MDS_RANK_NONE;
9135 int ret = open_ino_traverse_dir(ino, m, m->ancestors, false, false, &hint);
9136 if (ret > 0)
9137 return;
9138 reply = make_message<MMDSOpenInoReply>(m->get_tid(), ino, hint, ret);
9139 }
9140 mds->send_message_mds(reply, from);
9141 }
9142
9143 void MDCache::handle_open_ino_reply(const cref_t<MMDSOpenInoReply> &m)
9144 {
9145 dout(10) << "handle_open_ino_reply " << *m << dendl;
9146
9147 inodeno_t ino = m->ino;
9148 mds_rank_t from = mds_rank_t(m->get_source().num());
9149 auto it = opening_inodes.find(ino);
9150 if (it != opening_inodes.end() && it->second.checking == from) {
9151 open_ino_info_t& info = it->second;
9152 info.checking = MDS_RANK_NONE;
9153 info.checked.insert(from);
9154
9155 CInode *in = get_inode(ino);
9156 if (in) {
9157 dout(10) << " found cached " << *in << dendl;
9158 open_ino_finish(ino, info, in->authority().first);
9159 } else if (!m->ancestors.empty()) {
9160 dout(10) << " found ino " << ino << " on mds." << from << dendl;
9161 if (!info.want_replica) {
9162 open_ino_finish(ino, info, from);
9163 return;
9164 }
9165
9166 info.ancestors = m->ancestors;
9167 info.auth_hint = from;
9168 info.checking = mds->get_nodeid();
9169 info.discover = true;
9170 _open_ino_traverse_dir(ino, info, 0);
9171 } else if (m->error) {
9172 dout(10) << " error " << m->error << " from mds." << from << dendl;
9173 do_open_ino(ino, info, m->error);
9174 } else {
9175 if (m->hint >= 0 && m->hint != mds->get_nodeid()) {
9176 info.auth_hint = m->hint;
9177 info.checked.erase(m->hint);
9178 }
9179 do_open_ino_peer(ino, info);
9180 }
9181 }
9182 }
9183
9184 void MDCache::kick_open_ino_peers(mds_rank_t who)
9185 {
9186 dout(10) << "kick_open_ino_peers mds." << who << dendl;
9187
9188 for (map<inodeno_t, open_ino_info_t>::iterator p = opening_inodes.begin();
9189 p != opening_inodes.end();
9190 ++p) {
9191 open_ino_info_t& info = p->second;
9192 if (info.checking == who) {
9193 dout(10) << " kicking ino " << p->first << " who was checking mds." << who << dendl;
9194 info.checking = MDS_RANK_NONE;
9195 do_open_ino_peer(p->first, info);
9196 } else if (info.checking == MDS_RANK_NONE) {
9197 dout(10) << " kicking ino " << p->first << " who was waiting" << dendl;
9198 do_open_ino_peer(p->first, info);
9199 }
9200 }
9201 }
9202
9203 void MDCache::open_ino(inodeno_t ino, int64_t pool, MDSContext* fin,
9204 bool want_replica, bool want_xlocked,
9205 vector<inode_backpointer_t> *ancestors_hint,
9206 mds_rank_t auth_hint)
9207 {
9208 dout(10) << "open_ino " << ino << " pool " << pool << " want_replica "
9209 << want_replica << dendl;
9210
9211 auto it = opening_inodes.find(ino);
9212 if (it != opening_inodes.end()) {
9213 open_ino_info_t& info = it->second;
9214 if (want_replica) {
9215 info.want_replica = true;
9216 if (want_xlocked && !info.want_xlocked) {
9217 if (!info.ancestors.empty()) {
9218 CInode *diri = get_inode(info.ancestors[0].dirino);
9219 if (diri) {
9220 frag_t fg = diri->pick_dirfrag(info.ancestors[0].dname);
9221 CDir *dir = diri->get_dirfrag(fg);
9222 if (dir && !dir->is_auth()) {
9223 filepath path(info.ancestors[0].dname, 0);
9224 discover_path(dir, CEPH_NOSNAP, path, NULL, true);
9225 }
9226 }
9227 }
9228 info.want_xlocked = true;
9229 }
9230 }
9231 info.waiters.push_back(fin);
9232 } else {
9233 open_ino_info_t& info = opening_inodes[ino];
9234 info.want_replica = want_replica;
9235 info.want_xlocked = want_xlocked;
9236 info.tid = ++open_ino_last_tid;
9237 info.pool = pool >= 0 ? pool : default_file_layout.pool_id;
9238 info.waiters.push_back(fin);
9239 if (auth_hint != MDS_RANK_NONE)
9240 info.auth_hint = auth_hint;
9241 if (ancestors_hint) {
9242 info.ancestors = std::move(*ancestors_hint);
9243 info.fetch_backtrace = false;
9244 info.checking = mds->get_nodeid();
9245 _open_ino_traverse_dir(ino, info, 0);
9246 } else {
9247 do_open_ino(ino, info, 0);
9248 }
9249 }
9250 }
9251
9252 /* ---------------------------- */
9253
9254 /*
9255 * search for a given inode on MDS peers. optionally start with the given node.
9256
9257
9258 TODO
9259 - recover from mds node failure, recovery
9260 - traverse path
9261
9262 */
9263 void MDCache::find_ino_peers(inodeno_t ino, MDSContext *c,
9264 mds_rank_t hint, bool path_locked)
9265 {
9266 dout(5) << "find_ino_peers " << ino << " hint " << hint << dendl;
9267 CInode *in = get_inode(ino);
9268 if (in && in->state_test(CInode::STATE_PURGING)) {
9269 c->complete(-CEPHFS_ESTALE);
9270 return;
9271 }
9272 ceph_assert(!in);
9273
9274 ceph_tid_t tid = ++find_ino_peer_last_tid;
9275 find_ino_peer_info_t& fip = find_ino_peer[tid];
9276 fip.ino = ino;
9277 fip.tid = tid;
9278 fip.fin = c;
9279 fip.path_locked = path_locked;
9280 fip.hint = hint;
9281 _do_find_ino_peer(fip);
9282 }
9283
9284 void MDCache::_do_find_ino_peer(find_ino_peer_info_t& fip)
9285 {
9286 set<mds_rank_t> all, active;
9287 mds->mdsmap->get_mds_set(all);
9288 mds->mdsmap->get_mds_set_lower_bound(active, MDSMap::STATE_CLIENTREPLAY);
9289
9290 dout(10) << "_do_find_ino_peer " << fip.tid << " " << fip.ino
9291 << " active " << active << " all " << all
9292 << " checked " << fip.checked
9293 << dendl;
9294
9295 mds_rank_t m = MDS_RANK_NONE;
9296 if (fip.hint >= 0) {
9297 m = fip.hint;
9298 fip.hint = MDS_RANK_NONE;
9299 } else {
9300 for (set<mds_rank_t>::iterator p = active.begin(); p != active.end(); ++p)
9301 if (*p != mds->get_nodeid() &&
9302 fip.checked.count(*p) == 0) {
9303 m = *p;
9304 break;
9305 }
9306 }
9307 if (m == MDS_RANK_NONE) {
9308 all.erase(mds->get_nodeid());
9309 if (all != fip.checked) {
9310 dout(10) << "_do_find_ino_peer waiting for more peers to be active" << dendl;
9311 } else {
9312 dout(10) << "_do_find_ino_peer failed on " << fip.ino << dendl;
9313 fip.fin->complete(-CEPHFS_ESTALE);
9314 find_ino_peer.erase(fip.tid);
9315 }
9316 } else {
9317 fip.checking = m;
9318 mds->send_message_mds(make_message<MMDSFindIno>(fip.tid, fip.ino), m);
9319 }
9320 }
9321
9322 void MDCache::handle_find_ino(const cref_t<MMDSFindIno> &m)
9323 {
9324 if (mds->get_state() < MDSMap::STATE_REJOIN) {
9325 return;
9326 }
9327
9328 dout(10) << "handle_find_ino " << *m << dendl;
9329 auto r = make_message<MMDSFindInoReply>(m->tid);
9330 CInode *in = get_inode(m->ino);
9331 if (in) {
9332 in->make_path(r->path);
9333 dout(10) << " have " << r->path << " " << *in << dendl;
9334
9335 /*
9336 * If the the CInode was just created by using openc in current
9337 * auth MDS, but the client just sends a getattr request to another
9338 * replica MDS. Then here it will make a path of '#INODE-NUMBER'
9339 * only because the CInode hasn't been linked yet, and the replica
9340 * MDS will keep retrying until the auth MDS flushes the mdlog and
9341 * the C_MDS_openc_finish and link_primary_inode are called at most
9342 * 5 seconds later.
9343 */
9344 if (!in->get_parent_dn() && in->is_auth()) {
9345 mds->mdlog->flush();
9346 }
9347 }
9348 mds->send_message_mds(r, mds_rank_t(m->get_source().num()));
9349 }
9350
9351
9352 void MDCache::handle_find_ino_reply(const cref_t<MMDSFindInoReply> &m)
9353 {
9354 auto p = find_ino_peer.find(m->tid);
9355 if (p != find_ino_peer.end()) {
9356 dout(10) << "handle_find_ino_reply " << *m << dendl;
9357 find_ino_peer_info_t& fip = p->second;
9358
9359 // success?
9360 if (get_inode(fip.ino)) {
9361 dout(10) << "handle_find_ino_reply successfully found " << fip.ino << dendl;
9362 mds->queue_waiter(fip.fin);
9363 find_ino_peer.erase(p);
9364 return;
9365 }
9366
9367 mds_rank_t from = mds_rank_t(m->get_source().num());
9368 if (fip.checking == from)
9369 fip.checking = MDS_RANK_NONE;
9370 fip.checked.insert(from);
9371
9372 if (!m->path.empty()) {
9373 // we got a path!
9374 vector<CDentry*> trace;
9375 CF_MDS_RetryMessageFactory cf(mds, m);
9376 MDRequestRef null_ref;
9377 int flags = MDS_TRAVERSE_DISCOVER;
9378 if (fip.path_locked)
9379 flags |= MDS_TRAVERSE_PATH_LOCKED;
9380 int r = path_traverse(null_ref, cf, m->path, flags, &trace);
9381 if (r > 0)
9382 return;
9383 dout(0) << "handle_find_ino_reply failed with " << r << " on " << m->path
9384 << ", retrying" << dendl;
9385 fip.checked.clear();
9386 _do_find_ino_peer(fip);
9387 } else {
9388 // nope, continue.
9389 _do_find_ino_peer(fip);
9390 }
9391 } else {
9392 dout(10) << "handle_find_ino_reply tid " << m->tid << " dne" << dendl;
9393 }
9394 }
9395
9396 void MDCache::kick_find_ino_peers(mds_rank_t who)
9397 {
9398 // find_ino_peers requests we should move on from
9399 for (map<ceph_tid_t,find_ino_peer_info_t>::iterator p = find_ino_peer.begin();
9400 p != find_ino_peer.end();
9401 ++p) {
9402 find_ino_peer_info_t& fip = p->second;
9403 if (fip.checking == who) {
9404 dout(10) << "kicking find_ino_peer " << fip.tid << " who was checking mds." << who << dendl;
9405 fip.checking = MDS_RANK_NONE;
9406 _do_find_ino_peer(fip);
9407 } else if (fip.checking == MDS_RANK_NONE) {
9408 dout(10) << "kicking find_ino_peer " << fip.tid << " who was waiting" << dendl;
9409 _do_find_ino_peer(fip);
9410 }
9411 }
9412 }
9413
9414 /* ---------------------------- */
9415
9416 int MDCache::get_num_client_requests()
9417 {
9418 int count = 0;
9419 for (ceph::unordered_map<metareqid_t, MDRequestRef>::iterator p = active_requests.begin();
9420 p != active_requests.end();
9421 ++p) {
9422 MDRequestRef& mdr = p->second;
9423 if (mdr->reqid.name.is_client() && !mdr->is_peer())
9424 count++;
9425 }
9426 return count;
9427 }
9428
9429 MDRequestRef MDCache::request_start(const cref_t<MClientRequest>& req)
9430 {
9431 // did we win a forward race against a peer?
9432 if (active_requests.count(req->get_reqid())) {
9433 MDRequestRef& mdr = active_requests[req->get_reqid()];
9434 ceph_assert(mdr);
9435 if (mdr->is_peer()) {
9436 dout(10) << "request_start already had " << *mdr << ", waiting for finish" << dendl;
9437 mdr->more()->waiting_for_finish.push_back(new C_MDS_RetryMessage(mds, req));
9438 } else {
9439 dout(10) << "request_start already processing " << *mdr << ", dropping new msg" << dendl;
9440 }
9441 return MDRequestRef();
9442 }
9443
9444 // register new client request
9445 MDRequestImpl::Params params;
9446 params.reqid = req->get_reqid();
9447 params.attempt = req->get_num_fwd();
9448 params.client_req = req;
9449 params.initiated = req->get_recv_stamp();
9450 params.throttled = req->get_throttle_stamp();
9451 params.all_read = req->get_recv_complete_stamp();
9452 params.dispatched = req->get_dispatch_stamp();
9453
9454 MDRequestRef mdr =
9455 mds->op_tracker.create_request<MDRequestImpl,MDRequestImpl::Params*>(&params);
9456 active_requests[params.reqid] = mdr;
9457 mdr->set_op_stamp(req->get_stamp());
9458 dout(7) << "request_start " << *mdr << dendl;
9459 return mdr;
9460 }
9461
9462 MDRequestRef MDCache::request_start_peer(metareqid_t ri, __u32 attempt, const cref_t<Message> &m)
9463 {
9464 int by = m->get_source().num();
9465 MDRequestImpl::Params params;
9466 params.reqid = ri;
9467 params.attempt = attempt;
9468 params.triggering_peer_req = m;
9469 params.peer_to = by;
9470 params.initiated = m->get_recv_stamp();
9471 params.throttled = m->get_throttle_stamp();
9472 params.all_read = m->get_recv_complete_stamp();
9473 params.dispatched = m->get_dispatch_stamp();
9474 MDRequestRef mdr =
9475 mds->op_tracker.create_request<MDRequestImpl,MDRequestImpl::Params*>(&params);
9476 ceph_assert(active_requests.count(mdr->reqid) == 0);
9477 active_requests[mdr->reqid] = mdr;
9478 dout(7) << "request_start_peer " << *mdr << " by mds." << by << dendl;
9479 return mdr;
9480 }
9481
9482 MDRequestRef MDCache::request_start_internal(int op)
9483 {
9484 utime_t now = ceph_clock_now();
9485 MDRequestImpl::Params params;
9486 params.reqid.name = entity_name_t::MDS(mds->get_nodeid());
9487 params.reqid.tid = mds->issue_tid();
9488 params.initiated = now;
9489 params.throttled = now;
9490 params.all_read = now;
9491 params.dispatched = now;
9492 params.internal_op = op;
9493 MDRequestRef mdr =
9494 mds->op_tracker.create_request<MDRequestImpl,MDRequestImpl::Params*>(&params);
9495
9496 ceph_assert(active_requests.count(mdr->reqid) == 0);
9497 active_requests[mdr->reqid] = mdr;
9498 dout(7) << "request_start_internal " << *mdr << " op " << op << dendl;
9499 return mdr;
9500 }
9501
9502 MDRequestRef MDCache::request_get(metareqid_t rid)
9503 {
9504 ceph::unordered_map<metareqid_t, MDRequestRef>::iterator p = active_requests.find(rid);
9505 ceph_assert(p != active_requests.end());
9506 dout(7) << "request_get " << rid << " " << *p->second << dendl;
9507 return p->second;
9508 }
9509
9510 void MDCache::request_finish(MDRequestRef& mdr)
9511 {
9512 dout(7) << "request_finish " << *mdr << dendl;
9513 mdr->mark_event("finishing request");
9514
9515 // peer finisher?
9516 if (mdr->has_more() && mdr->more()->peer_commit) {
9517 Context *fin = mdr->more()->peer_commit;
9518 mdr->more()->peer_commit = 0;
9519 int ret;
9520 if (mdr->aborted) {
9521 mdr->aborted = false;
9522 ret = -1;
9523 mdr->more()->peer_rolling_back = true;
9524 } else {
9525 ret = 0;
9526 mdr->committing = true;
9527 }
9528 fin->complete(ret); // this must re-call request_finish.
9529 return;
9530 }
9531
9532 switch(mdr->internal_op) {
9533 case CEPH_MDS_OP_FRAGMENTDIR:
9534 logger->inc(l_mdss_ireq_fragmentdir);
9535 break;
9536 case CEPH_MDS_OP_EXPORTDIR:
9537 logger->inc(l_mdss_ireq_exportdir);
9538 break;
9539 case CEPH_MDS_OP_ENQUEUE_SCRUB:
9540 logger->inc(l_mdss_ireq_enqueue_scrub);
9541 break;
9542 case CEPH_MDS_OP_FLUSH:
9543 logger->inc(l_mdss_ireq_flush);
9544 break;
9545 case CEPH_MDS_OP_REPAIR_FRAGSTATS:
9546 logger->inc(l_mdss_ireq_fragstats);
9547 break;
9548 case CEPH_MDS_OP_REPAIR_INODESTATS:
9549 logger->inc(l_mdss_ireq_inodestats);
9550 break;
9551 }
9552
9553 request_cleanup(mdr);
9554 }
9555
9556
9557 void MDCache::request_forward(MDRequestRef& mdr, mds_rank_t who, int port)
9558 {
9559 CachedStackStringStream css;
9560 *css << "forwarding request to mds." << who;
9561 mdr->mark_event(css->strv());
9562 if (mdr->client_request && mdr->client_request->get_source().is_client()) {
9563 dout(7) << "request_forward " << *mdr << " to mds." << who << " req "
9564 << *mdr->client_request << dendl;
9565 if (mdr->is_batch_head()) {
9566 mdr->release_batch_op()->forward(who);
9567 } else {
9568 mds->forward_message_mds(mdr->release_client_request(), who);
9569 }
9570 if (mds->logger) mds->logger->inc(l_mds_forward);
9571 } else if (mdr->internal_op >= 0) {
9572 dout(10) << "request_forward on internal op; cancelling" << dendl;
9573 mdr->internal_op_finish->complete(-CEPHFS_EXDEV);
9574 } else {
9575 dout(7) << "request_forward drop " << *mdr << " req " << *mdr->client_request
9576 << " was from mds" << dendl;
9577 }
9578 request_cleanup(mdr);
9579 }
9580
9581
9582 void MDCache::dispatch_request(MDRequestRef& mdr)
9583 {
9584 if (mdr->client_request) {
9585 mds->server->dispatch_client_request(mdr);
9586 } else if (mdr->peer_request) {
9587 mds->server->dispatch_peer_request(mdr);
9588 } else {
9589 switch (mdr->internal_op) {
9590 case CEPH_MDS_OP_FRAGMENTDIR:
9591 dispatch_fragment_dir(mdr);
9592 break;
9593 case CEPH_MDS_OP_EXPORTDIR:
9594 migrator->dispatch_export_dir(mdr, 0);
9595 break;
9596 case CEPH_MDS_OP_ENQUEUE_SCRUB:
9597 enqueue_scrub_work(mdr);
9598 break;
9599 case CEPH_MDS_OP_FLUSH:
9600 flush_dentry_work(mdr);
9601 break;
9602 case CEPH_MDS_OP_REPAIR_FRAGSTATS:
9603 repair_dirfrag_stats_work(mdr);
9604 break;
9605 case CEPH_MDS_OP_REPAIR_INODESTATS:
9606 repair_inode_stats_work(mdr);
9607 break;
9608 case CEPH_MDS_OP_RDLOCK_FRAGSSTATS:
9609 rdlock_dirfrags_stats_work(mdr);
9610 break;
9611 default:
9612 ceph_abort();
9613 }
9614 }
9615 }
9616
9617
9618 void MDCache::request_drop_foreign_locks(MDRequestRef& mdr)
9619 {
9620 if (!mdr->has_more())
9621 return;
9622
9623 // clean up peers
9624 // (will implicitly drop remote dn pins)
9625 for (set<mds_rank_t>::iterator p = mdr->more()->peers.begin();
9626 p != mdr->more()->peers.end();
9627 ++p) {
9628 auto r = make_message<MMDSPeerRequest>(mdr->reqid, mdr->attempt,
9629 MMDSPeerRequest::OP_FINISH);
9630
9631 if (mdr->killed && !mdr->committing) {
9632 r->mark_abort();
9633 } else if (mdr->more()->srcdn_auth_mds == *p &&
9634 mdr->more()->inode_import.length() > 0) {
9635 // information about rename imported caps
9636 r->inode_export = std::move(mdr->more()->inode_import);
9637 }
9638
9639 mds->send_message_mds(r, *p);
9640 }
9641
9642 /* strip foreign xlocks out of lock lists, since the OP_FINISH drops them
9643 * implicitly. Note that we don't call the finishers -- there shouldn't
9644 * be any on a remote lock and the request finish wakes up all
9645 * the waiters anyway! */
9646
9647 for (auto it = mdr->locks.begin(); it != mdr->locks.end(); ) {
9648 SimpleLock *lock = it->lock;
9649 if (it->is_xlock() && !lock->get_parent()->is_auth()) {
9650 dout(10) << "request_drop_foreign_locks forgetting lock " << *lock
9651 << " on " << lock->get_parent() << dendl;
9652 lock->put_xlock();
9653 mdr->locks.erase(it++);
9654 } else if (it->is_remote_wrlock()) {
9655 dout(10) << "request_drop_foreign_locks forgetting remote_wrlock " << *lock
9656 << " on mds." << it->wrlock_target << " on " << *lock->get_parent() << dendl;
9657 if (it->is_wrlock()) {
9658 it->clear_remote_wrlock();
9659 ++it;
9660 } else {
9661 mdr->locks.erase(it++);
9662 }
9663 } else {
9664 ++it;
9665 }
9666 }
9667
9668 mdr->more()->peers.clear(); /* we no longer have requests out to them, and
9669 * leaving them in can cause double-notifies as
9670 * this function can get called more than once */
9671 }
9672
9673 void MDCache::request_drop_non_rdlocks(MDRequestRef& mdr)
9674 {
9675 request_drop_foreign_locks(mdr);
9676 mds->locker->drop_non_rdlocks(mdr.get());
9677 }
9678
9679 void MDCache::request_drop_locks(MDRequestRef& mdr)
9680 {
9681 request_drop_foreign_locks(mdr);
9682 mds->locker->drop_locks(mdr.get());
9683 }
9684
9685 void MDCache::request_cleanup(MDRequestRef& mdr)
9686 {
9687 dout(15) << "request_cleanup " << *mdr << dendl;
9688
9689 if (mdr->has_more()) {
9690 if (mdr->more()->is_ambiguous_auth)
9691 mdr->clear_ambiguous_auth();
9692 if (!mdr->more()->waiting_for_finish.empty())
9693 mds->queue_waiters(mdr->more()->waiting_for_finish);
9694 }
9695
9696 request_drop_locks(mdr);
9697
9698 // drop (local) auth pins
9699 mdr->drop_local_auth_pins();
9700
9701 // drop stickydirs
9702 mdr->put_stickydirs();
9703
9704 mds->locker->kick_cap_releases(mdr);
9705
9706 // drop cache pins
9707 mdr->drop_pins();
9708
9709 // remove from session
9710 mdr->item_session_request.remove_myself();
9711
9712 // remove from map
9713 active_requests.erase(mdr->reqid);
9714
9715 if (mds->logger)
9716 log_stat();
9717
9718 mdr->mark_event("cleaned up request");
9719 }
9720
9721 void MDCache::request_kill(MDRequestRef& mdr)
9722 {
9723 // rollback peer requests is tricky. just let the request proceed.
9724 if (mdr->has_more() &&
9725 (!mdr->more()->witnessed.empty() || !mdr->more()->waiting_on_peer.empty())) {
9726 if (!(mdr->locking_state & MutationImpl::ALL_LOCKED)) {
9727 ceph_assert(mdr->more()->witnessed.empty());
9728 mdr->aborted = true;
9729 dout(10) << "request_kill " << *mdr << " -- waiting for peer reply, delaying" << dendl;
9730 } else {
9731 dout(10) << "request_kill " << *mdr << " -- already started peer prep, no-op" << dendl;
9732 }
9733
9734 ceph_assert(mdr->used_prealloc_ino == 0);
9735 ceph_assert(mdr->prealloc_inos.empty());
9736
9737 mdr->session = NULL;
9738 mdr->item_session_request.remove_myself();
9739 return;
9740 }
9741
9742 mdr->killed = true;
9743 mdr->mark_event("killing request");
9744
9745 if (mdr->committing) {
9746 dout(10) << "request_kill " << *mdr << " -- already committing, remove it from sesssion requests" << dendl;
9747 mdr->item_session_request.remove_myself();
9748 } else {
9749 dout(10) << "request_kill " << *mdr << dendl;
9750 request_cleanup(mdr);
9751 }
9752 }
9753
9754 // -------------------------------------------------------------------------------
9755 // SNAPREALMS
9756
9757 void MDCache::create_global_snaprealm()
9758 {
9759 CInode *in = new CInode(this); // dummy inode
9760 create_unlinked_system_inode(in, CEPH_INO_GLOBAL_SNAPREALM, S_IFDIR|0755);
9761 add_inode(in);
9762 global_snaprealm = in->snaprealm;
9763 }
9764
9765 void MDCache::do_realm_invalidate_and_update_notify(CInode *in, int snapop, bool notify_clients)
9766 {
9767 dout(10) << "do_realm_invalidate_and_update_notify " << *in->snaprealm << " " << *in << dendl;
9768
9769 vector<inodeno_t> split_inos;
9770 vector<inodeno_t> split_realms;
9771
9772 if (notify_clients) {
9773 if (snapop == CEPH_SNAP_OP_SPLIT) {
9774 // notify clients of update|split
9775 for (auto p = in->snaprealm->inodes_with_caps.begin(); !p.end(); ++p)
9776 split_inos.push_back((*p)->ino());
9777
9778 for (auto& r : in->snaprealm->open_children)
9779 split_realms.push_back(r->inode->ino());
9780 }
9781 }
9782
9783 map<client_t, ref_t<MClientSnap>> updates;
9784 list<SnapRealm*> q;
9785 q.push_back(in->snaprealm);
9786 while (!q.empty()) {
9787 SnapRealm *realm = q.front();
9788 q.pop_front();
9789
9790 dout(10) << " realm " << *realm << " on " << *realm->inode << dendl;
9791 realm->invalidate_cached_snaps();
9792
9793 if (notify_clients) {
9794 for (const auto& p : realm->client_caps) {
9795 const auto& client = p.first;
9796 const auto& caps = p.second;
9797 ceph_assert(!caps->empty());
9798
9799 auto em = updates.emplace(std::piecewise_construct, std::forward_as_tuple(client), std::forward_as_tuple());
9800 if (em.second) {
9801 auto update = make_message<MClientSnap>(CEPH_SNAP_OP_SPLIT);
9802 update->head.split = in->ino();
9803 update->split_inos = split_inos;
9804 update->split_realms = split_realms;
9805 update->bl = in->snaprealm->get_snap_trace();
9806 em.first->second = std::move(update);
9807 }
9808 }
9809 }
9810
9811 // notify for active children, too.
9812 dout(10) << " " << realm << " open_children are " << realm->open_children << dendl;
9813 for (auto& r : realm->open_children)
9814 q.push_back(r);
9815 }
9816
9817 if (notify_clients)
9818 send_snaps(updates);
9819 }
9820
9821 void MDCache::send_snap_update(CInode *in, version_t stid, int snap_op)
9822 {
9823 dout(10) << __func__ << " " << *in << " stid " << stid << dendl;
9824 ceph_assert(in->is_auth());
9825
9826 set<mds_rank_t> mds_set;
9827 if (stid > 0) {
9828 mds->mdsmap->get_mds_set_lower_bound(mds_set, MDSMap::STATE_RESOLVE);
9829 mds_set.erase(mds->get_nodeid());
9830 } else {
9831 in->list_replicas(mds_set);
9832 }
9833
9834 if (!mds_set.empty()) {
9835 bufferlist snap_blob;
9836 in->encode_snap(snap_blob);
9837
9838 for (auto p : mds_set) {
9839 auto m = make_message<MMDSSnapUpdate>(in->ino(), stid, snap_op);
9840 m->snap_blob = snap_blob;
9841 mds->send_message_mds(m, p);
9842 }
9843 }
9844
9845 if (stid > 0)
9846 notify_global_snaprealm_update(snap_op);
9847 }
9848
9849 void MDCache::handle_snap_update(const cref_t<MMDSSnapUpdate> &m)
9850 {
9851 mds_rank_t from = mds_rank_t(m->get_source().num());
9852 dout(10) << __func__ << " " << *m << " from mds." << from << dendl;
9853
9854 if (mds->get_state() < MDSMap::STATE_RESOLVE &&
9855 mds->get_want_state() != CEPH_MDS_STATE_RESOLVE) {
9856 return;
9857 }
9858
9859 // null rejoin_done means open_snaprealms() has already been called
9860 bool notify_clients = mds->get_state() > MDSMap::STATE_REJOIN ||
9861 (mds->is_rejoin() && !rejoin_done);
9862
9863 if (m->get_tid() > 0) {
9864 mds->snapclient->notify_commit(m->get_tid());
9865 if (notify_clients)
9866 notify_global_snaprealm_update(m->get_snap_op());
9867 }
9868
9869 CInode *in = get_inode(m->get_ino());
9870 if (in) {
9871 ceph_assert(!in->is_auth());
9872 if (mds->get_state() > MDSMap::STATE_REJOIN ||
9873 (mds->is_rejoin() && !in->is_rejoining())) {
9874 auto p = m->snap_blob.cbegin();
9875 in->decode_snap(p);
9876
9877 if (!notify_clients) {
9878 if (!rejoin_pending_snaprealms.count(in)) {
9879 in->get(CInode::PIN_OPENINGSNAPPARENTS);
9880 rejoin_pending_snaprealms.insert(in);
9881 }
9882 }
9883 do_realm_invalidate_and_update_notify(in, m->get_snap_op(), notify_clients);
9884 }
9885 }
9886 }
9887
9888 void MDCache::notify_global_snaprealm_update(int snap_op)
9889 {
9890 if (snap_op != CEPH_SNAP_OP_DESTROY)
9891 snap_op = CEPH_SNAP_OP_UPDATE;
9892 set<Session*> sessions;
9893 mds->sessionmap.get_client_session_set(sessions);
9894 for (auto &session : sessions) {
9895 if (!session->is_open() && !session->is_stale())
9896 continue;
9897 auto update = make_message<MClientSnap>(snap_op);
9898 update->head.split = global_snaprealm->inode->ino();
9899 update->bl = global_snaprealm->get_snap_trace();
9900 mds->send_message_client_counted(update, session);
9901 }
9902 }
9903
9904 // -------------------------------------------------------------------------------
9905 // STRAYS
9906
9907 struct C_MDC_RetryScanStray : public MDCacheContext {
9908 dirfrag_t next;
9909 C_MDC_RetryScanStray(MDCache *c, dirfrag_t n) : MDCacheContext(c), next(n) { }
9910 void finish(int r) override {
9911 mdcache->scan_stray_dir(next);
9912 }
9913 };
9914
9915 void MDCache::scan_stray_dir(dirfrag_t next)
9916 {
9917 dout(10) << "scan_stray_dir " << next << dendl;
9918
9919 if (next.ino)
9920 next.frag = strays[MDS_INO_STRAY_INDEX(next.ino)]->dirfragtree[next.frag.value()];
9921
9922 for (int i = 0; i < NUM_STRAY; ++i) {
9923 if (strays[i]->ino() < next.ino)
9924 continue;
9925
9926 std::vector<CDir*> ls;
9927 strays[i]->get_dirfrags(ls);
9928
9929 for (const auto& dir : ls) {
9930 if (dir->get_frag() < next.frag)
9931 continue;
9932
9933 if (!dir->can_auth_pin()) {
9934 dir->add_waiter(CDir::WAIT_UNFREEZE, new C_MDC_RetryScanStray(this, dir->dirfrag()));
9935 return;
9936 }
9937
9938 if (!dir->is_complete()) {
9939 dir->fetch(new C_MDC_RetryScanStray(this, dir->dirfrag()));
9940 return;
9941 }
9942
9943 for (auto &p : dir->items) {
9944 CDentry *dn = p.second;
9945 dn->state_set(CDentry::STATE_STRAY);
9946 CDentry::linkage_t *dnl = dn->get_projected_linkage();
9947 if (dnl->is_primary()) {
9948 CInode *in = dnl->get_inode();
9949 if (in->get_inode()->nlink == 0)
9950 in->state_set(CInode::STATE_ORPHAN);
9951 maybe_eval_stray(in);
9952 }
9953 }
9954 }
9955 }
9956 }
9957
9958 void MDCache::fetch_backtrace(inodeno_t ino, int64_t pool, bufferlist& bl, Context *fin)
9959 {
9960 object_t oid = CInode::get_object_name(ino, frag_t(), "");
9961 mds->objecter->getxattr(oid, object_locator_t(pool), "parent", CEPH_NOSNAP, &bl, 0, fin);
9962 if (mds->logger)
9963 mds->logger->inc(l_mds_openino_backtrace_fetch);
9964 }
9965
9966
9967
9968
9969
9970 // ========================================================================================
9971 // DISCOVER
9972 /*
9973
9974 - for all discovers (except base_inos, e.g. root, stray), waiters are attached
9975 to the parent metadata object in the cache (pinning it).
9976
9977 - all discovers are tracked by tid, so that we can ignore potentially dup replies.
9978
9979 */
9980
9981 void MDCache::_send_discover(discover_info_t& d)
9982 {
9983 auto dis = make_message<MDiscover>(d.ino, d.frag, d.snap, d.want_path,
9984 d.want_base_dir, d.path_locked);
9985 dis->set_tid(d.tid);
9986 mds->send_message_mds(dis, d.mds);
9987 }
9988
9989 void MDCache::discover_base_ino(inodeno_t want_ino,
9990 MDSContext *onfinish,
9991 mds_rank_t from)
9992 {
9993 dout(7) << "discover_base_ino " << want_ino << " from mds." << from << dendl;
9994 if (waiting_for_base_ino[from].count(want_ino) == 0) {
9995 discover_info_t& d = _create_discover(from);
9996 d.ino = want_ino;
9997 _send_discover(d);
9998 }
9999 waiting_for_base_ino[from][want_ino].push_back(onfinish);
10000 }
10001
10002
10003 void MDCache::discover_dir_frag(CInode *base,
10004 frag_t approx_fg,
10005 MDSContext *onfinish,
10006 mds_rank_t from)
10007 {
10008 if (from < 0)
10009 from = base->authority().first;
10010
10011 dirfrag_t df(base->ino(), approx_fg);
10012 dout(7) << "discover_dir_frag " << df
10013 << " from mds." << from << dendl;
10014
10015 if (!base->is_waiting_for_dir(approx_fg) || !onfinish) {
10016 discover_info_t& d = _create_discover(from);
10017 d.pin_base(base);
10018 d.ino = base->ino();
10019 d.frag = approx_fg;
10020 d.want_base_dir = true;
10021 _send_discover(d);
10022 }
10023
10024 if (onfinish)
10025 base->add_dir_waiter(approx_fg, onfinish);
10026 }
10027
10028 struct C_MDC_RetryDiscoverPath : public MDCacheContext {
10029 CInode *base;
10030 snapid_t snapid;
10031 filepath path;
10032 mds_rank_t from;
10033 C_MDC_RetryDiscoverPath(MDCache *c, CInode *b, snapid_t s, filepath &p, mds_rank_t f) :
10034 MDCacheContext(c), base(b), snapid(s), path(p), from(f) {}
10035 void finish(int r) override {
10036 mdcache->discover_path(base, snapid, path, 0, from);
10037 }
10038 };
10039
10040 void MDCache::discover_path(CInode *base,
10041 snapid_t snap,
10042 filepath want_path,
10043 MDSContext *onfinish,
10044 bool path_locked,
10045 mds_rank_t from)
10046 {
10047 if (from < 0)
10048 from = base->authority().first;
10049
10050 dout(7) << "discover_path " << base->ino() << " " << want_path << " snap " << snap << " from mds." << from
10051 << (path_locked ? " path_locked":"")
10052 << dendl;
10053
10054 if (base->is_ambiguous_auth()) {
10055 dout(10) << " waiting for single auth on " << *base << dendl;
10056 if (!onfinish)
10057 onfinish = new C_MDC_RetryDiscoverPath(this, base, snap, want_path, from);
10058 base->add_waiter(CInode::WAIT_SINGLEAUTH, onfinish);
10059 return;
10060 } else if (from == mds->get_nodeid()) {
10061 MDSContext::vec finished;
10062 base->take_waiting(CInode::WAIT_DIR, finished);
10063 mds->queue_waiters(finished);
10064 return;
10065 }
10066
10067 frag_t fg = base->pick_dirfrag(want_path[0]);
10068 if ((path_locked && want_path.depth() == 1) ||
10069 !base->is_waiting_for_dir(fg) || !onfinish) {
10070 discover_info_t& d = _create_discover(from);
10071 d.ino = base->ino();
10072 d.pin_base(base);
10073 d.frag = fg;
10074 d.snap = snap;
10075 d.want_path = want_path;
10076 d.want_base_dir = true;
10077 d.path_locked = path_locked;
10078 _send_discover(d);
10079 }
10080
10081 // register + wait
10082 if (onfinish)
10083 base->add_dir_waiter(fg, onfinish);
10084 }
10085
10086 struct C_MDC_RetryDiscoverPath2 : public MDCacheContext {
10087 CDir *base;
10088 snapid_t snapid;
10089 filepath path;
10090 C_MDC_RetryDiscoverPath2(MDCache *c, CDir *b, snapid_t s, filepath &p) :
10091 MDCacheContext(c), base(b), snapid(s), path(p) {}
10092 void finish(int r) override {
10093 mdcache->discover_path(base, snapid, path, 0);
10094 }
10095 };
10096
10097 void MDCache::discover_path(CDir *base,
10098 snapid_t snap,
10099 filepath want_path,
10100 MDSContext *onfinish,
10101 bool path_locked)
10102 {
10103 mds_rank_t from = base->authority().first;
10104
10105 dout(7) << "discover_path " << base->dirfrag() << " " << want_path << " snap " << snap << " from mds." << from
10106 << (path_locked ? " path_locked":"")
10107 << dendl;
10108
10109 if (base->is_ambiguous_auth()) {
10110 dout(7) << " waiting for single auth on " << *base << dendl;
10111 if (!onfinish)
10112 onfinish = new C_MDC_RetryDiscoverPath2(this, base, snap, want_path);
10113 base->add_waiter(CDir::WAIT_SINGLEAUTH, onfinish);
10114 return;
10115 } else if (from == mds->get_nodeid()) {
10116 MDSContext::vec finished;
10117 base->take_sub_waiting(finished);
10118 mds->queue_waiters(finished);
10119 return;
10120 }
10121
10122 if ((path_locked && want_path.depth() == 1) ||
10123 !base->is_waiting_for_dentry(want_path[0].c_str(), snap) || !onfinish) {
10124 discover_info_t& d = _create_discover(from);
10125 d.ino = base->ino();
10126 d.pin_base(base->inode);
10127 d.frag = base->get_frag();
10128 d.snap = snap;
10129 d.want_path = want_path;
10130 d.want_base_dir = false;
10131 d.path_locked = path_locked;
10132 _send_discover(d);
10133 }
10134
10135 // register + wait
10136 if (onfinish)
10137 base->add_dentry_waiter(want_path[0], snap, onfinish);
10138 }
10139
10140 void MDCache::kick_discovers(mds_rank_t who)
10141 {
10142 for (map<ceph_tid_t,discover_info_t>::iterator p = discovers.begin();
10143 p != discovers.end();
10144 ++p) {
10145 if (p->second.mds != who)
10146 continue;
10147 _send_discover(p->second);
10148 }
10149 }
10150
10151
10152 void MDCache::handle_discover(const cref_t<MDiscover> &dis)
10153 {
10154 mds_rank_t whoami = mds->get_nodeid();
10155 mds_rank_t from = mds_rank_t(dis->get_source().num());
10156
10157 ceph_assert(from != whoami);
10158
10159 if (mds->get_state() <= MDSMap::STATE_REJOIN) {
10160 if (mds->get_state() < MDSMap::STATE_REJOIN &&
10161 mds->get_want_state() < CEPH_MDS_STATE_REJOIN) {
10162 return;
10163 }
10164
10165 // proceed if requester is in the REJOIN stage, the request is from parallel_fetch().
10166 // delay processing request from survivor because we may not yet choose lock states.
10167 if (!mds->mdsmap->is_rejoin(from)) {
10168 dout(0) << "discover_reply not yet active(|still rejoining), delaying" << dendl;
10169 mds->wait_for_replay(new C_MDS_RetryMessage(mds, dis));
10170 return;
10171 }
10172 }
10173
10174
10175 CInode *cur = 0;
10176 auto reply = make_message<MDiscoverReply>(*dis);
10177
10178 snapid_t snapid = dis->get_snapid();
10179
10180 // get started.
10181 if (MDS_INO_IS_BASE(dis->get_base_ino()) &&
10182 !dis->wants_base_dir() && dis->get_want().depth() == 0) {
10183 // wants root
10184 dout(7) << "handle_discover from mds." << from
10185 << " wants base + " << dis->get_want().get_path()
10186 << " snap " << snapid
10187 << dendl;
10188
10189 cur = get_inode(dis->get_base_ino());
10190 ceph_assert(cur);
10191
10192 // add root
10193 reply->starts_with = MDiscoverReply::INODE;
10194 encode_replica_inode(cur, from, reply->trace, mds->mdsmap->get_up_features());
10195 dout(10) << "added base " << *cur << dendl;
10196 }
10197 else {
10198 // there's a base inode
10199 cur = get_inode(dis->get_base_ino(), snapid);
10200 if (!cur && snapid != CEPH_NOSNAP) {
10201 cur = get_inode(dis->get_base_ino());
10202 if (cur && !cur->is_multiversion())
10203 cur = NULL; // nope!
10204 }
10205
10206 if (!cur) {
10207 dout(7) << "handle_discover mds." << from
10208 << " don't have base ino " << dis->get_base_ino() << "." << snapid
10209 << dendl;
10210 if (!dis->wants_base_dir() && dis->get_want().depth() > 0)
10211 reply->set_error_dentry(dis->get_dentry(0));
10212 reply->set_flag_error_dir();
10213 } else if (dis->wants_base_dir()) {
10214 dout(7) << "handle_discover mds." << from
10215 << " wants basedir+" << dis->get_want().get_path()
10216 << " has " << *cur
10217 << dendl;
10218 } else {
10219 dout(7) << "handle_discover mds." << from
10220 << " wants " << dis->get_want().get_path()
10221 << " has " << *cur
10222 << dendl;
10223 }
10224 }
10225
10226 ceph_assert(reply);
10227
10228 // add content
10229 // do some fidgeting to include a dir if they asked for the base dir, or just root.
10230 for (unsigned i = 0;
10231 cur && (i < dis->get_want().depth() || dis->get_want().depth() == 0);
10232 i++) {
10233
10234 // -- figure out the dir
10235
10236 // is *cur even a dir at all?
10237 if (!cur->is_dir()) {
10238 dout(7) << *cur << " not a dir" << dendl;
10239 reply->set_flag_error_dir();
10240 break;
10241 }
10242
10243 // pick frag
10244 frag_t fg;
10245 if (dis->get_want().depth()) {
10246 // dentry specifies
10247 fg = cur->pick_dirfrag(dis->get_dentry(i));
10248 } else {
10249 // requester explicity specified the frag
10250 ceph_assert(dis->wants_base_dir() || MDS_INO_IS_BASE(dis->get_base_ino()));
10251 fg = dis->get_base_dir_frag();
10252 if (!cur->dirfragtree.is_leaf(fg))
10253 fg = cur->dirfragtree[fg.value()];
10254 }
10255 CDir *curdir = cur->get_dirfrag(fg);
10256
10257 if ((!curdir && !cur->is_auth()) ||
10258 (curdir && !curdir->is_auth())) {
10259
10260 /* before:
10261 * ONLY set flag if empty!!
10262 * otherwise requester will wake up waiter(s) _and_ continue with discover,
10263 * resulting in duplicate discovers in flight,
10264 * which can wreak havoc when discovering rename srcdn (which may move)
10265 */
10266
10267 if (reply->is_empty()) {
10268 // only hint if empty.
10269 // someday this could be better, but right now the waiter logic isn't smart enough.
10270
10271 // hint
10272 if (curdir) {
10273 dout(7) << " not dirfrag auth, setting dir_auth_hint for " << *curdir << dendl;
10274 reply->set_dir_auth_hint(curdir->authority().first);
10275 } else {
10276 dout(7) << " dirfrag not open, not inode auth, setting dir_auth_hint for "
10277 << *cur << dendl;
10278 reply->set_dir_auth_hint(cur->authority().first);
10279 }
10280
10281 // note error dentry, if any
10282 // NOTE: important, as it allows requester to issue an equivalent discover
10283 // to whomever we hint at.
10284 if (dis->get_want().depth() > i)
10285 reply->set_error_dentry(dis->get_dentry(i));
10286 }
10287
10288 break;
10289 }
10290
10291 if (!curdir) { // open dir?
10292 if (cur->is_frozen()) {
10293 if (!reply->is_empty()) {
10294 dout(7) << *cur << " is frozen, non-empty reply, stopping" << dendl;
10295 break;
10296 }
10297 dout(7) << *cur << " is frozen, empty reply, waiting" << dendl;
10298 cur->add_waiter(CInode::WAIT_UNFREEZE, new C_MDS_RetryMessage(mds, dis));
10299 return;
10300 }
10301 curdir = cur->get_or_open_dirfrag(this, fg);
10302 } else if (curdir->is_frozen_tree() ||
10303 (curdir->is_frozen_dir() && fragment_are_all_frozen(curdir))) {
10304 if (!reply->is_empty()) {
10305 dout(7) << *curdir << " is frozen, non-empty reply, stopping" << dendl;
10306 break;
10307 }
10308 if (dis->wants_base_dir() && dis->get_base_dir_frag() != curdir->get_frag()) {
10309 dout(7) << *curdir << " is frozen, dirfrag mismatch, stopping" << dendl;
10310 reply->set_flag_error_dir();
10311 break;
10312 }
10313 dout(7) << *curdir << " is frozen, empty reply, waiting" << dendl;
10314 curdir->add_waiter(CDir::WAIT_UNFREEZE, new C_MDS_RetryMessage(mds, dis));
10315 return;
10316 }
10317
10318 // add dir
10319 if (curdir->get_version() == 0) {
10320 // fetch newly opened dir
10321 } else if (reply->is_empty() && !dis->wants_base_dir()) {
10322 dout(7) << "handle_discover not adding unwanted base dir " << *curdir << dendl;
10323 // make sure the base frag is correct, though, in there was a refragment since the
10324 // original request was sent.
10325 reply->set_base_dir_frag(curdir->get_frag());
10326 } else {
10327 ceph_assert(!curdir->is_ambiguous_auth()); // would be frozen.
10328 if (!reply->trace.length())
10329 reply->starts_with = MDiscoverReply::DIR;
10330 encode_replica_dir(curdir, from, reply->trace);
10331 dout(7) << "handle_discover added dir " << *curdir << dendl;
10332 }
10333
10334 // lookup
10335 CDentry *dn = 0;
10336 if (curdir->get_version() == 0) {
10337 // fetch newly opened dir
10338 ceph_assert(!curdir->has_bloom());
10339 } else if (dis->get_want().depth() > 0) {
10340 // lookup dentry
10341 dn = curdir->lookup(dis->get_dentry(i), snapid);
10342 } else
10343 break; // done!
10344
10345 // incomplete dir?
10346 if (!dn) {
10347 if (!curdir->is_complete() &&
10348 !(snapid == CEPH_NOSNAP &&
10349 curdir->has_bloom() &&
10350 !curdir->is_in_bloom(dis->get_dentry(i)))) {
10351 // readdir
10352 dout(7) << "incomplete dir contents for " << *curdir << ", fetching" << dendl;
10353 if (reply->is_empty()) {
10354 // fetch and wait
10355 curdir->fetch(new C_MDS_RetryMessage(mds, dis),
10356 dis->wants_base_dir() && curdir->get_version() == 0);
10357 return;
10358 } else {
10359 // initiate fetch, but send what we have so far
10360 curdir->fetch(0);
10361 break;
10362 }
10363 }
10364
10365 if (snapid != CEPH_NOSNAP && !reply->is_empty()) {
10366 dout(7) << "dentry " << dis->get_dentry(i) << " snap " << snapid
10367 << " dne, non-empty reply, stopping" << dendl;
10368 break;
10369 }
10370
10371 // send null dentry
10372 dout(7) << "dentry " << dis->get_dentry(i) << " dne, returning null in "
10373 << *curdir << dendl;
10374 if (snapid == CEPH_NOSNAP)
10375 dn = curdir->add_null_dentry(dis->get_dentry(i));
10376 else
10377 dn = curdir->add_null_dentry(dis->get_dentry(i), snapid, snapid);
10378 }
10379 ceph_assert(dn);
10380
10381 // don't add replica to purging dentry/inode
10382 if (dn->state_test(CDentry::STATE_PURGING)) {
10383 if (reply->is_empty())
10384 reply->set_flag_error_dn(dis->get_dentry(i));
10385 break;
10386 }
10387
10388 CDentry::linkage_t *dnl = dn->get_linkage();
10389
10390 // xlocked dentry?
10391 // ...always block on non-tail items (they are unrelated)
10392 // ...allow xlocked tail disocvery _only_ if explicitly requested
10393 if (dn->lock.is_xlocked()) {
10394 // is this the last (tail) item in the discover traversal?
10395 if (dis->is_path_locked()) {
10396 dout(7) << "handle_discover allowing discovery of xlocked " << *dn << dendl;
10397 } else if (reply->is_empty()) {
10398 dout(7) << "handle_discover blocking on xlocked " << *dn << dendl;
10399 dn->lock.add_waiter(SimpleLock::WAIT_RD, new C_MDS_RetryMessage(mds, dis));
10400 return;
10401 } else {
10402 dout(7) << "handle_discover non-empty reply, xlocked tail " << *dn << dendl;
10403 break;
10404 }
10405 }
10406
10407 // frozen inode?
10408 bool tailitem = (dis->get_want().depth() == 0) || (i == dis->get_want().depth() - 1);
10409 if (dnl->is_primary() && dnl->get_inode()->is_frozen_inode()) {
10410 if (tailitem && dis->is_path_locked()) {
10411 dout(7) << "handle_discover allowing discovery of frozen tail " << *dnl->get_inode() << dendl;
10412 } else if (reply->is_empty()) {
10413 dout(7) << *dnl->get_inode() << " is frozen, empty reply, waiting" << dendl;
10414 dnl->get_inode()->add_waiter(CDir::WAIT_UNFREEZE, new C_MDS_RetryMessage(mds, dis));
10415 return;
10416 } else {
10417 dout(7) << *dnl->get_inode() << " is frozen, non-empty reply, stopping" << dendl;
10418 break;
10419 }
10420 }
10421
10422 // add dentry
10423 if (!reply->trace.length())
10424 reply->starts_with = MDiscoverReply::DENTRY;
10425 encode_replica_dentry(dn, from, reply->trace);
10426 dout(7) << "handle_discover added dentry " << *dn << dendl;
10427
10428 if (!dnl->is_primary()) break; // stop on null or remote link.
10429
10430 // add inode
10431 CInode *next = dnl->get_inode();
10432 ceph_assert(next->is_auth());
10433
10434 encode_replica_inode(next, from, reply->trace, mds->mdsmap->get_up_features());
10435 dout(7) << "handle_discover added inode " << *next << dendl;
10436
10437 // descend, keep going.
10438 cur = next;
10439 continue;
10440 }
10441
10442 // how did we do?
10443 ceph_assert(!reply->is_empty());
10444 dout(7) << "handle_discover sending result back to asker mds." << from << dendl;
10445 mds->send_message(reply, dis->get_connection());
10446 }
10447
10448 void MDCache::handle_discover_reply(const cref_t<MDiscoverReply> &m)
10449 {
10450 /*
10451 if (mds->get_state() < MDSMap::STATE_ACTIVE) {
10452 dout(0) << "discover_reply NOT ACTIVE YET" << dendl;
10453 return;
10454 }
10455 */
10456 dout(7) << "discover_reply " << *m << dendl;
10457 if (m->is_flag_error_dir())
10458 dout(7) << " flag error, dir" << dendl;
10459 if (m->is_flag_error_dn())
10460 dout(7) << " flag error, dentry = " << m->get_error_dentry() << dendl;
10461
10462 MDSContext::vec finished, error;
10463 mds_rank_t from = mds_rank_t(m->get_source().num());
10464
10465 // starting point
10466 CInode *cur = get_inode(m->get_base_ino());
10467 auto p = m->trace.cbegin();
10468
10469 int next = m->starts_with;
10470
10471 // decrement discover counters
10472 if (m->get_tid()) {
10473 map<ceph_tid_t,discover_info_t>::iterator p = discovers.find(m->get_tid());
10474 if (p != discovers.end()) {
10475 dout(10) << " found tid " << m->get_tid() << dendl;
10476 discovers.erase(p);
10477 } else {
10478 dout(10) << " tid " << m->get_tid() << " not found, must be dup reply" << dendl;
10479 }
10480 }
10481
10482 // discover may start with an inode
10483 if (!p.end() && next == MDiscoverReply::INODE) {
10484 decode_replica_inode(cur, p, NULL, finished);
10485 dout(7) << "discover_reply got base inode " << *cur << dendl;
10486 ceph_assert(cur->is_base());
10487
10488 next = MDiscoverReply::DIR;
10489
10490 // take waiters?
10491 if (cur->is_base() &&
10492 waiting_for_base_ino[from].count(cur->ino())) {
10493 finished.swap(waiting_for_base_ino[from][cur->ino()]);
10494 waiting_for_base_ino[from].erase(cur->ino());
10495 }
10496 }
10497 ceph_assert(cur);
10498
10499 // loop over discover results.
10500 // indexes follow each ([[dir] dentry] inode)
10501 // can start, end with any type.
10502 while (!p.end()) {
10503 // dir
10504 frag_t fg;
10505 CDir *curdir = nullptr;
10506 if (next == MDiscoverReply::DIR) {
10507 decode_replica_dir(curdir, p, cur, mds_rank_t(m->get_source().num()), finished);
10508 if (cur->ino() == m->get_base_ino() && curdir->get_frag() != m->get_base_dir_frag()) {
10509 ceph_assert(m->get_wanted_base_dir());
10510 cur->take_dir_waiting(m->get_base_dir_frag(), finished);
10511 }
10512 } else {
10513 // note: this can only happen our first way around this loop.
10514 if (p.end() && m->is_flag_error_dn()) {
10515 fg = cur->pick_dirfrag(m->get_error_dentry());
10516 curdir = cur->get_dirfrag(fg);
10517 } else
10518 curdir = cur->get_dirfrag(m->get_base_dir_frag());
10519 }
10520
10521 if (p.end())
10522 break;
10523
10524 // dentry
10525 CDentry *dn = nullptr;
10526 decode_replica_dentry(dn, p, curdir, finished);
10527
10528 if (p.end())
10529 break;
10530
10531 // inode
10532 decode_replica_inode(cur, p, dn, finished);
10533
10534 next = MDiscoverReply::DIR;
10535 }
10536
10537 // dir error?
10538 // or dir_auth hint?
10539 if (m->is_flag_error_dir() && !cur->is_dir()) {
10540 // not a dir.
10541 cur->take_waiting(CInode::WAIT_DIR, error);
10542 } else if (m->is_flag_error_dir() || m->get_dir_auth_hint() != CDIR_AUTH_UNKNOWN) {
10543 mds_rank_t who = m->get_dir_auth_hint();
10544 if (who == mds->get_nodeid()) who = -1;
10545 if (who >= 0)
10546 dout(7) << " dir_auth_hint is " << m->get_dir_auth_hint() << dendl;
10547
10548
10549 if (m->get_wanted_base_dir()) {
10550 frag_t fg = m->get_base_dir_frag();
10551 CDir *dir = cur->get_dirfrag(fg);
10552
10553 if (cur->is_waiting_for_dir(fg)) {
10554 if (cur->is_auth())
10555 cur->take_waiting(CInode::WAIT_DIR, finished);
10556 else if (dir || !cur->dirfragtree.is_leaf(fg))
10557 cur->take_dir_waiting(fg, finished);
10558 else
10559 discover_dir_frag(cur, fg, 0, who);
10560 } else
10561 dout(7) << " doing nothing, nobody is waiting for dir" << dendl;
10562 }
10563
10564 // try again?
10565 if (m->get_error_dentry().length()) {
10566 frag_t fg = cur->pick_dirfrag(m->get_error_dentry());
10567 CDir *dir = cur->get_dirfrag(fg);
10568 // wanted a dentry
10569 if (dir && dir->is_waiting_for_dentry(m->get_error_dentry(), m->get_wanted_snapid())) {
10570 if (dir->is_auth() || dir->lookup(m->get_error_dentry())) {
10571 dir->take_dentry_waiting(m->get_error_dentry(), m->get_wanted_snapid(),
10572 m->get_wanted_snapid(), finished);
10573 } else {
10574 filepath relpath(m->get_error_dentry(), 0);
10575 discover_path(dir, m->get_wanted_snapid(), relpath, 0, m->is_path_locked());
10576 }
10577 } else
10578 dout(7) << " doing nothing, have dir but nobody is waiting on dentry "
10579 << m->get_error_dentry() << dendl;
10580 }
10581 } else if (m->is_flag_error_dn()) {
10582 frag_t fg = cur->pick_dirfrag(m->get_error_dentry());
10583 CDir *dir = cur->get_dirfrag(fg);
10584 if (dir) {
10585 if (dir->is_auth()) {
10586 dir->take_sub_waiting(finished);
10587 } else {
10588 dir->take_dentry_waiting(m->get_error_dentry(), m->get_wanted_snapid(),
10589 m->get_wanted_snapid(), error);
10590 }
10591 }
10592 }
10593
10594 // waiters
10595 finish_contexts(g_ceph_context, error, -CEPHFS_ENOENT); // finish errors directly
10596 mds->queue_waiters(finished);
10597 }
10598
10599
10600
10601 // ----------------------------
10602 // REPLICAS
10603
10604
10605 void MDCache::encode_replica_dir(CDir *dir, mds_rank_t to, bufferlist& bl)
10606 {
10607 ENCODE_START(1, 1, bl);
10608 dirfrag_t df = dir->dirfrag();
10609 encode(df, bl);
10610 __u32 nonce = dir->add_replica(to);
10611 encode(nonce, bl);
10612 dir->_encode_base(bl);
10613 ENCODE_FINISH(bl);
10614 }
10615
10616 void MDCache::encode_replica_dentry(CDentry *dn, mds_rank_t to, bufferlist& bl)
10617 {
10618 ENCODE_START(2, 1, bl);
10619 encode(dn->get_name(), bl);
10620 encode(dn->last, bl);
10621
10622 __u32 nonce = dn->add_replica(to);
10623 encode(nonce, bl);
10624 encode(dn->first, bl);
10625 encode(dn->linkage.remote_ino, bl);
10626 encode(dn->linkage.remote_d_type, bl);
10627 dn->lock.encode_state_for_replica(bl);
10628 bool need_recover = mds->get_state() < MDSMap::STATE_ACTIVE;
10629 encode(need_recover, bl);
10630 encode(dn->alternate_name, bl);
10631 ENCODE_FINISH(bl);
10632 }
10633
10634 void MDCache::encode_replica_inode(CInode *in, mds_rank_t to, bufferlist& bl,
10635 uint64_t features)
10636 {
10637 ceph_assert(in->is_auth());
10638
10639 ENCODE_START(2, 1, bl);
10640 encode(in->ino(), bl); // bleh, minor assymetry here
10641 encode(in->last, bl);
10642
10643 __u32 nonce = in->add_replica(to);
10644 encode(nonce, bl);
10645
10646 in->_encode_base(bl, features);
10647 in->_encode_locks_state_for_replica(bl, mds->get_state() < MDSMap::STATE_ACTIVE);
10648
10649 __u32 state = in->state;
10650 encode(state, bl);
10651
10652 ENCODE_FINISH(bl);
10653 }
10654
10655 void MDCache::decode_replica_dir(CDir *&dir, bufferlist::const_iterator& p, CInode *diri, mds_rank_t from,
10656 MDSContext::vec& finished)
10657 {
10658 DECODE_START(1, p);
10659 dirfrag_t df;
10660 decode(df, p);
10661
10662 ceph_assert(diri->ino() == df.ino);
10663
10664 // add it (_replica_)
10665 dir = diri->get_dirfrag(df.frag);
10666
10667 if (dir) {
10668 // had replica. update w/ new nonce.
10669 __u32 nonce;
10670 decode(nonce, p);
10671 dir->set_replica_nonce(nonce);
10672 dir->_decode_base(p);
10673 dout(7) << __func__ << " had " << *dir << " nonce " << dir->replica_nonce << dendl;
10674 } else {
10675 // force frag to leaf in the diri tree
10676 if (!diri->dirfragtree.is_leaf(df.frag)) {
10677 dout(7) << __func__ << " forcing frag " << df.frag << " to leaf in the fragtree "
10678 << diri->dirfragtree << dendl;
10679 diri->dirfragtree.force_to_leaf(g_ceph_context, df.frag);
10680 }
10681 // add replica.
10682 dir = diri->add_dirfrag( new CDir(diri, df.frag, this, false) );
10683 __u32 nonce;
10684 decode(nonce, p);
10685 dir->set_replica_nonce(nonce);
10686 dir->_decode_base(p);
10687 // is this a dir_auth delegation boundary?
10688 if (from != diri->authority().first ||
10689 diri->is_ambiguous_auth() ||
10690 diri->is_base())
10691 adjust_subtree_auth(dir, from);
10692
10693 dout(7) << __func__ << " added " << *dir << " nonce " << dir->replica_nonce << dendl;
10694 // get waiters
10695 diri->take_dir_waiting(df.frag, finished);
10696 }
10697 DECODE_FINISH(p);
10698 }
10699
10700 void MDCache::decode_replica_dentry(CDentry *&dn, bufferlist::const_iterator& p, CDir *dir, MDSContext::vec& finished)
10701 {
10702 DECODE_START(1, p);
10703 string name;
10704 snapid_t last;
10705 decode(name, p);
10706 decode(last, p);
10707
10708 dn = dir->lookup(name, last);
10709
10710 // have it?
10711 bool is_new = false;
10712 if (dn) {
10713 is_new = false;
10714 dout(7) << __func__ << " had " << *dn << dendl;
10715 } else {
10716 is_new = true;
10717 dn = dir->add_null_dentry(name, 1 /* this will get updated below */, last);
10718 dout(7) << __func__ << " added " << *dn << dendl;
10719 }
10720
10721 __u32 nonce;
10722 decode(nonce, p);
10723 dn->set_replica_nonce(nonce);
10724 decode(dn->first, p);
10725
10726 inodeno_t rino;
10727 unsigned char rdtype;
10728 decode(rino, p);
10729 decode(rdtype, p);
10730 dn->lock.decode_state(p, is_new);
10731
10732 bool need_recover;
10733 decode(need_recover, p);
10734
10735 mempool::mds_co::string alternate_name;
10736 if (struct_v >= 2) {
10737 decode(alternate_name, p);
10738 }
10739
10740 if (is_new) {
10741 dn->set_alternate_name(std::move(alternate_name));
10742 if (rino)
10743 dir->link_remote_inode(dn, rino, rdtype);
10744 if (need_recover)
10745 dn->lock.mark_need_recover();
10746 } else {
10747 ceph_assert(dn->alternate_name == alternate_name);
10748 }
10749
10750 dir->take_dentry_waiting(name, dn->first, dn->last, finished);
10751 DECODE_FINISH(p);
10752 }
10753
10754 void MDCache::decode_replica_inode(CInode *&in, bufferlist::const_iterator& p, CDentry *dn, MDSContext::vec& finished)
10755 {
10756 DECODE_START(2, p);
10757 inodeno_t ino;
10758 snapid_t last;
10759 __u32 nonce;
10760 decode(ino, p);
10761 decode(last, p);
10762 decode(nonce, p);
10763 in = get_inode(ino, last);
10764 if (!in) {
10765 in = new CInode(this, false, 2, last);
10766 in->set_replica_nonce(nonce);
10767 in->_decode_base(p);
10768 in->_decode_locks_state_for_replica(p, true);
10769 add_inode(in);
10770 if (in->ino() == CEPH_INO_ROOT)
10771 in->inode_auth.first = 0;
10772 else if (in->is_mdsdir())
10773 in->inode_auth.first = in->ino() - MDS_INO_MDSDIR_OFFSET;
10774 dout(10) << __func__ << " added " << *in << dendl;
10775 if (dn) {
10776 ceph_assert(dn->get_linkage()->is_null());
10777 dn->dir->link_primary_inode(dn, in);
10778 }
10779 } else {
10780 in->set_replica_nonce(nonce);
10781 in->_decode_base(p);
10782 in->_decode_locks_state_for_replica(p, false);
10783 dout(10) << __func__ << " had " << *in << dendl;
10784 }
10785
10786 if (dn) {
10787 if (!dn->get_linkage()->is_primary() || dn->get_linkage()->get_inode() != in)
10788 dout(10) << __func__ << " different linkage in dentry " << *dn << dendl;
10789 }
10790
10791 if (struct_v >= 2) {
10792 __u32 s;
10793 decode(s, p);
10794 s &= CInode::MASK_STATE_REPLICATED;
10795 if (s & CInode::STATE_RANDEPHEMERALPIN) {
10796 dout(10) << "replica inode is random ephemeral pinned" << dendl;
10797 in->set_ephemeral_pin(false, true);
10798 }
10799 }
10800
10801 DECODE_FINISH(p);
10802 }
10803
10804
10805 void MDCache::encode_replica_stray(CDentry *straydn, mds_rank_t who, bufferlist& bl)
10806 {
10807 ceph_assert(straydn->get_num_auth_pins());
10808 ENCODE_START(2, 1, bl);
10809 uint64_t features = mds->mdsmap->get_up_features();
10810 encode_replica_inode(get_myin(), who, bl, features);
10811 encode_replica_dir(straydn->get_dir()->inode->get_parent_dn()->get_dir(), who, bl);
10812 encode_replica_dentry(straydn->get_dir()->inode->get_parent_dn(), who, bl);
10813 encode_replica_inode(straydn->get_dir()->inode, who, bl, features);
10814 encode_replica_dir(straydn->get_dir(), who, bl);
10815 encode_replica_dentry(straydn, who, bl);
10816 if (!straydn->get_projected_linkage()->is_null()) {
10817 encode_replica_inode(straydn->get_projected_linkage()->get_inode(), who, bl, features);
10818 }
10819 ENCODE_FINISH(bl);
10820 }
10821
10822 void MDCache::decode_replica_stray(CDentry *&straydn, CInode **in, const bufferlist &bl, mds_rank_t from)
10823 {
10824 MDSContext::vec finished;
10825 auto p = bl.cbegin();
10826
10827 DECODE_START(2, p);
10828 CInode *mdsin = nullptr;
10829 decode_replica_inode(mdsin, p, NULL, finished);
10830 CDir *mdsdir = nullptr;
10831 decode_replica_dir(mdsdir, p, mdsin, from, finished);
10832 CDentry *straydirdn = nullptr;
10833 decode_replica_dentry(straydirdn, p, mdsdir, finished);
10834 CInode *strayin = nullptr;
10835 decode_replica_inode(strayin, p, straydirdn, finished);
10836 CDir *straydir = nullptr;
10837 decode_replica_dir(straydir, p, strayin, from, finished);
10838
10839 decode_replica_dentry(straydn, p, straydir, finished);
10840 if (struct_v >= 2 && in) {
10841 decode_replica_inode(*in, p, straydn, finished);
10842 }
10843 if (!finished.empty())
10844 mds->queue_waiters(finished);
10845 DECODE_FINISH(p);
10846 }
10847
10848
10849 int MDCache::send_dir_updates(CDir *dir, bool bcast)
10850 {
10851 // this is an FYI, re: replication
10852
10853 set<mds_rank_t> who;
10854 if (bcast) {
10855 set<mds_rank_t> mds_set;
10856 mds->get_mds_map()->get_active_mds_set(mds_set);
10857
10858 set<mds_rank_t> replica_set;
10859 for (const auto &p : dir->get_replicas()) {
10860 replica_set.insert(p.first);
10861 }
10862
10863 std::set_difference(mds_set.begin(), mds_set.end(),
10864 replica_set.begin(), replica_set.end(),
10865 std::inserter(who, who.end()));
10866 } else {
10867 for (const auto &p : dir->get_replicas()) {
10868 who.insert(p.first);
10869 }
10870 }
10871
10872 dout(7) << "sending dir_update on " << *dir << " bcast " << bcast << " to " << who << dendl;
10873
10874 filepath path;
10875 dir->inode->make_path(path);
10876
10877 std::set<int32_t> dir_rep_set;
10878 for (const auto &r : dir->dir_rep_by) {
10879 dir_rep_set.insert(r);
10880 }
10881
10882 mds_rank_t whoami = mds->get_nodeid();
10883 for (set<mds_rank_t>::iterator it = who.begin();
10884 it != who.end();
10885 ++it) {
10886 if (*it == whoami) continue;
10887 //if (*it == except) continue;
10888 dout(7) << "sending dir_update on " << *dir << " to " << *it << dendl;
10889
10890 mds->send_message_mds(make_message<MDirUpdate>(mds->get_nodeid(), dir->dirfrag(), dir->dir_rep, dir_rep_set, path, bcast), *it);
10891 }
10892
10893 return 0;
10894 }
10895
10896 void MDCache::handle_dir_update(const cref_t<MDirUpdate> &m)
10897 {
10898 dirfrag_t df = m->get_dirfrag();
10899 CDir *dir = get_dirfrag(df);
10900 if (!dir) {
10901 dout(5) << "dir_update on " << df << ", don't have it" << dendl;
10902
10903 // discover it?
10904 if (m->should_discover()) {
10905 // only try once!
10906 // this is key to avoid a fragtree update race, among other things.
10907 m->inc_tried_discover();
10908 vector<CDentry*> trace;
10909 CInode *in;
10910 filepath path = m->get_path();
10911 dout(5) << "trying discover on dir_update for " << path << dendl;
10912 CF_MDS_RetryMessageFactory cf(mds, m);
10913 MDRequestRef null_ref;
10914 int r = path_traverse(null_ref, cf, path, MDS_TRAVERSE_DISCOVER, &trace, &in);
10915 if (r > 0)
10916 return;
10917 if (r == 0 &&
10918 in->ino() == df.ino &&
10919 in->get_approx_dirfrag(df.frag) == NULL) {
10920 open_remote_dirfrag(in, df.frag, new C_MDS_RetryMessage(mds, m));
10921 return;
10922 }
10923 }
10924
10925 return;
10926 }
10927
10928 if (!m->has_tried_discover()) {
10929 // Update if it already exists. Othwerwise it got updated by discover reply.
10930 dout(5) << "dir_update on " << *dir << dendl;
10931 dir->dir_rep = m->get_dir_rep();
10932 dir->dir_rep_by.clear();
10933 for (const auto &e : m->get_dir_rep_by()) {
10934 dir->dir_rep_by.insert(e);
10935 }
10936 }
10937 }
10938
10939
10940
10941
10942
10943 // LINK
10944
10945 void MDCache::encode_remote_dentry_link(CDentry::linkage_t *dnl, bufferlist& bl)
10946 {
10947 ENCODE_START(1, 1, bl);
10948 inodeno_t ino = dnl->get_remote_ino();
10949 encode(ino, bl);
10950 __u8 d_type = dnl->get_remote_d_type();
10951 encode(d_type, bl);
10952 ENCODE_FINISH(bl);
10953 }
10954
10955 void MDCache::decode_remote_dentry_link(CDir *dir, CDentry *dn, bufferlist::const_iterator& p)
10956 {
10957 DECODE_START(1, p);
10958 inodeno_t ino;
10959 __u8 d_type;
10960 decode(ino, p);
10961 decode(d_type, p);
10962 dout(10) << __func__ << " remote " << ino << " " << d_type << dendl;
10963 dir->link_remote_inode(dn, ino, d_type);
10964 DECODE_FINISH(p);
10965 }
10966
10967 void MDCache::send_dentry_link(CDentry *dn, MDRequestRef& mdr)
10968 {
10969 dout(7) << __func__ << " " << *dn << dendl;
10970
10971 CDir *subtree = get_subtree_root(dn->get_dir());
10972 for (const auto &p : dn->get_replicas()) {
10973 // don't tell (rename) witnesses; they already know
10974 if (mdr.get() && mdr->more()->witnessed.count(p.first))
10975 continue;
10976 if (mds->mdsmap->get_state(p.first) < MDSMap::STATE_REJOIN ||
10977 (mds->mdsmap->get_state(p.first) == MDSMap::STATE_REJOIN &&
10978 rejoin_gather.count(p.first)))
10979 continue;
10980 CDentry::linkage_t *dnl = dn->get_linkage();
10981 auto m = make_message<MDentryLink>(subtree->dirfrag(), dn->get_dir()->dirfrag(), dn->get_name(), dnl->is_primary());
10982 if (dnl->is_primary()) {
10983 dout(10) << __func__ << " primary " << *dnl->get_inode() << dendl;
10984 encode_replica_inode(dnl->get_inode(), p.first, m->bl,
10985 mds->mdsmap->get_up_features());
10986 } else if (dnl->is_remote()) {
10987 encode_remote_dentry_link(dnl, m->bl);
10988 } else
10989 ceph_abort(); // aie, bad caller!
10990 mds->send_message_mds(m, p.first);
10991 }
10992 }
10993
10994 void MDCache::handle_dentry_link(const cref_t<MDentryLink> &m)
10995 {
10996 CDentry *dn = NULL;
10997 CDir *dir = get_dirfrag(m->get_dirfrag());
10998 if (!dir) {
10999 dout(7) << __func__ << " don't have dirfrag " << m->get_dirfrag() << dendl;
11000 } else {
11001 dn = dir->lookup(m->get_dn());
11002 if (!dn) {
11003 dout(7) << __func__ << " don't have dentry " << *dir << " dn " << m->get_dn() << dendl;
11004 } else {
11005 dout(7) << __func__ << " on " << *dn << dendl;
11006 CDentry::linkage_t *dnl = dn->get_linkage();
11007
11008 ceph_assert(!dn->is_auth());
11009 ceph_assert(dnl->is_null());
11010 }
11011 }
11012
11013 auto p = m->bl.cbegin();
11014 MDSContext::vec finished;
11015 if (dn) {
11016 if (m->get_is_primary()) {
11017 // primary link.
11018 CInode *in = nullptr;
11019 decode_replica_inode(in, p, dn, finished);
11020 } else {
11021 // remote link, easy enough.
11022 decode_remote_dentry_link(dir, dn, p);
11023 }
11024 } else {
11025 ceph_abort();
11026 }
11027
11028 if (!finished.empty())
11029 mds->queue_waiters(finished);
11030
11031 return;
11032 }
11033
11034
11035 // UNLINK
11036
11037 void MDCache::send_dentry_unlink(CDentry *dn, CDentry *straydn, MDRequestRef& mdr)
11038 {
11039 dout(10) << __func__ << " " << *dn << dendl;
11040 // share unlink news with replicas
11041 set<mds_rank_t> replicas;
11042 dn->list_replicas(replicas);
11043 bufferlist snapbl;
11044 if (straydn) {
11045 straydn->list_replicas(replicas);
11046 CInode *strayin = straydn->get_linkage()->get_inode();
11047 strayin->encode_snap_blob(snapbl);
11048 }
11049 for (set<mds_rank_t>::iterator it = replicas.begin();
11050 it != replicas.end();
11051 ++it) {
11052 // don't tell (rmdir) witnesses; they already know
11053 if (mdr.get() && mdr->more()->witnessed.count(*it))
11054 continue;
11055
11056 if (mds->mdsmap->get_state(*it) < MDSMap::STATE_REJOIN ||
11057 (mds->mdsmap->get_state(*it) == MDSMap::STATE_REJOIN &&
11058 rejoin_gather.count(*it)))
11059 continue;
11060
11061 auto unlink = make_message<MDentryUnlink>(dn->get_dir()->dirfrag(), dn->get_name());
11062 if (straydn) {
11063 encode_replica_stray(straydn, *it, unlink->straybl);
11064 unlink->snapbl = snapbl;
11065 }
11066 mds->send_message_mds(unlink, *it);
11067 }
11068 }
11069
11070 void MDCache::handle_dentry_unlink(const cref_t<MDentryUnlink> &m)
11071 {
11072 // straydn
11073 CDentry *straydn = nullptr;
11074 CInode *strayin = nullptr;
11075 if (m->straybl.length())
11076 decode_replica_stray(straydn, &strayin, m->straybl, mds_rank_t(m->get_source().num()));
11077
11078 CDir *dir = get_dirfrag(m->get_dirfrag());
11079 if (!dir) {
11080 dout(7) << __func__ << " don't have dirfrag " << m->get_dirfrag() << dendl;
11081 } else {
11082 CDentry *dn = dir->lookup(m->get_dn());
11083 if (!dn) {
11084 dout(7) << __func__ << " don't have dentry " << *dir << " dn " << m->get_dn() << dendl;
11085 } else {
11086 dout(7) << __func__ << " on " << *dn << dendl;
11087 CDentry::linkage_t *dnl = dn->get_linkage();
11088
11089 // open inode?
11090 if (dnl->is_primary()) {
11091 CInode *in = dnl->get_inode();
11092 dn->dir->unlink_inode(dn);
11093 ceph_assert(straydn);
11094 straydn->dir->link_primary_inode(straydn, in);
11095
11096 // in->first is lazily updated on replica; drag it forward so
11097 // that we always keep it in sync with the dnq
11098 ceph_assert(straydn->first >= in->first);
11099 in->first = straydn->first;
11100
11101 // update subtree map?
11102 if (in->is_dir())
11103 adjust_subtree_after_rename(in, dir, false);
11104
11105 if (m->snapbl.length()) {
11106 bool hadrealm = (in->snaprealm ? true : false);
11107 in->decode_snap_blob(m->snapbl);
11108 ceph_assert(in->snaprealm);
11109 if (!hadrealm)
11110 do_realm_invalidate_and_update_notify(in, CEPH_SNAP_OP_SPLIT, false);
11111 }
11112
11113 // send caps to auth (if we're not already)
11114 if (in->is_any_caps() &&
11115 !in->state_test(CInode::STATE_EXPORTINGCAPS))
11116 migrator->export_caps(in);
11117
11118 straydn = NULL;
11119 } else {
11120 ceph_assert(!straydn);
11121 ceph_assert(dnl->is_remote());
11122 dn->dir->unlink_inode(dn);
11123 }
11124 ceph_assert(dnl->is_null());
11125 }
11126 }
11127
11128 // race with trim_dentry()
11129 if (straydn) {
11130 ceph_assert(straydn->get_num_ref() == 0);
11131 ceph_assert(straydn->get_linkage()->is_null());
11132 expiremap ex;
11133 trim_dentry(straydn, ex);
11134 send_expire_messages(ex);
11135 }
11136 }
11137
11138
11139
11140
11141
11142
11143 // ===================================================================
11144
11145
11146
11147 // ===================================================================
11148 // FRAGMENT
11149
11150
11151 /**
11152 * adjust_dir_fragments -- adjust fragmentation for a directory
11153 *
11154 * @param diri directory inode
11155 * @param basefrag base fragment
11156 * @param bits bit adjustment. positive for split, negative for merge.
11157 */
11158 void MDCache::adjust_dir_fragments(CInode *diri, frag_t basefrag, int bits,
11159 std::vector<CDir*>* resultfrags,
11160 MDSContext::vec& waiters,
11161 bool replay)
11162 {
11163 dout(10) << "adjust_dir_fragments " << basefrag << " " << bits
11164 << " on " << *diri << dendl;
11165
11166 auto&& p = diri->get_dirfrags_under(basefrag);
11167
11168 adjust_dir_fragments(diri, p.second, basefrag, bits, resultfrags, waiters, replay);
11169 }
11170
11171 CDir *MDCache::force_dir_fragment(CInode *diri, frag_t fg, bool replay)
11172 {
11173 CDir *dir = diri->get_dirfrag(fg);
11174 if (dir)
11175 return dir;
11176
11177 dout(10) << "force_dir_fragment " << fg << " on " << *diri << dendl;
11178
11179 std::vector<CDir*> src, result;
11180 MDSContext::vec waiters;
11181
11182 // split a parent?
11183 frag_t parent = diri->dirfragtree.get_branch_or_leaf(fg);
11184 while (1) {
11185 CDir *pdir = diri->get_dirfrag(parent);
11186 if (pdir) {
11187 int split = fg.bits() - parent.bits();
11188 dout(10) << " splitting parent by " << split << " " << *pdir << dendl;
11189 src.push_back(pdir);
11190 adjust_dir_fragments(diri, src, parent, split, &result, waiters, replay);
11191 dir = diri->get_dirfrag(fg);
11192 if (dir) {
11193 dout(10) << "force_dir_fragment result " << *dir << dendl;
11194 break;
11195 }
11196 }
11197 if (parent == frag_t())
11198 break;
11199 frag_t last = parent;
11200 parent = parent.parent();
11201 dout(10) << " " << last << " parent is " << parent << dendl;
11202 }
11203
11204 if (!dir) {
11205 // hoover up things under fg?
11206 {
11207 auto&& p = diri->get_dirfrags_under(fg);
11208 src.insert(std::end(src), std::cbegin(p.second), std::cend(p.second));
11209 }
11210 if (src.empty()) {
11211 dout(10) << "force_dir_fragment no frags under " << fg << dendl;
11212 } else {
11213 dout(10) << " will combine frags under " << fg << ": " << src << dendl;
11214 adjust_dir_fragments(diri, src, fg, 0, &result, waiters, replay);
11215 dir = result.front();
11216 dout(10) << "force_dir_fragment result " << *dir << dendl;
11217 }
11218 }
11219 if (!replay)
11220 mds->queue_waiters(waiters);
11221 return dir;
11222 }
11223
11224 void MDCache::adjust_dir_fragments(CInode *diri,
11225 const std::vector<CDir*>& srcfrags,
11226 frag_t basefrag, int bits,
11227 std::vector<CDir*>* resultfrags,
11228 MDSContext::vec& waiters,
11229 bool replay)
11230 {
11231 dout(10) << "adjust_dir_fragments " << basefrag << " bits " << bits
11232 << " srcfrags " << srcfrags
11233 << " on " << *diri << dendl;
11234
11235 // adjust fragtree
11236 // yuck. we may have discovered the inode while it was being fragmented.
11237 if (!diri->dirfragtree.is_leaf(basefrag))
11238 diri->dirfragtree.force_to_leaf(g_ceph_context, basefrag);
11239
11240 if (bits > 0)
11241 diri->dirfragtree.split(basefrag, bits);
11242 dout(10) << " new fragtree is " << diri->dirfragtree << dendl;
11243
11244 if (srcfrags.empty())
11245 return;
11246
11247 // split
11248 CDir *parent_dir = diri->get_parent_dir();
11249 CDir *parent_subtree = 0;
11250 if (parent_dir)
11251 parent_subtree = get_subtree_root(parent_dir);
11252
11253 ceph_assert(srcfrags.size() >= 1);
11254 if (bits > 0) {
11255 // SPLIT
11256 ceph_assert(srcfrags.size() == 1);
11257 CDir *dir = srcfrags.front();
11258
11259 dir->split(bits, resultfrags, waiters, replay);
11260
11261 // did i change the subtree map?
11262 if (dir->is_subtree_root()) {
11263 // new frags are now separate subtrees
11264 for (const auto& dir : *resultfrags) {
11265 subtrees[dir].clear(); // new frag is now its own subtree
11266 }
11267
11268 // was i a bound?
11269 if (parent_subtree) {
11270 ceph_assert(subtrees[parent_subtree].count(dir));
11271 subtrees[parent_subtree].erase(dir);
11272 for (const auto& dir : *resultfrags) {
11273 ceph_assert(dir->is_subtree_root());
11274 subtrees[parent_subtree].insert(dir);
11275 }
11276 }
11277
11278 // adjust my bounds.
11279 set<CDir*> bounds;
11280 bounds.swap(subtrees[dir]);
11281 subtrees.erase(dir);
11282 for (set<CDir*>::iterator p = bounds.begin();
11283 p != bounds.end();
11284 ++p) {
11285 CDir *frag = get_subtree_root((*p)->get_parent_dir());
11286 subtrees[frag].insert(*p);
11287 }
11288
11289 show_subtrees(10);
11290 }
11291
11292 diri->close_dirfrag(dir->get_frag());
11293
11294 } else {
11295 // MERGE
11296
11297 // are my constituent bits subtrees? if so, i will be too.
11298 // (it's all or none, actually.)
11299 bool any_subtree = false, any_non_subtree = false;
11300 for (const auto& dir : srcfrags) {
11301 if (dir->is_subtree_root())
11302 any_subtree = true;
11303 else
11304 any_non_subtree = true;
11305 }
11306 ceph_assert(!any_subtree || !any_non_subtree);
11307
11308 set<CDir*> new_bounds;
11309 if (any_subtree) {
11310 for (const auto& dir : srcfrags) {
11311 // this simplifies the code that find subtrees underneath the dirfrag
11312 if (!dir->is_subtree_root()) {
11313 dir->state_set(CDir::STATE_AUXSUBTREE);
11314 adjust_subtree_auth(dir, mds->get_nodeid());
11315 }
11316 }
11317
11318 for (const auto& dir : srcfrags) {
11319 ceph_assert(dir->is_subtree_root());
11320 dout(10) << " taking srcfrag subtree bounds from " << *dir << dendl;
11321 map<CDir*, set<CDir*> >::iterator q = subtrees.find(dir);
11322 set<CDir*>::iterator r = q->second.begin();
11323 while (r != subtrees[dir].end()) {
11324 new_bounds.insert(*r);
11325 subtrees[dir].erase(r++);
11326 }
11327 subtrees.erase(q);
11328
11329 // remove myself as my parent's bound
11330 if (parent_subtree)
11331 subtrees[parent_subtree].erase(dir);
11332 }
11333 }
11334
11335 // merge
11336 CDir *f = new CDir(diri, basefrag, this, srcfrags.front()->is_auth());
11337 f->merge(srcfrags, waiters, replay);
11338
11339 if (any_subtree) {
11340 ceph_assert(f->is_subtree_root());
11341 subtrees[f].swap(new_bounds);
11342 if (parent_subtree)
11343 subtrees[parent_subtree].insert(f);
11344
11345 show_subtrees(10);
11346 }
11347
11348 resultfrags->push_back(f);
11349 }
11350 }
11351
11352
11353 class C_MDC_FragmentFrozen : public MDSInternalContext {
11354 MDCache *mdcache;
11355 MDRequestRef mdr;
11356 public:
11357 C_MDC_FragmentFrozen(MDCache *m, MDRequestRef& r) :
11358 MDSInternalContext(m->mds), mdcache(m), mdr(r) {}
11359 void finish(int r) override {
11360 mdcache->fragment_frozen(mdr, r);
11361 }
11362 };
11363
11364 bool MDCache::can_fragment(CInode *diri, const std::vector<CDir*>& dirs)
11365 {
11366 if (is_readonly()) {
11367 dout(7) << "can_fragment: read-only FS, no fragmenting for now" << dendl;
11368 return false;
11369 }
11370 if (mds->is_cluster_degraded()) {
11371 dout(7) << "can_fragment: cluster degraded, no fragmenting for now" << dendl;
11372 return false;
11373 }
11374 if (diri->get_parent_dir() &&
11375 diri->get_parent_dir()->get_inode()->is_stray()) {
11376 dout(7) << "can_fragment: i won't merge|split anything in stray" << dendl;
11377 return false;
11378 }
11379 if (diri->is_mdsdir() || diri->ino() == CEPH_INO_CEPH) {
11380 dout(7) << "can_fragment: i won't fragment mdsdir or .ceph" << dendl;
11381 return false;
11382 }
11383
11384 for (const auto& dir : dirs) {
11385 if (dir->scrub_is_in_progress()) {
11386 dout(7) << "can_fragment: scrub in progress " << *dir << dendl;
11387 return false;
11388 }
11389
11390 if (dir->state_test(CDir::STATE_FRAGMENTING)) {
11391 dout(7) << "can_fragment: already fragmenting " << *dir << dendl;
11392 return false;
11393 }
11394 if (!dir->is_auth()) {
11395 dout(7) << "can_fragment: not auth on " << *dir << dendl;
11396 return false;
11397 }
11398 if (dir->is_bad()) {
11399 dout(7) << "can_fragment: bad dirfrag " << *dir << dendl;
11400 return false;
11401 }
11402 if (dir->is_frozen() ||
11403 dir->is_freezing()) {
11404 dout(7) << "can_fragment: can't merge, freezing|frozen. wait for other exports to finish first." << dendl;
11405 return false;
11406 }
11407 }
11408
11409 return true;
11410 }
11411
11412 void MDCache::split_dir(CDir *dir, int bits)
11413 {
11414 dout(7) << __func__ << " " << *dir << " bits " << bits << dendl;
11415 ceph_assert(dir->is_auth());
11416 CInode *diri = dir->inode;
11417
11418 std::vector<CDir*> dirs;
11419 dirs.push_back(dir);
11420
11421 if (!can_fragment(diri, dirs)) {
11422 dout(7) << __func__ << " cannot fragment right now, dropping" << dendl;
11423 return;
11424 }
11425
11426 if (dir->frag.bits() + bits > 24) {
11427 dout(7) << __func__ << " frag bits > 24, dropping" << dendl;
11428 return;
11429 }
11430
11431 MDRequestRef mdr = request_start_internal(CEPH_MDS_OP_FRAGMENTDIR);
11432 mdr->more()->fragment_base = dir->dirfrag();
11433
11434 ceph_assert(fragments.count(dir->dirfrag()) == 0);
11435 fragment_info_t& info = fragments[dir->dirfrag()];
11436 info.mdr = mdr;
11437 info.dirs.push_back(dir);
11438 info.bits = bits;
11439 info.last_cum_auth_pins_change = ceph_clock_now();
11440
11441 fragment_freeze_dirs(dirs);
11442 // initial mark+complete pass
11443 fragment_mark_and_complete(mdr);
11444 }
11445
11446 void MDCache::merge_dir(CInode *diri, frag_t frag)
11447 {
11448 dout(7) << "merge_dir to " << frag << " on " << *diri << dendl;
11449
11450 auto&& [all, dirs] = diri->get_dirfrags_under(frag);
11451 if (!all) {
11452 dout(7) << "don't have all frags under " << frag << " for " << *diri << dendl;
11453 return;
11454 }
11455
11456 if (diri->dirfragtree.is_leaf(frag)) {
11457 dout(10) << " " << frag << " already a leaf for " << *diri << dendl;
11458 return;
11459 }
11460
11461 if (!can_fragment(diri, dirs))
11462 return;
11463
11464 CDir *first = dirs.front();
11465 int bits = first->get_frag().bits() - frag.bits();
11466 dout(10) << " we are merging by " << bits << " bits" << dendl;
11467
11468 dirfrag_t basedirfrag(diri->ino(), frag);
11469 MDRequestRef mdr = request_start_internal(CEPH_MDS_OP_FRAGMENTDIR);
11470 mdr->more()->fragment_base = basedirfrag;
11471
11472 ceph_assert(fragments.count(basedirfrag) == 0);
11473 fragment_info_t& info = fragments[basedirfrag];
11474 info.mdr = mdr;
11475 info.dirs = dirs;
11476 info.bits = -bits;
11477 info.last_cum_auth_pins_change = ceph_clock_now();
11478
11479 fragment_freeze_dirs(dirs);
11480 // initial mark+complete pass
11481 fragment_mark_and_complete(mdr);
11482 }
11483
11484 void MDCache::fragment_freeze_dirs(const std::vector<CDir*>& dirs)
11485 {
11486 bool any_subtree = false, any_non_subtree = false;
11487 for (const auto& dir : dirs) {
11488 dir->auth_pin(dir); // until we mark and complete them
11489 dir->state_set(CDir::STATE_FRAGMENTING);
11490 dir->freeze_dir();
11491 ceph_assert(dir->is_freezing_dir());
11492
11493 if (dir->is_subtree_root())
11494 any_subtree = true;
11495 else
11496 any_non_subtree = true;
11497 }
11498
11499 if (any_subtree && any_non_subtree) {
11500 // either all dirfrags are subtree roots or all are not.
11501 for (const auto& dir : dirs) {
11502 if (dir->is_subtree_root()) {
11503 ceph_assert(dir->state_test(CDir::STATE_AUXSUBTREE));
11504 } else {
11505 dir->state_set(CDir::STATE_AUXSUBTREE);
11506 adjust_subtree_auth(dir, mds->get_nodeid());
11507 }
11508 }
11509 }
11510 }
11511
11512 class C_MDC_FragmentMarking : public MDCacheContext {
11513 MDRequestRef mdr;
11514 public:
11515 C_MDC_FragmentMarking(MDCache *m, MDRequestRef& r) : MDCacheContext(m), mdr(r) {}
11516 void finish(int r) override {
11517 mdcache->fragment_mark_and_complete(mdr);
11518 }
11519 };
11520
11521 void MDCache::fragment_mark_and_complete(MDRequestRef& mdr)
11522 {
11523 dirfrag_t basedirfrag = mdr->more()->fragment_base;
11524 map<dirfrag_t,fragment_info_t>::iterator it = fragments.find(basedirfrag);
11525 if (it == fragments.end() || it->second.mdr != mdr) {
11526 dout(7) << "fragment_mark_and_complete " << basedirfrag << " must have aborted" << dendl;
11527 request_finish(mdr);
11528 return;
11529 }
11530
11531 fragment_info_t& info = it->second;
11532 CInode *diri = info.dirs.front()->get_inode();
11533 dout(10) << "fragment_mark_and_complete " << info.dirs << " on " << *diri << dendl;
11534
11535 MDSGatherBuilder gather(g_ceph_context);
11536
11537 for (const auto& dir : info.dirs) {
11538 bool ready = true;
11539 if (!dir->is_complete()) {
11540 dout(15) << " fetching incomplete " << *dir << dendl;
11541 dir->fetch(gather.new_sub(), true); // ignore authpinnability
11542 ready = false;
11543 } else if (dir->get_frag() == frag_t()) {
11544 // The COMPLETE flag gets lost if we fragment a new dirfrag, then rollback
11545 // the operation. To avoid CDir::fetch() complaining about missing object,
11546 // we commit new dirfrag first.
11547 if (dir->state_test(CDir::STATE_CREATING)) {
11548 dout(15) << " waiting until new dir gets journaled " << *dir << dendl;
11549 dir->add_waiter(CDir::WAIT_CREATED, gather.new_sub());
11550 ready = false;
11551 } else if (dir->is_new()) {
11552 dout(15) << " committing new " << *dir << dendl;
11553 ceph_assert(dir->is_dirty());
11554 dir->commit(0, gather.new_sub(), true);
11555 ready = false;
11556 }
11557 }
11558 if (!ready)
11559 continue;
11560
11561 if (!dir->state_test(CDir::STATE_DNPINNEDFRAG)) {
11562 dout(15) << " marking " << *dir << dendl;
11563 for (auto &p : dir->items) {
11564 CDentry *dn = p.second;
11565 dn->get(CDentry::PIN_FRAGMENTING);
11566 ceph_assert(!dn->state_test(CDentry::STATE_FRAGMENTING));
11567 dn->state_set(CDentry::STATE_FRAGMENTING);
11568 }
11569 dir->state_set(CDir::STATE_DNPINNEDFRAG);
11570 dir->auth_unpin(dir);
11571 } else {
11572 dout(15) << " already marked " << *dir << dendl;
11573 }
11574 }
11575 if (gather.has_subs()) {
11576 gather.set_finisher(new C_MDC_FragmentMarking(this, mdr));
11577 gather.activate();
11578 return;
11579 }
11580
11581 for (const auto& dir : info.dirs) {
11582 if (!dir->is_frozen_dir()) {
11583 ceph_assert(dir->is_freezing_dir());
11584 dir->add_waiter(CDir::WAIT_FROZEN, gather.new_sub());
11585 }
11586 }
11587 if (gather.has_subs()) {
11588 gather.set_finisher(new C_MDC_FragmentFrozen(this, mdr));
11589 gather.activate();
11590 // flush log so that request auth_pins are retired
11591 mds->mdlog->flush();
11592 return;
11593 }
11594
11595 fragment_frozen(mdr, 0);
11596 }
11597
11598 void MDCache::fragment_unmark_unfreeze_dirs(const std::vector<CDir*>& dirs)
11599 {
11600 dout(10) << "fragment_unmark_unfreeze_dirs " << dirs << dendl;
11601 for (const auto& dir : dirs) {
11602 dout(10) << " frag " << *dir << dendl;
11603
11604 ceph_assert(dir->state_test(CDir::STATE_FRAGMENTING));
11605 dir->state_clear(CDir::STATE_FRAGMENTING);
11606
11607 if (dir->state_test(CDir::STATE_DNPINNEDFRAG)) {
11608 dir->state_clear(CDir::STATE_DNPINNEDFRAG);
11609
11610 for (auto &p : dir->items) {
11611 CDentry *dn = p.second;
11612 ceph_assert(dn->state_test(CDentry::STATE_FRAGMENTING));
11613 dn->state_clear(CDentry::STATE_FRAGMENTING);
11614 dn->put(CDentry::PIN_FRAGMENTING);
11615 }
11616 } else {
11617 dir->auth_unpin(dir);
11618 }
11619
11620 dir->unfreeze_dir();
11621 }
11622 }
11623
11624 bool MDCache::fragment_are_all_frozen(CDir *dir)
11625 {
11626 ceph_assert(dir->is_frozen_dir());
11627 map<dirfrag_t,fragment_info_t>::iterator p;
11628 for (p = fragments.lower_bound(dirfrag_t(dir->ino(), 0));
11629 p != fragments.end() && p->first.ino == dir->ino();
11630 ++p) {
11631 if (p->first.frag.contains(dir->get_frag()))
11632 return p->second.all_frozen;
11633 }
11634 ceph_abort();
11635 return false;
11636 }
11637
11638 void MDCache::fragment_freeze_inc_num_waiters(CDir *dir)
11639 {
11640 map<dirfrag_t,fragment_info_t>::iterator p;
11641 for (p = fragments.lower_bound(dirfrag_t(dir->ino(), 0));
11642 p != fragments.end() && p->first.ino == dir->ino();
11643 ++p) {
11644 if (p->first.frag.contains(dir->get_frag())) {
11645 p->second.num_remote_waiters++;
11646 return;
11647 }
11648 }
11649 ceph_abort();
11650 }
11651
11652 void MDCache::find_stale_fragment_freeze()
11653 {
11654 dout(10) << "find_stale_fragment_freeze" << dendl;
11655 // see comment in Migrator::find_stale_export_freeze()
11656 utime_t now = ceph_clock_now();
11657 utime_t cutoff = now;
11658 cutoff -= g_conf()->mds_freeze_tree_timeout;
11659
11660 for (map<dirfrag_t,fragment_info_t>::iterator p = fragments.begin();
11661 p != fragments.end(); ) {
11662 dirfrag_t df = p->first;
11663 fragment_info_t& info = p->second;
11664 ++p;
11665 if (info.all_frozen)
11666 continue;
11667 CDir *dir;
11668 int total_auth_pins = 0;
11669 for (const auto& d : info.dirs) {
11670 dir = d;
11671 if (!dir->state_test(CDir::STATE_DNPINNEDFRAG)) {
11672 total_auth_pins = -1;
11673 break;
11674 }
11675 if (dir->is_frozen_dir())
11676 continue;
11677 total_auth_pins += dir->get_auth_pins() + dir->get_dir_auth_pins();
11678 }
11679 if (total_auth_pins < 0)
11680 continue;
11681 if (info.last_cum_auth_pins != total_auth_pins) {
11682 info.last_cum_auth_pins = total_auth_pins;
11683 info.last_cum_auth_pins_change = now;
11684 continue;
11685 }
11686 if (info.last_cum_auth_pins_change >= cutoff)
11687 continue;
11688 dir = info.dirs.front();
11689 if (info.num_remote_waiters > 0 ||
11690 (!dir->inode->is_root() && dir->get_parent_dir()->is_freezing())) {
11691 dout(10) << " cancel fragmenting " << df << " bit " << info.bits << dendl;
11692 std::vector<CDir*> dirs;
11693 info.dirs.swap(dirs);
11694 fragments.erase(df);
11695 fragment_unmark_unfreeze_dirs(dirs);
11696 }
11697 }
11698 }
11699
11700 class C_MDC_FragmentPrep : public MDCacheLogContext {
11701 MDRequestRef mdr;
11702 public:
11703 C_MDC_FragmentPrep(MDCache *m, MDRequestRef& r) : MDCacheLogContext(m), mdr(r) {}
11704 void finish(int r) override {
11705 mdcache->_fragment_logged(mdr);
11706 }
11707 };
11708
11709 class C_MDC_FragmentStore : public MDCacheContext {
11710 MDRequestRef mdr;
11711 public:
11712 C_MDC_FragmentStore(MDCache *m, MDRequestRef& r) : MDCacheContext(m), mdr(r) {}
11713 void finish(int r) override {
11714 mdcache->_fragment_stored(mdr);
11715 }
11716 };
11717
11718 class C_MDC_FragmentCommit : public MDCacheLogContext {
11719 dirfrag_t basedirfrag;
11720 MDRequestRef mdr;
11721 public:
11722 C_MDC_FragmentCommit(MDCache *m, dirfrag_t df, const MDRequestRef& r) :
11723 MDCacheLogContext(m), basedirfrag(df), mdr(r) {}
11724 void finish(int r) override {
11725 mdcache->_fragment_committed(basedirfrag, mdr);
11726 }
11727 };
11728
11729 class C_IO_MDC_FragmentPurgeOld : public MDCacheIOContext {
11730 dirfrag_t basedirfrag;
11731 int bits;
11732 MDRequestRef mdr;
11733 public:
11734 C_IO_MDC_FragmentPurgeOld(MDCache *m, dirfrag_t f, int b,
11735 const MDRequestRef& r) :
11736 MDCacheIOContext(m), basedirfrag(f), bits(b), mdr(r) {}
11737 void finish(int r) override {
11738 ceph_assert(r == 0 || r == -CEPHFS_ENOENT);
11739 mdcache->_fragment_old_purged(basedirfrag, bits, mdr);
11740 }
11741 void print(ostream& out) const override {
11742 out << "fragment_purge_old(" << basedirfrag << ")";
11743 }
11744 };
11745
11746 void MDCache::fragment_frozen(MDRequestRef& mdr, int r)
11747 {
11748 dirfrag_t basedirfrag = mdr->more()->fragment_base;
11749 map<dirfrag_t,fragment_info_t>::iterator it = fragments.find(basedirfrag);
11750 if (it == fragments.end() || it->second.mdr != mdr) {
11751 dout(7) << "fragment_frozen " << basedirfrag << " must have aborted" << dendl;
11752 request_finish(mdr);
11753 return;
11754 }
11755
11756 ceph_assert(r == 0);
11757 fragment_info_t& info = it->second;
11758 dout(10) << "fragment_frozen " << basedirfrag.frag << " by " << info.bits
11759 << " on " << info.dirs.front()->get_inode() << dendl;
11760
11761 info.all_frozen = true;
11762 dispatch_fragment_dir(mdr);
11763 }
11764
11765 void MDCache::dispatch_fragment_dir(MDRequestRef& mdr)
11766 {
11767 dirfrag_t basedirfrag = mdr->more()->fragment_base;
11768 map<dirfrag_t,fragment_info_t>::iterator it = fragments.find(basedirfrag);
11769 if (it == fragments.end() || it->second.mdr != mdr) {
11770 dout(7) << "dispatch_fragment_dir " << basedirfrag << " must have aborted" << dendl;
11771 request_finish(mdr);
11772 return;
11773 }
11774
11775 fragment_info_t& info = it->second;
11776 CInode *diri = info.dirs.front()->get_inode();
11777
11778 dout(10) << "dispatch_fragment_dir " << basedirfrag << " bits " << info.bits
11779 << " on " << *diri << dendl;
11780
11781 if (mdr->more()->peer_error)
11782 mdr->aborted = true;
11783
11784 if (!mdr->aborted) {
11785 MutationImpl::LockOpVec lov;
11786 lov.add_wrlock(&diri->dirfragtreelock);
11787 // prevent a racing gather on any other scatterlocks too
11788 lov.lock_scatter_gather(&diri->nestlock);
11789 lov.lock_scatter_gather(&diri->filelock);
11790 if (!mds->locker->acquire_locks(mdr, lov, NULL, true)) {
11791 if (!mdr->aborted)
11792 return;
11793 }
11794 }
11795
11796 if (mdr->aborted) {
11797 dout(10) << " can't auth_pin " << *diri << ", requeuing dir "
11798 << info.dirs.front()->dirfrag() << dendl;
11799 if (info.bits > 0)
11800 mds->balancer->queue_split(info.dirs.front(), false);
11801 else
11802 mds->balancer->queue_merge(info.dirs.front());
11803 fragment_unmark_unfreeze_dirs(info.dirs);
11804 fragments.erase(it);
11805 request_finish(mdr);
11806 return;
11807 }
11808
11809 mdr->ls = mds->mdlog->get_current_segment();
11810 EFragment *le = new EFragment(mds->mdlog, EFragment::OP_PREPARE, basedirfrag, info.bits);
11811 mds->mdlog->start_entry(le);
11812
11813 for (const auto& dir : info.dirs) {
11814 dirfrag_rollback rollback;
11815 rollback.fnode = dir->fnode;
11816 le->add_orig_frag(dir->get_frag(), &rollback);
11817 }
11818
11819 // refragment
11820 MDSContext::vec waiters;
11821 adjust_dir_fragments(diri, info.dirs, basedirfrag.frag, info.bits,
11822 &info.resultfrags, waiters, false);
11823 if (g_conf()->mds_debug_frag)
11824 diri->verify_dirfrags();
11825 mds->queue_waiters(waiters);
11826
11827 for (const auto& fg : le->orig_frags)
11828 ceph_assert(!diri->dirfragtree.is_leaf(fg));
11829
11830 le->metablob.add_dir_context(info.resultfrags.front());
11831 for (const auto& dir : info.resultfrags) {
11832 if (diri->is_auth()) {
11833 le->metablob.add_fragmented_dir(dir, false, false);
11834 } else {
11835 dir->state_set(CDir::STATE_DIRTYDFT);
11836 le->metablob.add_fragmented_dir(dir, false, true);
11837 }
11838 }
11839
11840 // dft lock
11841 if (diri->is_auth()) {
11842 // journal dirfragtree
11843 auto pi = diri->project_inode(mdr);
11844 pi.inode->version = diri->pre_dirty();
11845 predirty_journal_parents(mdr, &le->metablob, diri, 0, PREDIRTY_PRIMARY);
11846 journal_dirty_inode(mdr.get(), &le->metablob, diri);
11847 } else {
11848 mds->locker->mark_updated_scatterlock(&diri->dirfragtreelock);
11849 mdr->ls->dirty_dirfrag_dirfragtree.push_back(&diri->item_dirty_dirfrag_dirfragtree);
11850 mdr->add_updated_lock(&diri->dirfragtreelock);
11851 }
11852
11853 /*
11854 // filelock
11855 mds->locker->mark_updated_scatterlock(&diri->filelock);
11856 mut->ls->dirty_dirfrag_dir.push_back(&diri->item_dirty_dirfrag_dir);
11857 mut->add_updated_lock(&diri->filelock);
11858
11859 // dirlock
11860 mds->locker->mark_updated_scatterlock(&diri->nestlock);
11861 mut->ls->dirty_dirfrag_nest.push_back(&diri->item_dirty_dirfrag_nest);
11862 mut->add_updated_lock(&diri->nestlock);
11863 */
11864
11865 add_uncommitted_fragment(basedirfrag, info.bits, le->orig_frags, mdr->ls);
11866 mds->server->submit_mdlog_entry(le, new C_MDC_FragmentPrep(this, mdr),
11867 mdr, __func__);
11868 mds->mdlog->flush();
11869 }
11870
11871 void MDCache::_fragment_logged(MDRequestRef& mdr)
11872 {
11873 dirfrag_t basedirfrag = mdr->more()->fragment_base;
11874 auto& info = fragments.at(basedirfrag);
11875 CInode *diri = info.resultfrags.front()->get_inode();
11876
11877 dout(10) << "fragment_logged " << basedirfrag << " bits " << info.bits
11878 << " on " << *diri << dendl;
11879 mdr->mark_event("prepare logged");
11880
11881 mdr->apply(); // mark scatterlock
11882
11883 // store resulting frags
11884 MDSGatherBuilder gather(g_ceph_context, new C_MDC_FragmentStore(this, mdr));
11885
11886 for (const auto& dir : info.resultfrags) {
11887 dout(10) << " storing result frag " << *dir << dendl;
11888
11889 dir->mark_dirty(mdr->ls);
11890 dir->mark_new(mdr->ls);
11891
11892 // freeze and store them too
11893 dir->auth_pin(this);
11894 dir->state_set(CDir::STATE_FRAGMENTING);
11895 dir->commit(0, gather.new_sub(), true); // ignore authpinnability
11896 }
11897
11898 gather.activate();
11899 }
11900
11901 void MDCache::_fragment_stored(MDRequestRef& mdr)
11902 {
11903 dirfrag_t basedirfrag = mdr->more()->fragment_base;
11904 fragment_info_t &info = fragments.at(basedirfrag);
11905 CDir *first = info.resultfrags.front();
11906 CInode *diri = first->get_inode();
11907
11908 dout(10) << "fragment_stored " << basedirfrag << " bits " << info.bits
11909 << " on " << *diri << dendl;
11910 mdr->mark_event("new frags stored");
11911
11912 // tell peers
11913 mds_rank_t diri_auth = (first->is_subtree_root() && !diri->is_auth()) ?
11914 diri->authority().first : CDIR_AUTH_UNKNOWN;
11915 for (const auto &p : first->get_replicas()) {
11916 if (mds->mdsmap->get_state(p.first) < MDSMap::STATE_REJOIN ||
11917 (mds->mdsmap->get_state(p.first) == MDSMap::STATE_REJOIN &&
11918 rejoin_gather.count(p.first)))
11919 continue;
11920
11921 auto notify = make_message<MMDSFragmentNotify>(basedirfrag, info.bits, mdr->reqid.tid);
11922 if (diri_auth != CDIR_AUTH_UNKNOWN && // subtree root
11923 diri_auth != p.first) { // not auth mds of diri
11924 /*
11925 * In the nornal case, mds does not trim dir inode whose child dirfrags
11926 * are likely being fragmented (see trim_inode()). But when fragmenting
11927 * subtree roots, following race can happen:
11928 *
11929 * - mds.a (auth mds of dirfrag) sends fragment_notify message to
11930 * mds.c and drops wrlock on dirfragtreelock.
11931 * - mds.b (auth mds of dir inode) changes dirfragtreelock state to
11932 * SYNC and send lock message mds.c
11933 * - mds.c receives the lock message and changes dirfragtreelock state
11934 * to SYNC
11935 * - mds.c trim dirfrag and dir inode from its cache
11936 * - mds.c receives the fragment_notify message
11937 *
11938 * So we need to ensure replicas have received the notify, then unlock
11939 * the dirfragtreelock.
11940 */
11941 notify->mark_ack_wanted();
11942 info.notify_ack_waiting.insert(p.first);
11943 }
11944
11945 // freshly replicate new dirs to peers
11946 for (const auto& dir : info.resultfrags) {
11947 encode_replica_dir(dir, p.first, notify->basebl);
11948 }
11949
11950 mds->send_message_mds(notify, p.first);
11951 }
11952
11953 // journal commit
11954 EFragment *le = new EFragment(mds->mdlog, EFragment::OP_COMMIT, basedirfrag, info.bits);
11955 mds->mdlog->start_submit_entry(le, new C_MDC_FragmentCommit(this, basedirfrag, mdr));
11956
11957
11958 // unfreeze resulting frags
11959 for (const auto& dir : info.resultfrags) {
11960 dout(10) << " result frag " << *dir << dendl;
11961
11962 for (auto &p : dir->items) {
11963 CDentry *dn = p.second;
11964 ceph_assert(dn->state_test(CDentry::STATE_FRAGMENTING));
11965 dn->state_clear(CDentry::STATE_FRAGMENTING);
11966 dn->put(CDentry::PIN_FRAGMENTING);
11967 }
11968
11969 // unfreeze
11970 dir->unfreeze_dir();
11971 }
11972
11973 if (info.notify_ack_waiting.empty()) {
11974 fragment_drop_locks(info);
11975 } else {
11976 mds->locker->drop_locks_for_fragment_unfreeze(mdr.get());
11977 }
11978 }
11979
11980 void MDCache::_fragment_committed(dirfrag_t basedirfrag, const MDRequestRef& mdr)
11981 {
11982 dout(10) << "fragment_committed " << basedirfrag << dendl;
11983 if (mdr)
11984 mdr->mark_event("commit logged");
11985
11986 ufragment &uf = uncommitted_fragments.at(basedirfrag);
11987
11988 // remove old frags
11989 C_GatherBuilder gather(
11990 g_ceph_context,
11991 new C_OnFinisher(
11992 new C_IO_MDC_FragmentPurgeOld(this, basedirfrag, uf.bits, mdr),
11993 mds->finisher));
11994
11995 SnapContext nullsnapc;
11996 object_locator_t oloc(mds->get_metadata_pool());
11997 for (const auto& fg : uf.old_frags) {
11998 object_t oid = CInode::get_object_name(basedirfrag.ino, fg, "");
11999 ObjectOperation op;
12000 if (fg == frag_t()) {
12001 // backtrace object
12002 dout(10) << " truncate orphan dirfrag " << oid << dendl;
12003 op.truncate(0);
12004 op.omap_clear();
12005 } else {
12006 dout(10) << " removing orphan dirfrag " << oid << dendl;
12007 op.remove();
12008 }
12009 mds->objecter->mutate(oid, oloc, op, nullsnapc,
12010 ceph::real_clock::now(),
12011 0, gather.new_sub());
12012 }
12013
12014 ceph_assert(gather.has_subs());
12015 gather.activate();
12016 }
12017
12018 void MDCache::_fragment_old_purged(dirfrag_t basedirfrag, int bits, const MDRequestRef& mdr)
12019 {
12020 dout(10) << "fragment_old_purged " << basedirfrag << dendl;
12021 if (mdr)
12022 mdr->mark_event("old frags purged");
12023
12024 EFragment *le = new EFragment(mds->mdlog, EFragment::OP_FINISH, basedirfrag, bits);
12025 mds->mdlog->start_submit_entry(le);
12026
12027 finish_uncommitted_fragment(basedirfrag, EFragment::OP_FINISH);
12028
12029 if (mds->logger) {
12030 if (bits > 0) {
12031 mds->logger->inc(l_mds_dir_split);
12032 } else {
12033 mds->logger->inc(l_mds_dir_merge);
12034 }
12035 }
12036
12037 if (mdr) {
12038 auto it = fragments.find(basedirfrag);
12039 ceph_assert(it != fragments.end());
12040 it->second.finishing = true;
12041 if (it->second.notify_ack_waiting.empty())
12042 fragment_maybe_finish(it);
12043 else
12044 mdr->mark_event("wating for notify acks");
12045 }
12046 }
12047
12048 void MDCache::fragment_drop_locks(fragment_info_t& info)
12049 {
12050 mds->locker->drop_locks(info.mdr.get());
12051 request_finish(info.mdr);
12052 //info.mdr.reset();
12053 }
12054
12055 void MDCache::fragment_maybe_finish(const fragment_info_iterator& it)
12056 {
12057 if (!it->second.finishing)
12058 return;
12059
12060 // unmark & auth_unpin
12061 for (const auto &dir : it->second.resultfrags) {
12062 dir->state_clear(CDir::STATE_FRAGMENTING);
12063 dir->auth_unpin(this);
12064
12065 // In case the resulting fragments are beyond the split size,
12066 // we might need to split them again right away (they could
12067 // have been taking inserts between unfreezing and getting
12068 // here)
12069 mds->balancer->maybe_fragment(dir, false);
12070 }
12071
12072 fragments.erase(it);
12073 }
12074
12075
12076 void MDCache::handle_fragment_notify_ack(const cref_t<MMDSFragmentNotifyAck> &ack)
12077 {
12078 dout(10) << "handle_fragment_notify_ack " << *ack << " from " << ack->get_source() << dendl;
12079 mds_rank_t from = mds_rank_t(ack->get_source().num());
12080
12081 if (mds->get_state() < MDSMap::STATE_ACTIVE) {
12082 return;
12083 }
12084
12085 auto it = fragments.find(ack->get_base_dirfrag());
12086 if (it == fragments.end() ||
12087 it->second.get_tid() != ack->get_tid()) {
12088 dout(10) << "handle_fragment_notify_ack obsolete message, dropping" << dendl;
12089 return;
12090 }
12091
12092 if (it->second.notify_ack_waiting.erase(from) &&
12093 it->second.notify_ack_waiting.empty()) {
12094 fragment_drop_locks(it->second);
12095 fragment_maybe_finish(it);
12096 }
12097 }
12098
12099 void MDCache::handle_fragment_notify(const cref_t<MMDSFragmentNotify> &notify)
12100 {
12101 dout(10) << "handle_fragment_notify " << *notify << " from " << notify->get_source() << dendl;
12102 mds_rank_t from = mds_rank_t(notify->get_source().num());
12103
12104 if (mds->get_state() < MDSMap::STATE_REJOIN) {
12105 return;
12106 }
12107
12108 CInode *diri = get_inode(notify->get_ino());
12109 if (diri) {
12110 frag_t base = notify->get_basefrag();
12111 int bits = notify->get_bits();
12112
12113 /*
12114 if ((bits < 0 && diri->dirfragtree.is_leaf(base)) ||
12115 (bits > 0 && !diri->dirfragtree.is_leaf(base))) {
12116 dout(10) << " dft " << diri->dirfragtree << " state doesn't match " << base << " by " << bits
12117 << ", must have found out during resolve/rejoin? ignoring. " << *diri << dendl;
12118 return;
12119 }
12120 */
12121
12122 // refragment
12123 MDSContext::vec waiters;
12124 std::vector<CDir*> resultfrags;
12125 adjust_dir_fragments(diri, base, bits, &resultfrags, waiters, false);
12126 if (g_conf()->mds_debug_frag)
12127 diri->verify_dirfrags();
12128
12129 for (const auto& dir : resultfrags) {
12130 diri->take_dir_waiting(dir->get_frag(), waiters);
12131 }
12132
12133 // add new replica dirs values
12134 auto p = notify->basebl.cbegin();
12135 while (!p.end()) {
12136 CDir *tmp_dir = nullptr;
12137 decode_replica_dir(tmp_dir, p, diri, from, waiters);
12138 }
12139
12140 mds->queue_waiters(waiters);
12141 } else {
12142 ceph_abort();
12143 }
12144
12145 if (notify->is_ack_wanted()) {
12146 auto ack = make_message<MMDSFragmentNotifyAck>(notify->get_base_dirfrag(),
12147 notify->get_bits(), notify->get_tid());
12148 mds->send_message_mds(ack, from);
12149 }
12150 }
12151
12152 void MDCache::add_uncommitted_fragment(dirfrag_t basedirfrag, int bits, const frag_vec_t& old_frags,
12153 LogSegment *ls, bufferlist *rollback)
12154 {
12155 dout(10) << "add_uncommitted_fragment: base dirfrag " << basedirfrag << " bits " << bits << dendl;
12156 ceph_assert(!uncommitted_fragments.count(basedirfrag));
12157 ufragment& uf = uncommitted_fragments[basedirfrag];
12158 uf.old_frags = old_frags;
12159 uf.bits = bits;
12160 uf.ls = ls;
12161 ls->uncommitted_fragments.insert(basedirfrag);
12162 if (rollback)
12163 uf.rollback.swap(*rollback);
12164 }
12165
12166 void MDCache::finish_uncommitted_fragment(dirfrag_t basedirfrag, int op)
12167 {
12168 dout(10) << "finish_uncommitted_fragments: base dirfrag " << basedirfrag
12169 << " op " << EFragment::op_name(op) << dendl;
12170 map<dirfrag_t, ufragment>::iterator it = uncommitted_fragments.find(basedirfrag);
12171 if (it != uncommitted_fragments.end()) {
12172 ufragment& uf = it->second;
12173 if (op != EFragment::OP_FINISH && !uf.old_frags.empty()) {
12174 uf.committed = true;
12175 } else {
12176 uf.ls->uncommitted_fragments.erase(basedirfrag);
12177 mds->queue_waiters(uf.waiters);
12178 uncommitted_fragments.erase(it);
12179 }
12180 }
12181 }
12182
12183 void MDCache::rollback_uncommitted_fragment(dirfrag_t basedirfrag, frag_vec_t&& old_frags)
12184 {
12185 dout(10) << "rollback_uncommitted_fragment: base dirfrag " << basedirfrag
12186 << " old_frags (" << old_frags << ")" << dendl;
12187 map<dirfrag_t, ufragment>::iterator it = uncommitted_fragments.find(basedirfrag);
12188 if (it != uncommitted_fragments.end()) {
12189 ufragment& uf = it->second;
12190 if (!uf.old_frags.empty()) {
12191 uf.old_frags = std::move(old_frags);
12192 uf.committed = true;
12193 } else {
12194 uf.ls->uncommitted_fragments.erase(basedirfrag);
12195 uncommitted_fragments.erase(it);
12196 }
12197 }
12198 }
12199
12200 void MDCache::wait_for_uncommitted_fragments(MDSContext* finisher)
12201 {
12202 MDSGatherBuilder gather(g_ceph_context, finisher);
12203 for (auto& p : uncommitted_fragments) {
12204 p.second.waiters.push_back(gather.new_sub());
12205 }
12206 gather.activate();
12207 }
12208
12209 struct C_MDC_FragmentRollback : public MDCacheLogContext {
12210 MutationRef mut;
12211 C_MDC_FragmentRollback(MDCache *c, MutationRef& m) :
12212 MDCacheLogContext(c), mut(m) {}
12213 void finish(int r) override {
12214 mut->apply();
12215 get_mds()->locker->drop_locks(mut.get());
12216 mut->cleanup();
12217 }
12218 };
12219
12220 void MDCache::rollback_uncommitted_fragments()
12221 {
12222 dout(10) << "rollback_uncommitted_fragments: " << uncommitted_fragments.size() << " pending" << dendl;
12223 for (map<dirfrag_t, ufragment>::iterator p = uncommitted_fragments.begin();
12224 p != uncommitted_fragments.end();
12225 ++p) {
12226 ufragment &uf = p->second;
12227 CInode *diri = get_inode(p->first.ino);
12228 ceph_assert(diri);
12229
12230 if (uf.committed) {
12231 _fragment_committed(p->first, MDRequestRef());
12232 continue;
12233 }
12234
12235 dout(10) << " rolling back " << p->first << " refragment by " << uf.bits << " bits" << dendl;
12236
12237 MutationRef mut(new MutationImpl());
12238 mut->ls = mds->mdlog->get_current_segment();
12239 EFragment *le = new EFragment(mds->mdlog, EFragment::OP_ROLLBACK, p->first, uf.bits);
12240 mds->mdlog->start_entry(le);
12241 bool diri_auth = (diri->authority() != CDIR_AUTH_UNDEF);
12242
12243 frag_vec_t old_frags;
12244 diri->dirfragtree.get_leaves_under(p->first.frag, old_frags);
12245
12246 std::vector<CDir*> resultfrags;
12247 if (uf.old_frags.empty()) {
12248 // created by old format EFragment
12249 MDSContext::vec waiters;
12250 adjust_dir_fragments(diri, p->first.frag, -uf.bits, &resultfrags, waiters, true);
12251 } else {
12252 auto bp = uf.rollback.cbegin();
12253 for (const auto& fg : uf.old_frags) {
12254 CDir *dir = force_dir_fragment(diri, fg);
12255 resultfrags.push_back(dir);
12256
12257 dirfrag_rollback rollback;
12258 decode(rollback, bp);
12259
12260 dir->fnode = rollback.fnode;
12261
12262 dir->mark_dirty(mut->ls);
12263
12264 if (!(dir->get_fnode()->rstat == dir->get_fnode()->accounted_rstat)) {
12265 dout(10) << " dirty nestinfo on " << *dir << dendl;
12266 mds->locker->mark_updated_scatterlock(&diri->nestlock);
12267 mut->ls->dirty_dirfrag_nest.push_back(&diri->item_dirty_dirfrag_nest);
12268 mut->add_updated_lock(&diri->nestlock);
12269 }
12270 if (!(dir->get_fnode()->fragstat == dir->get_fnode()->accounted_fragstat)) {
12271 dout(10) << " dirty fragstat on " << *dir << dendl;
12272 mds->locker->mark_updated_scatterlock(&diri->filelock);
12273 mut->ls->dirty_dirfrag_dir.push_back(&diri->item_dirty_dirfrag_dir);
12274 mut->add_updated_lock(&diri->filelock);
12275 }
12276
12277 le->add_orig_frag(dir->get_frag());
12278 le->metablob.add_dir_context(dir);
12279 if (diri_auth) {
12280 le->metablob.add_fragmented_dir(dir, true, false);
12281 } else {
12282 dout(10) << " dirty dirfragtree on " << *dir << dendl;
12283 dir->state_set(CDir::STATE_DIRTYDFT);
12284 le->metablob.add_fragmented_dir(dir, true, true);
12285 }
12286 }
12287 }
12288
12289 if (diri_auth) {
12290 auto pi = diri->project_inode(mut);
12291 pi.inode->version = diri->pre_dirty();
12292 predirty_journal_parents(mut, &le->metablob, diri, 0, PREDIRTY_PRIMARY);
12293 le->metablob.add_primary_dentry(diri->get_projected_parent_dn(), diri, true);
12294 } else {
12295 mds->locker->mark_updated_scatterlock(&diri->dirfragtreelock);
12296 mut->ls->dirty_dirfrag_dirfragtree.push_back(&diri->item_dirty_dirfrag_dirfragtree);
12297 mut->add_updated_lock(&diri->dirfragtreelock);
12298 }
12299
12300 if (g_conf()->mds_debug_frag)
12301 diri->verify_dirfrags();
12302
12303 for (const auto& leaf : old_frags) {
12304 ceph_assert(!diri->dirfragtree.is_leaf(leaf));
12305 }
12306
12307 mds->mdlog->submit_entry(le, new C_MDC_FragmentRollback(this, mut));
12308
12309 uf.old_frags.swap(old_frags);
12310 _fragment_committed(p->first, MDRequestRef());
12311 }
12312 }
12313
12314 void MDCache::force_readonly()
12315 {
12316 if (is_readonly())
12317 return;
12318
12319 dout(1) << "force file system read-only" << dendl;
12320 mds->clog->warn() << "force file system read-only";
12321
12322 set_readonly();
12323
12324 mds->server->force_clients_readonly();
12325
12326 // revoke write caps
12327 int count = 0;
12328 for (auto &p : inode_map) {
12329 CInode *in = p.second;
12330 if (in->is_head())
12331 mds->locker->eval(in, CEPH_CAP_LOCKS);
12332 if (!(++count % mds->heartbeat_reset_grace()))
12333 mds->heartbeat_reset();
12334 }
12335
12336 mds->mdlog->flush();
12337 }
12338
12339
12340 // ==============================================================
12341 // debug crap
12342
12343 void MDCache::show_subtrees(int dbl, bool force_print)
12344 {
12345 if (g_conf()->mds_thrash_exports)
12346 dbl += 15;
12347
12348 //dout(10) << "show_subtrees" << dendl;
12349
12350 if (!g_conf()->subsys.should_gather(ceph_subsys_mds, dbl))
12351 return; // i won't print anything.
12352
12353 if (subtrees.empty()) {
12354 dout(ceph::dout::need_dynamic(dbl)) << "show_subtrees - no subtrees"
12355 << dendl;
12356 return;
12357 }
12358
12359 if (!force_print && subtrees.size() > SUBTREES_COUNT_THRESHOLD &&
12360 !g_conf()->subsys.should_gather<ceph_subsys_mds, 25>()) {
12361 dout(ceph::dout::need_dynamic(dbl)) << "number of subtrees = " << subtrees.size() << "; not "
12362 "printing subtrees" << dendl;
12363 return;
12364 }
12365
12366 // root frags
12367 std::vector<CDir*> basefrags;
12368 for (set<CInode*>::iterator p = base_inodes.begin();
12369 p != base_inodes.end();
12370 ++p)
12371 (*p)->get_dirfrags(basefrags);
12372 //dout(15) << "show_subtrees, base dirfrags " << basefrags << dendl;
12373 dout(15) << "show_subtrees" << dendl;
12374
12375 // queue stuff
12376 list<pair<CDir*,int> > q;
12377 string indent;
12378 set<CDir*> seen;
12379
12380 // calc max depth
12381 for (const auto& dir : basefrags) {
12382 q.emplace_back(dir, 0);
12383 }
12384
12385 set<CDir*> subtrees_seen;
12386
12387 unsigned int depth = 0;
12388 while (!q.empty()) {
12389 CDir *dir = q.front().first;
12390 unsigned int d = q.front().second;
12391 q.pop_front();
12392
12393 if (subtrees.count(dir) == 0) continue;
12394
12395 subtrees_seen.insert(dir);
12396
12397 if (d > depth) depth = d;
12398
12399 // sanity check
12400 //dout(25) << "saw depth " << d << " " << *dir << dendl;
12401 if (seen.count(dir)) dout(0) << "aah, already seen " << *dir << dendl;
12402 ceph_assert(seen.count(dir) == 0);
12403 seen.insert(dir);
12404
12405 // nested items?
12406 if (!subtrees[dir].empty()) {
12407 for (set<CDir*>::iterator p = subtrees[dir].begin();
12408 p != subtrees[dir].end();
12409 ++p) {
12410 //dout(25) << " saw sub " << **p << dendl;
12411 q.push_front(pair<CDir*,int>(*p, d+1));
12412 }
12413 }
12414 }
12415
12416 if (!force_print && depth > SUBTREES_DEPTH_THRESHOLD &&
12417 !g_conf()->subsys.should_gather<ceph_subsys_mds, 25>()) {
12418 dout(ceph::dout::need_dynamic(dbl)) << "max depth among subtrees = " << depth << "; not printing "
12419 "subtrees" << dendl;
12420 return;
12421 }
12422
12423 // print tree
12424 for (const auto& dir : basefrags) {
12425 q.emplace_back(dir, 0);
12426 }
12427
12428 while (!q.empty()) {
12429 CDir *dir = q.front().first;
12430 int d = q.front().second;
12431 q.pop_front();
12432
12433 if (subtrees.count(dir) == 0) continue;
12434
12435 // adjust indenter
12436 while ((unsigned)d < indent.size())
12437 indent.resize(d);
12438
12439 // pad
12440 string pad = "______________________________________";
12441 pad.resize(depth*2+1-indent.size());
12442 if (!subtrees[dir].empty())
12443 pad[0] = '.'; // parent
12444
12445
12446 string auth;
12447 if (dir->is_auth())
12448 auth = "auth ";
12449 else
12450 auth = " rep ";
12451
12452 char s[10];
12453 if (dir->get_dir_auth().second == CDIR_AUTH_UNKNOWN)
12454 snprintf(s, sizeof(s), "%2d ", int(dir->get_dir_auth().first));
12455 else
12456 snprintf(s, sizeof(s), "%2d,%2d", int(dir->get_dir_auth().first), int(dir->get_dir_auth().second));
12457
12458 // print
12459 dout(ceph::dout::need_dynamic(dbl)) << indent << "|_" << pad << s
12460 << " " << auth << *dir << dendl;
12461
12462 if (dir->ino() == CEPH_INO_ROOT)
12463 ceph_assert(dir->inode == root);
12464 if (dir->ino() == MDS_INO_MDSDIR(mds->get_nodeid()))
12465 ceph_assert(dir->inode == myin);
12466 if (dir->inode->is_stray() && (MDS_INO_STRAY_OWNER(dir->ino()) == mds->get_nodeid()))
12467 ceph_assert(strays[MDS_INO_STRAY_INDEX(dir->ino())] == dir->inode);
12468
12469 // nested items?
12470 if (!subtrees[dir].empty()) {
12471 // more at my level?
12472 if (!q.empty() && q.front().second == d)
12473 indent += "| ";
12474 else
12475 indent += " ";
12476
12477 for (set<CDir*>::iterator p = subtrees[dir].begin();
12478 p != subtrees[dir].end();
12479 ++p)
12480 q.push_front(pair<CDir*,int>(*p, d+2));
12481 }
12482 }
12483
12484 // verify there isn't stray crap in subtree map
12485 int lost = 0;
12486 for (map<CDir*, set<CDir*> >::iterator p = subtrees.begin();
12487 p != subtrees.end();
12488 ++p) {
12489 if (subtrees_seen.count(p->first)) continue;
12490 dout(10) << "*** stray/lost entry in subtree map: " << *p->first << dendl;
12491 lost++;
12492 }
12493 ceph_assert(lost == 0);
12494 }
12495
12496 void MDCache::show_cache()
12497 {
12498 if (!g_conf()->subsys.should_gather<ceph_subsys_mds, 7>())
12499 return;
12500 dout(7) << "show_cache" << dendl;
12501
12502 auto show_func = [this](CInode *in) {
12503 // unlinked?
12504 if (!in->parent)
12505 dout(7) << " unlinked " << *in << dendl;
12506
12507 // dirfrags?
12508 auto&& dfs = in->get_dirfrags();
12509 for (const auto& dir : dfs) {
12510 dout(7) << " dirfrag " << *dir << dendl;
12511
12512 for (auto &p : dir->items) {
12513 CDentry *dn = p.second;
12514 dout(7) << " dentry " << *dn << dendl;
12515 CDentry::linkage_t *dnl = dn->get_linkage();
12516 if (dnl->is_primary() && dnl->get_inode())
12517 dout(7) << " inode " << *dnl->get_inode() << dendl;
12518 }
12519 }
12520 };
12521
12522 for (auto &p : inode_map)
12523 show_func(p.second);
12524 for (auto &p : snap_inode_map)
12525 show_func(p.second);
12526 }
12527
12528 void MDCache::cache_status(Formatter *f)
12529 {
12530 f->open_object_section("cache");
12531
12532 f->open_object_section("pool");
12533 mempool::get_pool(mempool::mds_co::id).dump(f);
12534 f->close_section();
12535
12536 f->close_section();
12537 }
12538
12539 void MDCache::dump_tree(CInode *in, const int cur_depth, const int max_depth, Formatter *f)
12540 {
12541 ceph_assert(in);
12542 if ((max_depth >= 0) && (cur_depth > max_depth)) {
12543 return;
12544 }
12545 auto&& ls = in->get_dirfrags();
12546 for (const auto &subdir : ls) {
12547 for (const auto &p : subdir->items) {
12548 CDentry *dn = p.second;
12549 CInode *in = dn->get_linkage()->get_inode();
12550 if (in) {
12551 dump_tree(in, cur_depth + 1, max_depth, f);
12552 }
12553 }
12554 }
12555 f->open_object_section("inode");
12556 in->dump(f, CInode::DUMP_DEFAULT | CInode::DUMP_DIRFRAGS);
12557 f->close_section();
12558 }
12559
12560 int MDCache::dump_cache(std::string_view file_name, double timeout)
12561 {
12562 return dump_cache(file_name, NULL, timeout);
12563 }
12564
12565 int MDCache::dump_cache(Formatter *f, double timeout)
12566 {
12567 return dump_cache(std::string_view(""), f, timeout);
12568 }
12569
12570 /**
12571 * Dump the metadata cache, either to a Formatter, if
12572 * provided, else to a plain text file.
12573 */
12574 int MDCache::dump_cache(std::string_view fn, Formatter *f, double timeout)
12575 {
12576 int r = 0;
12577
12578 // dumping large caches may cause mds to hang or worse get killed.
12579 // so, disallow the dump if the cache size exceeds the configured
12580 // threshold, which is 1G for formatter and unlimited for file (note
12581 // that this can be jacked up by the admin... and is nothing but foot
12582 // shooting, but the option itself is for devs and hence dangerous to
12583 // tune). TODO: remove this when fixed.
12584 uint64_t threshold = f ?
12585 g_conf().get_val<Option::size_t>("mds_dump_cache_threshold_formatter") :
12586 g_conf().get_val<Option::size_t>("mds_dump_cache_threshold_file");
12587
12588 if (threshold && cache_size() > threshold) {
12589 if (f) {
12590 CachedStackStringStream css;
12591 *css << "cache usage exceeds dump threshold";
12592 f->open_object_section("result");
12593 f->dump_string("error", css->strv());
12594 f->close_section();
12595 } else {
12596 derr << "cache usage exceeds dump threshold" << dendl;
12597 r = -CEPHFS_EINVAL;
12598 }
12599 return r;
12600 }
12601
12602 r = 0;
12603 int fd = -1;
12604
12605 if (f) {
12606 f->open_array_section("inodes");
12607 } else {
12608 char path[PATH_MAX] = "";
12609 if (fn.length()) {
12610 snprintf(path, sizeof path, "%s", fn.data());
12611 } else {
12612 snprintf(path, sizeof path, "cachedump.%d.mds%d", (int)mds->mdsmap->get_epoch(), int(mds->get_nodeid()));
12613 }
12614
12615 dout(1) << "dump_cache to " << path << dendl;
12616
12617 fd = ::open(path, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0600);
12618 if (fd < 0) {
12619 derr << "failed to open " << path << ": " << cpp_strerror(errno) << dendl;
12620 return errno;
12621 }
12622 }
12623
12624 auto dump_func = [fd, f](CInode *in) {
12625 int r;
12626 if (f) {
12627 f->open_object_section("inode");
12628 in->dump(f, CInode::DUMP_DEFAULT | CInode::DUMP_DIRFRAGS);
12629 f->close_section();
12630 return 1;
12631 }
12632 CachedStackStringStream css;
12633 *css << *in << std::endl;
12634 auto sv = css->strv();
12635 r = safe_write(fd, sv.data(), sv.size());
12636 if (r < 0)
12637 return r;
12638 auto&& dfs = in->get_dirfrags();
12639 for (auto &dir : dfs) {
12640 CachedStackStringStream css2;
12641 *css2 << " " << *dir << std::endl;
12642 auto sv = css2->strv();
12643 r = safe_write(fd, sv.data(), sv.size());
12644 if (r < 0)
12645 return r;
12646 for (auto &p : dir->items) {
12647 CDentry *dn = p.second;
12648 CachedStackStringStream css3;
12649 *css3 << " " << *dn << std::endl;
12650 auto sv = css3->strv();
12651 r = safe_write(fd, sv.data(), sv.size());
12652 if (r < 0)
12653 return r;
12654 }
12655 dir->check_rstats();
12656 }
12657 return 1;
12658 };
12659
12660 auto start = mono_clock::now();
12661 int64_t count = 0;
12662 for (auto &p : inode_map) {
12663 r = dump_func(p.second);
12664 if (r < 0)
12665 goto out;
12666 if (!(++count % 1000) &&
12667 timeout > 0 &&
12668 std::chrono::duration<double>(mono_clock::now() - start).count() > timeout) {
12669 r = -ETIMEDOUT;
12670 goto out;
12671 }
12672 }
12673 for (auto &p : snap_inode_map) {
12674 r = dump_func(p.second);
12675 if (r < 0)
12676 goto out;
12677 if (!(++count % 1000) &&
12678 timeout > 0 &&
12679 std::chrono::duration<double>(mono_clock::now() - start).count() > timeout) {
12680 r = -ETIMEDOUT;
12681 goto out;
12682 }
12683
12684 }
12685 r = 0;
12686
12687 out:
12688 if (f) {
12689 if (r == -ETIMEDOUT)
12690 {
12691 f->close_section();
12692 f->open_object_section("result");
12693 f->dump_string("error", "the operation timeout");
12694 }
12695 f->close_section(); // inodes
12696 } else {
12697 if (r == -ETIMEDOUT)
12698 {
12699 CachedStackStringStream css;
12700 *css << "error : the operation timeout" << std::endl;
12701 auto sv = css->strv();
12702 r = safe_write(fd, sv.data(), sv.size());
12703 }
12704 ::close(fd);
12705 }
12706 return r;
12707 }
12708
12709 void C_MDS_RetryRequest::finish(int r)
12710 {
12711 mdr->retry++;
12712 cache->dispatch_request(mdr);
12713 }
12714
12715 MDSContext *CF_MDS_RetryRequestFactory::build()
12716 {
12717 if (drop_locks) {
12718 mdcache->mds->locker->drop_locks(mdr.get(), nullptr);
12719 mdr->drop_local_auth_pins();
12720 }
12721 return new C_MDS_RetryRequest(mdcache, mdr);
12722 }
12723
12724 class C_MDS_EnqueueScrub : public Context
12725 {
12726 std::string tag;
12727 Formatter *formatter;
12728 Context *on_finish;
12729 public:
12730 ScrubHeaderRef header;
12731 C_MDS_EnqueueScrub(std::string_view tag, Formatter *f, Context *fin) :
12732 tag(tag), formatter(f), on_finish(fin), header(nullptr) {}
12733
12734 void finish(int r) override {
12735 formatter->open_object_section("results");
12736 formatter->dump_int("return_code", r);
12737 if (r == 0) {
12738 formatter->dump_string("scrub_tag", tag);
12739 formatter->dump_string("mode", "asynchronous");
12740 }
12741 formatter->close_section();
12742
12743 r = 0;
12744 if (on_finish)
12745 on_finish->complete(r);
12746 }
12747 };
12748
12749 void MDCache::enqueue_scrub(
12750 std::string_view path,
12751 std::string_view tag,
12752 bool force, bool recursive, bool repair,
12753 Formatter *f, Context *fin)
12754 {
12755 dout(10) << __func__ << " " << path << dendl;
12756
12757 filepath fp;
12758 if (path.compare(0, 4, "~mds") == 0) {
12759 mds_rank_t rank;
12760 if (path == "~mdsdir") {
12761 rank = mds->get_nodeid();
12762 } else {
12763 std::string err;
12764 rank = strict_strtoll(path.substr(4), 10, &err);
12765 if (!err.empty())
12766 rank = MDS_RANK_NONE;
12767 }
12768 if (rank >= 0 && rank < MAX_MDS)
12769 fp.set_path("", MDS_INO_MDSDIR(rank));
12770 }
12771 if (fp.get_ino() == inodeno_t(0))
12772 fp.set_path(path);
12773
12774 MDRequestRef mdr = request_start_internal(CEPH_MDS_OP_ENQUEUE_SCRUB);
12775 mdr->set_filepath(fp);
12776
12777 bool is_internal = false;
12778 std::string tag_str(tag);
12779 if (tag_str.empty()) {
12780 uuid_d uuid_gen;
12781 uuid_gen.generate_random();
12782 tag_str = uuid_gen.to_string();
12783 is_internal = true;
12784 }
12785
12786 C_MDS_EnqueueScrub *cs = new C_MDS_EnqueueScrub(tag_str, f, fin);
12787 cs->header = std::make_shared<ScrubHeader>(tag_str, is_internal, force, recursive, repair);
12788
12789 mdr->internal_op_finish = cs;
12790 enqueue_scrub_work(mdr);
12791 }
12792
12793 void MDCache::enqueue_scrub_work(MDRequestRef& mdr)
12794 {
12795 CInode *in;
12796 CF_MDS_RetryRequestFactory cf(this, mdr, true);
12797 int r = path_traverse(mdr, cf, mdr->get_filepath(),
12798 MDS_TRAVERSE_DISCOVER | MDS_TRAVERSE_RDLOCK_PATH,
12799 nullptr, &in);
12800 if (r > 0)
12801 return;
12802 if (r < 0) {
12803 mds->server->respond_to_request(mdr, r);
12804 return;
12805 }
12806
12807 // Cannot scrub same dentry twice at same time
12808 if (in->scrub_is_in_progress()) {
12809 mds->server->respond_to_request(mdr, -CEPHFS_EBUSY);
12810 return;
12811 } else {
12812 in->scrub_info();
12813 }
12814
12815 C_MDS_EnqueueScrub *cs = static_cast<C_MDS_EnqueueScrub*>(mdr->internal_op_finish);
12816 ScrubHeaderRef& header = cs->header;
12817
12818 r = mds->scrubstack->enqueue(in, header, !header->get_recursive());
12819
12820 mds->server->respond_to_request(mdr, r);
12821 }
12822
12823 struct C_MDC_RespondInternalRequest : public MDCacheLogContext {
12824 MDRequestRef mdr;
12825 C_MDC_RespondInternalRequest(MDCache *c, MDRequestRef& m) :
12826 MDCacheLogContext(c), mdr(m) {}
12827 void finish(int r) override {
12828 mdr->apply();
12829 get_mds()->server->respond_to_request(mdr, r);
12830 }
12831 };
12832
12833 struct C_MDC_ScrubRepaired : public MDCacheContext {
12834 ScrubHeaderRef header;
12835 public:
12836 C_MDC_ScrubRepaired(MDCache *m, const ScrubHeaderRef& h)
12837 : MDCacheContext(m), header(h) {
12838 header->inc_num_pending();
12839 }
12840 void finish(int r) override {
12841 header->dec_num_pending();
12842 }
12843 };
12844
12845 void MDCache::repair_dirfrag_stats(CDir *dir)
12846 {
12847 MDRequestRef mdr = request_start_internal(CEPH_MDS_OP_REPAIR_FRAGSTATS);
12848 mdr->pin(dir);
12849 mdr->internal_op_private = dir;
12850 if (dir->scrub_is_in_progress())
12851 mdr->internal_op_finish = new C_MDC_ScrubRepaired(this, dir->get_scrub_header());
12852 else
12853 mdr->internal_op_finish = new C_MDSInternalNoop;
12854 repair_dirfrag_stats_work(mdr);
12855 }
12856
12857 void MDCache::repair_dirfrag_stats_work(MDRequestRef& mdr)
12858 {
12859 CDir *dir = static_cast<CDir*>(mdr->internal_op_private);
12860 dout(10) << __func__ << " " << *dir << dendl;
12861
12862 if (!dir->is_auth()) {
12863 mds->server->respond_to_request(mdr, -CEPHFS_ESTALE);
12864 return;
12865 }
12866
12867 if (!mdr->is_auth_pinned(dir) && !dir->can_auth_pin()) {
12868 dir->add_waiter(CDir::WAIT_UNFREEZE, new C_MDS_RetryRequest(this, mdr));
12869
12870 mds->locker->drop_locks(mdr.get());
12871 mdr->drop_local_auth_pins();
12872 if (mdr->is_any_remote_auth_pin())
12873 mds->locker->notify_freeze_waiter(dir);
12874 return;
12875 }
12876
12877 mdr->auth_pin(dir);
12878
12879 MutationImpl::LockOpVec lov;
12880 CInode *diri = dir->inode;
12881 lov.add_rdlock(&diri->dirfragtreelock);
12882 lov.add_wrlock(&diri->nestlock);
12883 lov.add_wrlock(&diri->filelock);
12884 if (!mds->locker->acquire_locks(mdr, lov))
12885 return;
12886
12887 if (!dir->is_complete()) {
12888 dir->fetch(new C_MDS_RetryRequest(this, mdr));
12889 return;
12890 }
12891
12892 frag_info_t frag_info;
12893 nest_info_t nest_info;
12894 for (auto it = dir->begin(); it != dir->end(); ++it) {
12895 CDentry *dn = it->second;
12896 if (dn->last != CEPH_NOSNAP)
12897 continue;
12898 CDentry::linkage_t *dnl = dn->get_projected_linkage();
12899 if (dnl->is_primary()) {
12900 CInode *in = dnl->get_inode();
12901 nest_info.add(in->get_projected_inode()->accounted_rstat);
12902 if (in->is_dir())
12903 frag_info.nsubdirs++;
12904 else
12905 frag_info.nfiles++;
12906 } else if (dnl->is_remote())
12907 frag_info.nfiles++;
12908 }
12909
12910 auto pf = dir->get_projected_fnode();
12911 bool good_fragstat = frag_info.same_sums(pf->fragstat);
12912 bool good_rstat = nest_info.same_sums(pf->rstat);
12913 if (good_fragstat && good_rstat) {
12914 dout(10) << __func__ << " no corruption found" << dendl;
12915 mds->server->respond_to_request(mdr, 0);
12916 return;
12917 }
12918
12919 auto _pf = dir->project_fnode(mdr);
12920 _pf->version = dir->pre_dirty();
12921 pf = _pf;
12922
12923 mdr->ls = mds->mdlog->get_current_segment();
12924 EUpdate *le = new EUpdate(mds->mdlog, "repair_dirfrag");
12925 mds->mdlog->start_entry(le);
12926
12927 if (!good_fragstat) {
12928 if (pf->fragstat.mtime > frag_info.mtime)
12929 frag_info.mtime = pf->fragstat.mtime;
12930 if (pf->fragstat.change_attr > frag_info.change_attr)
12931 frag_info.change_attr = pf->fragstat.change_attr;
12932 _pf->fragstat = frag_info;
12933 mds->locker->mark_updated_scatterlock(&diri->filelock);
12934 mdr->ls->dirty_dirfrag_dir.push_back(&diri->item_dirty_dirfrag_dir);
12935 mdr->add_updated_lock(&diri->filelock);
12936 }
12937
12938 if (!good_rstat) {
12939 if (pf->rstat.rctime > nest_info.rctime)
12940 nest_info.rctime = pf->rstat.rctime;
12941 _pf->rstat = nest_info;
12942 mds->locker->mark_updated_scatterlock(&diri->nestlock);
12943 mdr->ls->dirty_dirfrag_nest.push_back(&diri->item_dirty_dirfrag_nest);
12944 mdr->add_updated_lock(&diri->nestlock);
12945 }
12946
12947 le->metablob.add_dir_context(dir);
12948 le->metablob.add_dir(dir, true);
12949
12950 mds->mdlog->submit_entry(le, new C_MDC_RespondInternalRequest(this, mdr));
12951 }
12952
12953 void MDCache::repair_inode_stats(CInode *diri)
12954 {
12955 MDRequestRef mdr = request_start_internal(CEPH_MDS_OP_REPAIR_INODESTATS);
12956 mdr->auth_pin(diri); // already auth pinned by CInode::validate_disk_state()
12957 mdr->internal_op_private = diri;
12958 if (diri->scrub_is_in_progress())
12959 mdr->internal_op_finish = new C_MDC_ScrubRepaired(this, diri->get_scrub_header());
12960 else
12961 mdr->internal_op_finish = new C_MDSInternalNoop;
12962 repair_inode_stats_work(mdr);
12963 }
12964
12965 void MDCache::repair_inode_stats_work(MDRequestRef& mdr)
12966 {
12967 CInode *diri = static_cast<CInode*>(mdr->internal_op_private);
12968 dout(10) << __func__ << " " << *diri << dendl;
12969
12970 if (!diri->is_auth()) {
12971 mds->server->respond_to_request(mdr, -CEPHFS_ESTALE);
12972 return;
12973 }
12974 if (!diri->is_dir()) {
12975 mds->server->respond_to_request(mdr, -CEPHFS_ENOTDIR);
12976 return;
12977 }
12978
12979 MutationImpl::LockOpVec lov;
12980
12981 if (mdr->ls) // already marked filelock/nestlock dirty ?
12982 goto do_rdlocks;
12983
12984 lov.add_rdlock(&diri->dirfragtreelock);
12985 lov.add_wrlock(&diri->nestlock);
12986 lov.add_wrlock(&diri->filelock);
12987 if (!mds->locker->acquire_locks(mdr, lov))
12988 return;
12989
12990 // Fetch all dirfrags and mark filelock/nestlock dirty. This will tirgger
12991 // the scatter-gather process, which will fix any fragstat/rstat errors.
12992 {
12993 frag_vec_t leaves;
12994 diri->dirfragtree.get_leaves(leaves);
12995 for (const auto& leaf : leaves) {
12996 CDir *dir = diri->get_dirfrag(leaf);
12997 if (!dir) {
12998 ceph_assert(mdr->is_auth_pinned(diri));
12999 dir = diri->get_or_open_dirfrag(this, leaf);
13000 }
13001 if (dir->get_version() == 0) {
13002 ceph_assert(dir->is_auth());
13003 dir->fetch(new C_MDS_RetryRequest(this, mdr));
13004 return;
13005 }
13006 }
13007 }
13008
13009 diri->state_set(CInode::STATE_REPAIRSTATS);
13010 mdr->ls = mds->mdlog->get_current_segment();
13011 mds->locker->mark_updated_scatterlock(&diri->filelock);
13012 mdr->ls->dirty_dirfrag_dir.push_back(&diri->item_dirty_dirfrag_dir);
13013 mds->locker->mark_updated_scatterlock(&diri->nestlock);
13014 mdr->ls->dirty_dirfrag_nest.push_back(&diri->item_dirty_dirfrag_nest);
13015
13016 mds->locker->drop_locks(mdr.get());
13017
13018 do_rdlocks:
13019 // force the scatter-gather process
13020 lov.clear();
13021 lov.add_rdlock(&diri->dirfragtreelock);
13022 lov.add_rdlock(&diri->nestlock);
13023 lov.add_rdlock(&diri->filelock);
13024 if (!mds->locker->acquire_locks(mdr, lov))
13025 return;
13026
13027 diri->state_clear(CInode::STATE_REPAIRSTATS);
13028
13029 frag_info_t dir_info;
13030 nest_info_t nest_info;
13031 nest_info.rsubdirs = 1; // it gets one to account for self
13032 if (const sr_t *srnode = diri->get_projected_srnode(); srnode)
13033 nest_info.rsnaps = srnode->snaps.size();
13034
13035 {
13036 frag_vec_t leaves;
13037 diri->dirfragtree.get_leaves(leaves);
13038 for (const auto& leaf : leaves) {
13039 CDir *dir = diri->get_dirfrag(leaf);
13040 ceph_assert(dir);
13041 ceph_assert(dir->get_version() > 0);
13042 dir_info.add(dir->get_fnode()->accounted_fragstat);
13043 nest_info.add(dir->get_fnode()->accounted_rstat);
13044 }
13045 }
13046
13047 if (!dir_info.same_sums(diri->get_inode()->dirstat) ||
13048 !nest_info.same_sums(diri->get_inode()->rstat)) {
13049 dout(10) << __func__ << " failed to fix fragstat/rstat on "
13050 << *diri << dendl;
13051 }
13052
13053 mds->server->respond_to_request(mdr, 0);
13054 }
13055
13056 void MDCache::rdlock_dirfrags_stats(CInode *diri, MDSInternalContext* fin)
13057 {
13058 MDRequestRef mdr = request_start_internal(CEPH_MDS_OP_RDLOCK_FRAGSSTATS);
13059 mdr->auth_pin(diri); // already auth pinned by CInode::validate_disk_state()
13060 mdr->internal_op_private = diri;
13061 mdr->internal_op_finish = fin;
13062 return rdlock_dirfrags_stats_work(mdr);
13063 }
13064
13065 void MDCache::rdlock_dirfrags_stats_work(MDRequestRef& mdr)
13066 {
13067 CInode *diri = static_cast<CInode*>(mdr->internal_op_private);
13068 dout(10) << __func__ << " " << *diri << dendl;
13069 if (!diri->is_auth()) {
13070 mds->server->respond_to_request(mdr, -CEPHFS_ESTALE);
13071 return;
13072 }
13073 if (!diri->is_dir()) {
13074 mds->server->respond_to_request(mdr, -CEPHFS_ENOTDIR);
13075 return;
13076 }
13077
13078 MutationImpl::LockOpVec lov;
13079 lov.add_rdlock(&diri->dirfragtreelock);
13080 lov.add_rdlock(&diri->nestlock);
13081 lov.add_rdlock(&diri->filelock);
13082 if (!mds->locker->acquire_locks(mdr, lov))
13083 return;
13084 dout(10) << __func__ << " start dirfrags : " << *diri << dendl;
13085
13086 mds->server->respond_to_request(mdr, 0);
13087 return;
13088 }
13089
13090 void MDCache::flush_dentry(std::string_view path, Context *fin)
13091 {
13092 if (is_readonly()) {
13093 dout(10) << __func__ << ": read-only FS" << dendl;
13094 fin->complete(-CEPHFS_EROFS);
13095 return;
13096 }
13097 dout(10) << "flush_dentry " << path << dendl;
13098 MDRequestRef mdr = request_start_internal(CEPH_MDS_OP_FLUSH);
13099 filepath fp(path);
13100 mdr->set_filepath(fp);
13101 mdr->internal_op_finish = fin;
13102 flush_dentry_work(mdr);
13103 }
13104
13105 class C_FinishIOMDR : public MDSContext {
13106 protected:
13107 MDSRank *mds;
13108 MDRequestRef mdr;
13109 MDSRank *get_mds() override { return mds; }
13110 public:
13111 C_FinishIOMDR(MDSRank *mds_, MDRequestRef& mdr_) : mds(mds_), mdr(mdr_) {}
13112 void finish(int r) override { mds->server->respond_to_request(mdr, r); }
13113 };
13114
13115 void MDCache::flush_dentry_work(MDRequestRef& mdr)
13116 {
13117 MutationImpl::LockOpVec lov;
13118 CInode *in = mds->server->rdlock_path_pin_ref(mdr, true);
13119 if (!in)
13120 return;
13121
13122 ceph_assert(in->is_auth());
13123 in->flush(new C_FinishIOMDR(mds, mdr));
13124 }
13125
13126
13127 /**
13128 * Initialize performance counters with global perfcounter
13129 * collection.
13130 */
13131 void MDCache::register_perfcounters()
13132 {
13133 PerfCountersBuilder pcb(g_ceph_context, "mds_cache", l_mdc_first, l_mdc_last);
13134
13135 // Stray/purge statistics
13136 pcb.add_u64(l_mdc_num_strays, "num_strays", "Stray dentries", "stry",
13137 PerfCountersBuilder::PRIO_INTERESTING);
13138 pcb.add_u64(l_mdc_num_recovering_enqueued,
13139 "num_recovering_enqueued", "Files waiting for recovery", "recy",
13140 PerfCountersBuilder::PRIO_INTERESTING);
13141 pcb.add_u64_counter(l_mdc_recovery_completed,
13142 "recovery_completed", "File recoveries completed", "recd",
13143 PerfCountersBuilder::PRIO_INTERESTING);
13144
13145 // useful recovery queue statistics
13146 pcb.set_prio_default(PerfCountersBuilder::PRIO_USEFUL);
13147 pcb.add_u64(l_mdc_num_recovering_processing, "num_recovering_processing",
13148 "Files currently being recovered");
13149 pcb.add_u64(l_mdc_num_recovering_prioritized, "num_recovering_prioritized",
13150 "Files waiting for recovery with elevated priority");
13151 pcb.add_u64_counter(l_mdc_recovery_started, "recovery_started",
13152 "File recoveries started");
13153
13154 // along with other stray dentries stats
13155 pcb.add_u64(l_mdc_num_strays_delayed, "num_strays_delayed",
13156 "Stray dentries delayed");
13157 pcb.add_u64(l_mdc_num_strays_enqueuing, "num_strays_enqueuing",
13158 "Stray dentries enqueuing for purge");
13159 pcb.add_u64_counter(l_mdc_strays_created, "strays_created",
13160 "Stray dentries created");
13161 pcb.add_u64_counter(l_mdc_strays_enqueued, "strays_enqueued",
13162 "Stray dentries enqueued for purge");
13163 pcb.add_u64_counter(l_mdc_strays_reintegrated, "strays_reintegrated",
13164 "Stray dentries reintegrated");
13165 pcb.add_u64_counter(l_mdc_strays_migrated, "strays_migrated",
13166 "Stray dentries migrated");
13167
13168 // low prio internal request stats
13169 pcb.add_u64_counter(l_mdss_ireq_enqueue_scrub, "ireq_enqueue_scrub",
13170 "Internal Request type enqueue scrub");
13171 pcb.add_u64_counter(l_mdss_ireq_exportdir, "ireq_exportdir",
13172 "Internal Request type export dir");
13173 pcb.add_u64_counter(l_mdss_ireq_flush, "ireq_flush",
13174 "Internal Request type flush");
13175 pcb.add_u64_counter(l_mdss_ireq_fragmentdir, "ireq_fragmentdir",
13176 "Internal Request type fragmentdir");
13177 pcb.add_u64_counter(l_mdss_ireq_fragstats, "ireq_fragstats",
13178 "Internal Request type frag stats");
13179 pcb.add_u64_counter(l_mdss_ireq_inodestats, "ireq_inodestats",
13180 "Internal Request type inode stats");
13181
13182 logger.reset(pcb.create_perf_counters());
13183 g_ceph_context->get_perfcounters_collection()->add(logger.get());
13184 recovery_queue.set_logger(logger.get());
13185 stray_manager.set_logger(logger.get());
13186 }
13187
13188 /**
13189 * Call this when putting references to an inode/dentry or
13190 * when attempting to trim it.
13191 *
13192 * If this inode is no longer linked by anyone, and this MDS
13193 * rank holds the primary dentry, and that dentry is in a stray
13194 * directory, then give up the dentry to the StrayManager, never
13195 * to be seen again by MDCache.
13196 *
13197 * @param delay if true, then purgeable inodes are stashed til
13198 * the next trim(), rather than being purged right
13199 * away.
13200 */
13201 void MDCache::maybe_eval_stray(CInode *in, bool delay) {
13202 if (in->get_inode()->nlink > 0 || in->is_base() || is_readonly() ||
13203 mds->get_state() <= MDSMap::STATE_REJOIN)
13204 return;
13205
13206 CDentry *dn = in->get_projected_parent_dn();
13207
13208 if (dn->state_test(CDentry::STATE_PURGING)) {
13209 /* We have already entered the purging process, no need
13210 * to re-evaluate me ! */
13211 return;
13212 }
13213
13214 if (dn->get_dir()->get_inode()->is_stray()) {
13215 if (delay)
13216 stray_manager.queue_delayed(dn);
13217 else
13218 stray_manager.eval_stray(dn);
13219 }
13220 }
13221
13222 void MDCache::clear_dirty_bits_for_stray(CInode* diri) {
13223 dout(10) << __func__ << " " << *diri << dendl;
13224 ceph_assert(diri->get_projected_parent_dir()->inode->is_stray());
13225 auto&& ls = diri->get_dirfrags();
13226 for (auto &p : ls) {
13227 if (p->is_auth() && !(p->is_frozen() || p->is_freezing()))
13228 p->try_remove_dentries_for_stray();
13229 }
13230 if (!diri->snaprealm) {
13231 if (diri->is_auth())
13232 diri->clear_dirty_rstat();
13233 diri->clear_scatter_dirty();
13234 }
13235 }
13236
13237 bool MDCache::dump_inode(Formatter *f, uint64_t number) {
13238 CInode *in = get_inode(number);
13239 if (!in) {
13240 return false;
13241 }
13242 f->open_object_section("inode");
13243 in->dump(f, CInode::DUMP_DEFAULT | CInode::DUMP_PATH);
13244 f->close_section();
13245 return true;
13246 }
13247
13248 void MDCache::handle_mdsmap(const MDSMap &mdsmap, const MDSMap &oldmap) {
13249 const mds_rank_t max_mds = mdsmap.get_max_mds();
13250
13251 // process export_pin_delayed_queue whenever a new MDSMap received
13252 auto &q = export_pin_delayed_queue;
13253 for (auto it = q.begin(); it != q.end(); ) {
13254 auto *in = *it;
13255 mds_rank_t export_pin = in->get_export_pin(false);
13256 dout(10) << " delayed export_pin=" << export_pin << " on " << *in
13257 << " max_mds=" << max_mds << dendl;
13258 if (export_pin >= mdsmap.get_max_mds()) {
13259 it++;
13260 continue;
13261 }
13262
13263 in->state_clear(CInode::STATE_DELAYEDEXPORTPIN);
13264 it = q.erase(it);
13265 in->queue_export_pin(export_pin);
13266 }
13267
13268 if (mdsmap.get_max_mds() != oldmap.get_max_mds()) {
13269 dout(10) << "Checking ephemerally pinned directories for redistribute due to max_mds change." << dendl;
13270 /* copy to vector to avoid removals during iteration */
13271 std::vector<CInode*> migrate;
13272 migrate.assign(export_ephemeral_pins.begin(), export_ephemeral_pins.end());
13273 for (auto& in : migrate) {
13274 in->maybe_export_pin();
13275 }
13276 }
13277
13278 if (max_mds <= 1) {
13279 export_ephemeral_dist_frag_bits = 0;
13280 } else {
13281 double want = g_conf().get_val<double>("mds_export_ephemeral_distributed_factor");
13282 want *= max_mds;
13283 unsigned n = 0;
13284 while ((1U << n) < (unsigned)want)
13285 ++n;
13286 export_ephemeral_dist_frag_bits = n;
13287 }
13288 }
13289
13290 void MDCache::upkeep_main(void)
13291 {
13292 std::unique_lock lock(upkeep_mutex);
13293 while (!upkeep_trim_shutdown.load()) {
13294 auto now = clock::now();
13295 auto since = now-upkeep_last_trim;
13296 auto trim_interval = clock::duration(g_conf().get_val<std::chrono::seconds>("mds_cache_trim_interval"));
13297 if (since >= trim_interval*.90) {
13298 lock.unlock(); /* mds_lock -> upkeep_mutex */
13299 std::scoped_lock mds_lock(mds->mds_lock);
13300 lock.lock();
13301 if (upkeep_trim_shutdown.load())
13302 return;
13303 check_memory_usage();
13304 if (mds->is_cache_trimmable()) {
13305 dout(20) << "upkeep thread trimming cache; last trim " << since << " ago" << dendl;
13306 bool active_with_clients = mds->is_active() || mds->is_clientreplay() || mds->is_stopping();
13307 if (active_with_clients) {
13308 trim_client_leases();
13309 }
13310 if (is_open()) {
13311 trim();
13312 }
13313 if (active_with_clients) {
13314 auto recall_flags = Server::RecallFlags::ENFORCE_MAX|Server::RecallFlags::ENFORCE_LIVENESS;
13315 if (cache_toofull()) {
13316 recall_flags = recall_flags|Server::RecallFlags::TRIM;
13317 }
13318 mds->server->recall_client_state(nullptr, recall_flags);
13319 }
13320 upkeep_last_trim = now = clock::now();
13321 } else {
13322 dout(10) << "cache not ready for trimming" << dendl;
13323 }
13324 } else {
13325 trim_interval -= since;
13326 }
13327 since = now-upkeep_last_release;
13328 auto release_interval = clock::duration(g_conf().get_val<std::chrono::seconds>("mds_cache_release_free_interval"));
13329 if (since >= release_interval*.90) {
13330 /* XXX not necessary once MDCache uses PriorityCache */
13331 dout(10) << "releasing free memory" << dendl;
13332 ceph_heap_release_free_memory();
13333 upkeep_last_release = clock::now();
13334 } else {
13335 release_interval -= since;
13336 }
13337 auto interval = std::min(release_interval, trim_interval);
13338 dout(20) << "upkeep thread waiting interval " << interval << dendl;
13339 upkeep_cvar.wait_for(lock, interval);
13340 }
13341 }