void TContainerHolder::RemoveLeftovers() { TError error; for (auto hy: Hierarchies) { std::vector<TCgroup> cgroups; error = hy->Cgroup(PORTO_ROOT_CGROUP).ChildsAll(cgroups); if (error) L_ERR() << "Cannot dump porto " << hy->Type << " cgroups : " << error << std::endl; for (auto cg = cgroups.rbegin(); cg != cgroups.rend(); cg++) { std::string name = cg->Name.substr(strlen(PORTO_ROOT_CGROUP) + 1); if (Containers.count(name)) continue; if (!cg->IsEmpty()) (void)cg->KillAll(9); (void)cg->Remove(); } } for (auto it: Containers) { auto container = it.second; if (container->Prop->Get<bool>(P_WEAK)) { auto holder_lock = ScopedLock(); L_ACT() << "Destroy weak container " << it.first << std::endl; Destroy(holder_lock, container); } } }
TError TCgroup::Remove() const { struct stat st; TError error; if (Secondary()) return TError(EError::Unknown, "Cannot create secondary cgroup " + Type()); L_ACT() << "Remove cgroup " << *this << std::endl; error = Path().Rmdir(); /* workaround for bad synchronization */ if (error && error.GetErrno() == EBUSY && !Path().StatStrict(st) && st.st_nlink == 2) { for (int i = 0; i < 100; i++) { usleep(config().daemon().cgroup_remove_timeout_s() * 10000); error = Path().Rmdir(); if (!error || error.GetErrno() != EBUSY) break; } } if (error && (error.GetErrno() != ENOENT || Exists())) L_ERR() << "Cannot remove cgroup " << *this << " : " << error << std::endl; return error; }
TError TContainerHolder::Restore(TScopedLock &holder_lock, const std::string &name, const kv::TNode &node) { if (name == ROOT_CONTAINER || name == PORTO_ROOT_CONTAINER) return TError::Success(); L_ACT() << "Restore container " << name << " (" << node.ShortDebugString() << ")" << std::endl; auto parent = GetParent(name); if (!parent) return TError(EError::InvalidValue, "invalid parent container"); int id = 0; TError error = RestoreId(node, id); if (error) return error; if (!id) return TError(EError::Unknown, "Couldn't restore container id"); auto c = std::make_shared<TContainer>(shared_from_this(), Storage, name, parent, id); error = c->Restore(holder_lock, node); if (error) { L_ERR() << "Can't restore container " << name << ": " << error << std::endl; return error; } Containers[name] = c; Statistics->Created++; return TError::Success(); }
void TTclass::Prepare(std::map<std::string, uint64_t> prio, std::map<std::string, uint64_t> rate, std::map<std::string, uint64_t> ceil) { L_ACT() << "Prepare tc class 0x" << std::hex << Handle << std::dec << " prio={" << MapToStr(prio) << "} rate={" << MapToStr(rate) << "} ceil={" << MapToStr(ceil) << "}" << std::endl; Prio = prio; Rate = rate; Ceil = ceil; }
TError TCgroup::Attach(pid_t pid) const { if (Secondary()) return TError(EError::Unknown, "Cannot attach to secondary cgroup " + Type()); L_ACT() << "Attach process " << pid << " to " << *this << std::endl; TError error = Knob("cgroup.procs").WriteAll(std::to_string(pid)); if (error) L_ERR() << "Cannot attach process " << pid << " to " << *this << " : " << error << std::endl; return error; }
TError TCgroup::Create() const { TError error; if (Secondary()) return TError(EError::Unknown, "Cannot create secondary cgroup " + Type()); L_ACT() << "Create cgroup " << *this << std::endl; error = Path().Mkdir(0755); if (error) L_ERR() << "Cannot create cgroup " << *this << " : " << error << std::endl; return error; }
bool TContainerHolder::RestoreFromStorage() { std::vector<std::shared_ptr<TKeyValueNode>> nodes; auto holder_lock = ScopedLock(); TError error = Storage->ListNodes(nodes); if (error) { L_ERR() << "Can't list key-value nodes: " << error << std::endl; return false; } auto name2node = SortNodes(nodes); bool restored = false; for (auto &pair : name2node) { auto node = pair.second; auto name = pair.first; L_ACT() << "Found " << name << " container in kvs" << std::endl; kv::TNode n; error = node->Load(n); if (error) continue; restored = true; error = Restore(holder_lock, name, n); if (error) { L_ERR() << "Can't restore " << name << ": " << error << std::endl; Statistics->RestoreFailed++; node->Remove(); continue; } // FIXME since v1.0 we need to cleanup kvalue nodes with old naming if (TKeyValueStorage::Get(n, P_RAW_NAME, name)) node->Remove(); } if (restored) { for (auto &c: Containers) { if (c.second->IsLostAndRestored()) { ScheduleCgroupSync(); break; } } } return restored; }
TError TCgroup::KillAll(int signal) const { std::vector<pid_t> tasks; TError error; L_ACT() << "KillAll " << signal << " " << *this << std::endl; error = GetTasks(tasks); if (!error) { for (const auto &pid : tasks) { if (kill(pid, signal) && errno != ESRCH) { error = TError(EError::Unknown, errno, StringFormat("kill(%d, %d)", pid, signal)); L_ERR() << "Cannot kill process " << pid << " : " << error << std::endl; } } } return error; }
TError TNetwork::Destroy() { auto lock = ScopedLock(); L_ACT() << "Removing network..." << std::endl; if (Tclass) { TError error = Tclass->Remove(); if (error) return error; Tclass = nullptr; } if (Qdisc) { TError error = Qdisc->Remove(); if (error) return error; Qdisc = nullptr; } return TError::Success(); }
bool TContainerHolder::DeliverEvent(const TEvent &event) { if (Verbose) L_EVT() << "Deliver event " << event.GetMsg() << std::endl; bool delivered = false; auto holder_lock = ScopedLock(); switch (event.Type) { case EEventType::OOM: { std::shared_ptr<TContainer> target = event.Container.lock(); if (target) { // check whether container can die due to OOM under holder lock, // assume container state is not changed when only holding // container lock if (target->MayReceiveOom(event.OOM.Fd)) { TNestedScopedLock lock(*target, holder_lock); if (target->IsValid() && target->MayReceiveOom(event.OOM.Fd)) { // we don't want any concurrent stop/start/pause/etc and // don't care whether parent acquired or not target->AcquireForced(); target->DeliverEvent(holder_lock, event); target->Release(); delivered = true; } } } break; } case EEventType::Respawn: { std::shared_ptr<TContainer> target = event.Container.lock(); if (target) { // check whether container can respawn under holder lock, // assume container state is not changed when only holding // container lock if (target->MayRespawn()) { TNestedScopedLock lock(*target, holder_lock); if (target->IsValid() && target->MayRespawn() && !target->IsAcquired()) { target->DeliverEvent(holder_lock, event); delivered = true; } } } break; } case EEventType::Exit: { auto list = List(); for (auto &target : list) { // check whether container can exit under holder lock, // assume container state is not changed when only holding // container lock if (target->MayExit(event.Exit.Pid)) { TNestedScopedLock lock(*target, holder_lock); if (target->IsValid() && target->MayExit(event.Exit.Pid)) { // we don't want any concurrent stop/start/pause/etc and // don't care whether parent acquired or not target->AcquireForced(); target->DeliverEvent(holder_lock, event); target->Release(); break; } } } AckExitStatus(event.Exit.Pid); delivered = true; break; } case EEventType::CgroupSync: { bool rearm = false; auto list = List(); for (auto &target : list) { // don't lock container here, LostAndRestored is never changed // after startup if (target->IsLostAndRestored()) rearm = true; if (target->IsAcquired()) continue; TNestedScopedLock lock(*target, holder_lock); if (target->IsValid() && target->IsLostAndRestored()) { if (target->IsAcquired()) continue; target->SyncStateWithCgroup(holder_lock); } } if (rearm) ScheduleCgroupSync(); delivered = true; break; } case EEventType::WaitTimeout: { auto w = event.WaitTimeout.Waiter.lock(); if (w) w->Signal(nullptr); delivered = true; break; } case EEventType::DestroyWeak: { auto container = event.Container.lock(); if (container) { TNestedScopedLock lock(*container, holder_lock); L_ACT() << "Destroy weak container " << container->GetName() << std::endl; Destroy(holder_lock, container); } } case EEventType::RotateLogs: { { /* gc */ std::vector<std::string> remove; for (auto target : List()) // don't lock container here, we don't care if we race, we // make real check under lock later if (target->CanRemoveDead()) remove.push_back(target->GetName()); for (auto name : remove) { std::shared_ptr<TContainer> container; TError error = Get(name, container); if (error) continue; TNestedScopedLock lock(*container, holder_lock, std::try_to_lock); if (!lock.IsLocked() || !container->IsValid() || !container->CanRemoveDead()) continue; TScopedAcquire acquire(container); if (!acquire.IsAcquired()) continue; L_ACT() << "Remove old dead " << name << std::endl; error = Destroy(holder_lock, container); if (error) L_ERR() << "Can't destroy " << name << ": " << error << std::endl; else Statistics->RemoveDead++; } } auto list = List(); for (auto &target : list) { if (target->IsAcquired()) continue; TNestedScopedLock lock(*target, holder_lock); if (target->IsValid() && !target->IsAcquired()) target->DeliverEvent(holder_lock, event); } ScheduleLogRotatation(); Statistics->Rotated++; delivered = true; break; } default: L_ERR() << "Unknown event " << event.GetMsg() << std::endl; } if (!delivered) L() << "Couldn't deliver " << event.GetMsg() << std::endl; return delivered; }
TError TCgroup::Set(const std::string &knob, const std::string &value) const { if (!Subsystem) return TError(EError::Unknown, "Cannot set to null cgroup"); L_ACT() << "Set " << *this << " " << knob << " = " << value << std::endl; return Knob(knob).WriteAll(value); }