当前位置:   article > 正文

Android&Linux系统suspend/resume机制_android suspend

android suspend

一、系统休眠唤醒机制
1.1 系统休眠唤醒介绍
一方面,在用户不需要系统工作的时候,系统休眠唤醒机制让系统尽可能进入一个功耗极低的状态,这时外部的设备、芯片内部ip、时钟进入了低功耗状态或关闭电源状态,从而尽可能的减少功耗,增加产品的续航;另一方面,在用户需要系统工作的时候,系统能够快速恢复电源、时钟、芯片内部ip及外部设备的工作,从而不影响用户的使用体验。系统休眠唤醒相比其他的功耗管理,对系统涉及面更广。系统休眠及唤醒过程涉及到pm core框架、device pm框架、用户进程及内核线程或worker,各设备驱动、power domain、cpu管理、process freeze&thaw、wakeup处理、设备suspend&resume、syscore suspend&resume、ddr自刷新等模块。
Linux内核提供了多种休眠(休眠)方式:freeze(idle)、standyby、STR(suspend to ram)和STD(suspend to disk),这些休眠方式通过文件节点/sys/power/state提供给用户操作,在用户空间通过向/sys/power/state文件节点分别写入freeze、standy、mem、disk,系统就会进入相应的状态。这样可以在系统需要进入相应状态的时候,由用户发起相应的休眠。在休眠之前会配置唤醒源,当系统休眠下去后,通过这些唤醒源(比如,按键、RTC、屏幕、USB拔插等)可以在需要的时候唤醒(resume)系统。这样在系统进入了休眠状态后,用户可以选择什么时刻,通过什么方式将系统快速唤醒,即兼顾了功耗低,又兼顾了性能。
1.2 系统休眠唤醒框架
在这里插入图片描述

系统休眠唤醒的框架包括三部分:Services、PM core、PM driver。
Services(user space):由三类service组成,system supend hal service、power manager service及普通的app service。其中,power manager service提供了wakelock锁的create/request/release管理功能,当没有services持有wakelock的话,power manager service会通过调用system supend hal service往文件节点/sys/power/state写mem发起内核的休眠。
PM core:实现power manage的核心逻辑,为上层services提供操作休眠唤醒的相关接口,通过利用底层相关的技术实现休眠唤醒过程中的cpu hotplug、wakup source enable/disable、设备的suspend&resume、freeze&thaw、power domain的关&开等功能或框架。
PM driver:主要实现了设备驱动的suspend&resume实现,架构驱动(gpio、irq、timer等)低功耗相关的操作。

1.3 系统休眠唤醒流程
在这里插入图片描述
流程框架左右是supend流程,右边是resume流程。
supend详细代码详解可参考:2.3;resume详细代码详解可参考:3.1

二、系统休眠机制
Suspend to idle、freezer、standby、ram模式,一般都称作系统休眠。用户空间通过写 /sys/power/state文件节点来触发system supend.
2.1 system suspend框架
在这里插入图片描述

详细代码参考:2.3.1、2.3.2、2.3.3、2.3.4
2.2 system suspend代码流程
在这里插入图片描述
2.3 system suspend核心代码逻辑
2.3.1 PowerManagerService模块核心代码逻辑


private static native void nativeSetAutoSuspend(boolean enable);
......
public void onDisplayStateChange(boolean allInactive, boolean allOff) {
    // This method is only needed to support legacy display blanking behavior// where the display's power state is coupled to suspend or to the power HAL.// The order of operations matters here.synchronized (mLock) {
        setPowerModeInternal(MODE_DISPLAY_INACTIVE, allInactive);
        // 灭屏状态
        if (allOff) {
            if (!mDecoupleHalInteractiveModeFromDisplayConfig) {
                setHalInteractiveModeLocked(false);
            }
            if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) {
                // 设置系统休眠的模式(mem、disk、standbly),默认为suspend to mem模式
                setHalAutoSuspendModeLocked(true);
            }
        } else {
            if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) {
                setHalAutoSuspendModeLocked(false);
            }
            if (!mDecoupleHalInteractiveModeFromDisplayConfig) {
                setHalInteractiveModeLocked(true);
            }
        }
    }
}

 // 设置系统休眠的模式(mem、disk、standbly),并进入suspend流程
private void setHalAutoSuspendModeLocked(boolean enable) {
    if (enable != mHalAutoSuspendModeEnabled) {
        if (DEBUG) {
            Slog.d(TAG, "Setting HAL auto-suspend mode to " + enable);
        }
        mHalAutoSuspendModeEnabled = enable;
        Trace.traceBegin(Trace.TRACE_TAG_POWER, "setHalAutoSuspend(" + enable + ")");
        try {
            // 通过JNI调用native层suspend接口
            mNativeWrapper.nativeSetAutoSuspend(enable);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_POWER);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

2.3.2 PMS JNI代码逻辑

static void nativeSetAutoSuspend(JNIEnv* /* env */, jclass /* clazz */, jboolean enable) { 
    // enable为true 
    if (enable) {
        android::base::Timer t;
        enableAutoSuspend();
        if (t.duration() > 100ms) {
            ALOGD("Excessive delay in autosuspend_enable() while turning screen off");
        }
    } else {
        android::base::Timer t;
        disableAutoSuspend();
        if (t.duration() > 100ms) {
            ALOGD("Excessive delay in autosuspend_disable() while turning screen on");
        }
    }
}

void enableAutoSuspend() {    
    static bool enabled = false;
    if (!enabled) {
        // 通过binder通信获取system suspen进程中SuspendControlServiceInternal服务
        static sp<IBinder> autosuspendClientToken = new BBinder();
        sp<system::suspend::internal::ISuspendControlServiceInternal> suspendControl =
                getSuspendControlInternal();
        // 调用binder API
        suspendControl->enableAutosuspend(autosuspendClientToken, &enabled);
    }

    {
        std::lock_guard<std::mutex> lock(gSuspendMutex);
        if (gSuspendBlocker) {
            gSuspendBlocker->release();
            gSuspendBlocker = nullptr;
        }
    }
}

// 获取SuspendControlServiceInternal 服务
sp<system::suspend::internal::ISuspendControlServiceInternal> getSuspendControlInternal() {    static std::once_flag suspendControlFlag;
    std::call_once(suspendControlFlag, []() {
        gSuspendControlInternal =
                waitForService<system::suspend::internal::ISuspendControlServiceInternal>(
                        String16("suspend_control_internal"));
        LOG_ALWAYS_FATAL_IF(gSuspendControlInternal == nullptr);
    });
    return gSuspendControlInternal;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

2.3.3 system suspend

// ISuspendControlServiceInternal.aidl

package android.system.suspend.internal;

import android.system.suspend.internal.SuspendInfo;
import android.system.suspend.internal.WakeLockInfo;
import android.system.suspend.internal.WakeupInfo;

/**
 * Interface exposed by the suspend hal that allows framework to toggle the suspend loop and
 * monitor native wakelocks.
 * @hide
 */interface ISuspendControlServiceInternal {
    /**
     * Starts automatic system suspension.
     *
     * @param token token registering automatic system suspension.
     * When all registered tokens die automatic system suspension is disabled.
     * @return true on success, false otherwise.
     */    boolean enableAutosuspend(IBinder token);
/**
     * Suspends the system even if there are wakelocks being held.
     */    boolean forceSuspend();
/**
     * Returns a list of wake lock stats.
     */    WakeLockInfo[] getWakeLockStats();
/**
     * Returns a list of wakeup stats.
     */    WakeupInfo[] getWakeupStats();
/**
     * Returns stats related to suspend.
     */    SuspendInfo getSuspendStats();
}

-----------------------------------------------------------------------------
// ISuspendControlService.aidl

package android.system.suspend;

import android.system.suspend.IWakelockCallback;
import android.system.suspend.ISuspendCallback;

/**
 * Interface exposed by the suspend hal that allows framework to toggle the suspend loop and
 * monitor native wakelocks.
 * @hide
 */interface ISuspendControlService {
    /**
     * Registers a callback for suspend events.  ISuspendControlService must keep track of all
     * registered callbacks unless the client process that registered the callback dies.
     *
     * @param callback the callback to register.
     * @return true on success, false otherwise.
     */    boolean registerCallback(ISuspendCallback callback);
/**
     * Registers a callback for a wakelock specified by its name.
     *
     * @param callback the callback to register.
     * @param name the name of the wakelock.
     * @return true on success, false otherwise.
     */    boolean registerWakelockCallback(IWakelockCallback callback, @utf8InCpp String name);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
// SuspendControlService.h

#include <android/system/suspend/BnSuspendControlService.h>
#include <android/system/suspend/internal/BnSuspendControlServiceInternal.h>
#include <android/system/suspend/internal/SuspendInfo.h>
#include <android/system/suspend/internal/WakeLockInfo.h>
#include <android/system/suspend/internal/WakeupInfo.h>

using ::android::system::suspend::BnSuspendControlService;
using ::android::system::suspend::ISuspendCallback;
using ::android::system::suspend::IWakelockCallback;
using ::android::system::suspend::internal::BnSuspendControlServiceInternal;
using ::android::system::suspend::internal::SuspendInfo;
using ::android::system::suspend::internal::WakeLockInfo;
using ::android::system::suspend::internal::WakeupInfo;

namespace android {
namespace system {
namespace suspend {
namespace V1_0 {

class SystemSuspend;

class SuspendControlService : public BnSuspendControlService,
                              public virtual IBinder::DeathRecipient {
   public:
    SuspendControlService() = default;
    ~SuspendControlService() override = default;

    binder::Status registerCallback(const sp<ISuspendCallback>& callback,
                                    bool* _aidl_return) override;
    binder::Status registerWakelockCallback(const sp<IWakelockCallback>& callback,
                                            const std::string& name, bool* _aidl_return) override;

    void binderDied(const wp<IBinder>& who) override;

    void notifyWakelock(const std::string& name, bool isAcquired);
    void notifyWakeup(bool success, std::vector<std::string>& wakeupReasons);

   private:
    std::map<std::string, std::vector<sp<IWakelockCallback>>> mWakelockCallbacks;
    std::mutex mCallbackLock;
    std::mutex mWakelockCallbackLock;
    std::vector<sp<ISuspendCallback>> mCallbacks;
    const std::vector<sp<ISuspendCallback>>::iterator findCb(const wp<IBinder>& cb) {        return std::find_if(
            mCallbacks.begin(), mCallbacks.end(),
            [&cb](const sp<ISuspendCallback>& i) { return cb == IInterface::asBinder(i); });
    }
};

class SuspendControlServiceInternal : public BnSuspendControlServiceInternal {
   public:
    SuspendControlServiceInternal() = default;
    ~SuspendControlServiceInternal() override = default;

    binder::Status enableAutosuspend(const sp<IBinder>& token, bool* _aidl_return) override;
    binder::Status forceSuspend(bool* _aidl_return) override;
    binder::Status getSuspendStats(SuspendInfo* _aidl_return) override;
    binder::Status getWakeLockStats(std::vector<WakeLockInfo>* _aidl_return) override;
    binder::Status getWakeupStats(std::vector<WakeupInfo>* _aidl_return) override;

    void setSuspendService(const wp<SystemSuspend>& suspend);
    status_t dump(int fd, const Vector<String16>& args) override;

   private:
    wp<SystemSuspend> mSuspend;
};

}  // namespace V1_0
}  // namespace suspend
}  // namespace system
}  // namespace android

// SuspendControlService.cpp

binder::Status SuspendControlServiceInternal::enableAutosuspend(const sp<IBinder>& token,                                                                bool* _aidl_return) {
    const auto suspendService = mSuspend.promote();
    return retOk(suspendService != nullptr && suspendService->enableAutosuspend(token),
                 _aidl_return);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
// SystemSuspend.h

#include <android-base/result.h>
#include <android-base/thread_annotations.h>
#include <android-base/unique_fd.h>
#include <android/system/suspend/internal/SuspendInfo.h>
#include <utils/RefBase.h>

#include <atomic>
#include <condition_variable>
#include <mutex>
#include <string>

#include "SuspendControlService.h"
#include "WakeLockEntryList.h"
#include "WakeupList.h"

namespace android {
namespace system {
namespace suspend {
namespace V1_0 {

using ::android::base::Result;
using ::android::base::unique_fd;
using ::android::system::suspend::internal::SuspendInfo;

using namespace std::chrono_literals;

class SystemSuspend;

struct SuspendStats {
    int success = 0;
    int fail = 0;
    int failedFreeze = 0;
    int failedPrepare = 0;
    int failedSuspend = 0;
    int failedSuspendLate = 0;
    int failedSuspendNoirq = 0;
    int failedResume = 0;
    int failedResumeEarly = 0;
    int failedResumeNoirq = 0;
    std::string lastFailedDev;
    int lastFailedErrno = 0;
    std::string lastFailedStep;
};

struct SleepTimeConfig {
    std::chrono::milliseconds baseSleepTime;
    std::chrono::milliseconds maxSleepTime;
    double sleepTimeScaleFactor;
    uint32_t backoffThreshold;
    std::chrono::milliseconds shortSuspendThreshold;
    bool failedSuspendBackoffEnabled;
    bool shortSuspendBackoffEnabled;
};

std::string readFd(int fd);

class SystemSuspend : public RefBase {
   public:
    SystemSuspend(unique_fd wakeupCountFd, unique_fd stateFd, unique_fd suspendStatsFd,
                  size_t maxStatsEntries, unique_fd kernelWakelockStatsFd,
                  unique_fd wakeupReasonsFd, unique_fd suspendTimeFd,
                  const SleepTimeConfig& sleepTimeConfig,
                  const sp<SuspendControlService>& controlService,
                  const sp<SuspendControlServiceInternal>& controlServiceInternal,
                  bool useSuspendCounter = true);
    void incSuspendCounter(const std::string& name);
    void decSuspendCounter(const std::string& name);
    bool enableAutosuspend(const sp<IBinder>& token);
    void disableAutosuspend();
    bool forceSuspend();

    const WakeupList& getWakeupList() const;
    const WakeLockEntryList& getStatsList() const;
    void updateWakeLockStatOnAcquire(const std::string& name, int pid);
    void updateWakeLockStatOnRelease(const std::string& name, int pid);
    void updateStatsNow();
    Result<SuspendStats> getSuspendStats();
    void getSuspendInfo(SuspendInfo* info);
    std::chrono::milliseconds getSleepTime() const;
    unique_fd reopenFileUsingFd(const int fd, int permission);

   private:
    ~SystemSuspend(void) override;

    std::mutex mAutosuspendClientTokensLock;
    std::mutex mAutosuspendLock ACQUIRED_AFTER(mAutosuspendClientTokensLock);
    std::mutex mSuspendInfoLock;

    void initAutosuspendLocked()
        EXCLUSIVE_LOCKS_REQUIRED(mAutosuspendClientTokensLock, mAutosuspendLock);
    void disableAutosuspendLocked()
        EXCLUSIVE_LOCKS_REQUIRED(mAutosuspendClientTokensLock, mAutosuspendLock);
    void checkAutosuspendClientsLivenessLocked()
        EXCLUSIVE_LOCKS_REQUIRED(mAutosuspendClientTokensLock);
    bool hasAliveAutosuspendTokenLocked() EXCLUSIVE_LOCKS_REQUIRED(mAutosuspendClientTokensLock);

    std::condition_variable mAutosuspendCondVar GUARDED_BY(mAutosuspendLock);
    uint32_t mSuspendCounter GUARDED_BY(mAutosuspendLock);

    std::vector<sp<IBinder>> mAutosuspendClientTokens GUARDED_BY(mAutosuspendClientTokensLock);
    std::atomic<bool> mAutosuspendEnabled GUARDED_BY(mAutosuspendLock){false};
    std::atomic<bool> mAutosuspendThreadCreated GUARDED_BY(mAutosuspendLock){false};
unique_fd mWakeupCountFd;
    unique_fd mStateFd;

    unique_fd mSuspendStatsFd;
    unique_fd mSuspendTimeFd;

    SuspendInfo mSuspendInfo GUARDED_BY(mSuspendInfoLock);

    const SleepTimeConfig kSleepTimeConfig;

    // Amount of thread sleep time between consecutive iterations of the suspend loopstd::chrono::milliseconds mSleepTime;
    int32_t mNumConsecutiveBadSuspends GUARDED_BY(mSuspendInfoLock);

    // Updates thread sleep time and suspend stats depending on the result of suspend attemptvoid updateSleepTime(bool success, const struct SuspendTime& suspendTime);

    sp<SuspendControlService> mControlService;
    sp<SuspendControlServiceInternal> mControlServiceInternal;

    WakeLockEntryList mStatsList;
    WakeupList mWakeupList;

    // If true, use mSuspendCounter to keep track of native wake locks. Otherwise, rely on// /sys/power/wake_lock interface to block suspend.// TODO(b/128923994): remove dependency on /sys/power/wake_lock interface.bool mUseSuspendCounter;
    unique_fd mWakeLockFd;
    unique_fd mWakeUnlockFd;
    unique_fd mWakeupReasonsFd;
};

}  // namespace V1_0
}  // namespace suspend
}  // namespace system
}  // namespace android
------------------------------------------------------------------------
// SystemSuspend.cpp

static const char kSleepState[] = "mem";

bool SystemSuspend::enableAutosuspend(const sp<IBinder>& token) {    auto tokensLock = std::lock_guard(mAutosuspendClientTokensLock);
    auto autosuspendLock = std::lock_guard(mAutosuspendLock);

    // Disable zygote kernel wakelock, since explicitly attempting to// enable autosuspend. This should be done even if autosuspend is// already enabled, since it could be the case that the framework// is restarting and connecting to the existing suspend service.if (!WriteStringToFd(kZygoteKernelWakelock, mWakeUnlockFd)) {
        PLOG(ERROR) << "error writing " << kZygoteKernelWakelock << " to " << kSysPowerWakeUnlock;
    }

    bool hasToken = std::find(mAutosuspendClientTokens.begin(), mAutosuspendClientTokens.end(),
                              token) != mAutosuspendClientTokens.end();

    if (!hasToken) {
        mAutosuspendClientTokens.push_back(token);
    }

    if (mAutosuspendEnabled) {
        LOG(ERROR) << "Autosuspend already started.";
        return false;
    }

    mAutosuspendEnabled = true;
    initAutosuspendLocked();
    return true;
}

// 创建auto suspend线程,每隔200ms
void SystemSuspend::initAutosuspendLocked() {    
    if (mAutosuspendThreadCreated) {
        LOG(INFO) << "Autosuspend thread already started.";
        return;
    }

    std::thread autosuspendThread([this] {
        auto autosuspendLock = std::unique_lock(mAutosuspendLock);
        bool shouldSleep = true;

        while (true) {
            {
                base::ScopedLockAssertion autosuspendLocked(mAutosuspendLock);

                if (!mAutosuspendEnabled) {
                    mAutosuspendThreadCreated = false;
                    return;
                }
                // If we got here by a failed write to /sys/power/wakeup_count; 
                //don't sleep// since we didn't attempt to suspend on the last cycle of this loop.
                if (shouldSleep) {
                    mAutosuspendCondVar.wait_for(
                        autosuspendLock, mSleepTime,
                        [this]() REQUIRES(mAutosuspendLock) { return !mAutosuspendEnabled; });
                }

                if (!mAutosuspendEnabled) continue;
                autosuspendLock.unlock();
            }

            lseek(mWakeupCountFd, 0, SEEK_SET);
            string wakeupCount = readFd(mWakeupCountFd);

            {
                autosuspendLock.lock();
                base::ScopedLockAssertion autosuspendLocked(mAutosuspendLock);

                if (wakeupCount.empty()) {
                    PLOG(ERROR) << "error reading from /sys/power/wakeup_count";
                    continue;
                }
                // 从/sys/power/wakeup_count节点读取数据,如果为空,则进入suspend失败
                shouldSleep = false;

                mAutosuspendCondVar.wait(autosuspendLock, [this]() REQUIRES(mAutosuspendLock) {
                    return mSuspendCounter == 0 || !mAutosuspendEnabled;
                });

                if (!mAutosuspendEnabled) continue;
                autosuspendLock.unlock();
            }

            bool success;
            {
                auto tokensLock = std::lock_guard(mAutosuspendClientTokensLock);
                // TODO: Clean up client tokens after soaking the new approach// checkAutosuspendClientsLivenessLocked();autosuspendLock.lock();
                base::ScopedLockAssertion autosuspendLocked(mAutosuspendLock);

                if (!hasAliveAutosuspendTokenLocked()) {
                    disableAutosuspendLocked();
                    continue;
                }

                // Check suspend counter hasn't increased while checking client livenessif (mSuspendCounter > 0) {
                    continue;
                }

                // The mutex is locked and *MUST* remain locked until we write to /sys/power/state.// Otherwise, a WakeLock might be acquired after we check mSuspendCounter and before// we write to /sys/power/state.if (!WriteStringToFd(wakeupCount, mWakeupCountFd)) {
                    PLOG(VERBOSE) << "error writing to /sys/power/wakeup_count";
                    continue;
                }
                // 将"men"字符串接入/sys/power/state节点,
                // 表示进入suspend to ram模式,调用kernel suspend的state_store函数
                success = WriteStringToFd(kSleepState, mStateFd);
                shouldSleep = true;

                autosuspendLock.unlock();
            }

            if (!success) {
                PLOG(VERBOSE) << "error writing to /sys/power/state";
            }

            struct SuspendTime suspendTime = readSuspendTime(mSuspendTimeFd);
            updateSleepTime(success, suspendTime);

            std::vector<std::string> wakeupReasons = readWakeupReasons(mWakeupReasonsFd);
            if (wakeupReasons == std::vector<std::string>({kUnknownWakeup})) {
                LOG(INFO) << "Unknown/empty wakeup reason. Re-opening wakeup_reason file.";

                mWakeupReasonsFd =
                    std::move(reopenFileUsingFd(mWakeupReasonsFd.get(), O_CLOEXEC | O_RDONLY));
            }
            mWakeupList.update(wakeupReasons);

            mControlService->notifyWakeup(success, wakeupReasons);

            // Take the lock before returning to the start of the loopautosuspendLock.lock();
        }
    });
    autosuspendThread.detach();
    mAutosuspendThreadCreated = true;
    LOG(INFO) << "automatic system suspend enabled";
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269

2.3.4 kernel suspend

// mian.c

// state_store是一个syscall调用函数,为进入suspend的入口函数,
// 接收user space传递的"mem",准备进入suspend to ram模式
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
                           const char *buf, size_t n)
{
        suspend_state_t state;
        int error;
        
        pr_info("%s:Userspace to kernel, process start write command \"echo mem > /sys/power/state\". The process is \"%s\"(pid %i). The thread is \"%s\"(tid %i).\n",
                STR_KERNEL_LOG_ENTER, current->group_leader->comm, current->group_leader->pid, current->comm, current->pid);
        
        error = pm_autosleep_lock();
        if (error)
                return error;
        
        if (pm_autosleep_state() > PM_SUSPEND_ON) {
                error = -EBUSY;
                goto out;
        }
        // "mem"字符串封装到suspend_state_t数据结构
        state = decode_state(buf, n);
        // 检查user space传递的suspend模式是否符合规定的范围
        if (state < PM_SUSPEND_MAX) {
            if (state == PM_SUSPEND_MEM)
                    state = mem_sleep_current;
            // 调用suspend.c函数,进入suspend阶段
            // 返回值为0表示suspend successed,否则supend failed,常用于suspend debug调试
            error = pm_suspend(state);
            if (!error) {
                    pr_info("%s:(( end )), kernel resume end.\n", STR_KERNEL_LOG_EXIT);
        } else if (state == PM_SUSPEND_MAX) {
        // PM_SUSPEND_MAX为suspend to disk,进入hibernate睡眠模式,走另一条流程
        error = hibernate();
        } else {
                error = -EINVAL;
        }

 out:
        pm_autosleep_unlock();
        return error ? error : n;
}

// suspend模式的字符串封装到suspend_state_t,并返回。如果是supend disk模式,则返回PM_SUSPEND_MAX
static suspend_state_t decode_state(const char *buf, size_t n)
{
#ifdef CONFIG_SUSPEND
        suspend_state_t state;
#endif
        char *p; 
        int len;

        p = memchr(buf, '\n', n); 
        len = p ? p - buf : n;

        /* Check hibernation first. */
        // 如果是supend disk模式,则返回PM_SUSPEND_MAX。表示系统进入suspend to disk
        if (len == 4 && str_has_prefix(buf, "disk"))
                return PM_SUSPEND_MAX;

#ifdef CONFIG_SUSPEND
        for (state = PM_SUSPEND_MIN; state < PM_SUSPEND_MAX; state++) {
                const char *label = pm_states[state];

                if (label && len == strlen(label) && !strncmp(buf, label, len))
                        return state;
        }
#endif

        return PM_SUSPEND_ON;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
// suspend.h

#define PM_SUSPEND_ON           ((__force suspend_state_t) 0)
#define PM_SUSPEND_TO_IDLE      ((__force suspend_state_t) 1)
#define PM_SUSPEND_STANDBY      ((__force suspend_state_t) 2)
#define PM_SUSPEND_MEM          ((__force suspend_state_t) 3)
#define PM_SUSPEND_MIN          PM_SUSPEND_TO_IDLE
#define PM_SUSPEND_MAX          ((__force suspend_state_t) 4)

enum suspend_stat_step {
        SUSPEND_FREEZE = 1,
        SUSPEND_PREPARE,
        SUSPEND_SUSPEND,
        SUSPEND_SUSPEND_LATE,
        SUSPEND_SUSPEND_NOIRQ,
        SUSPEND_RESUME_NOIRQ,
        SUSPEND_RESUME_EARLY,
        SUSPEND_RESUME
};

struct suspend_stats {
        int     success;
        int     fail;
        int     failed_freeze;
        int     failed_prepare;
        int     failed_suspend;
        int     failed_suspend_late;
        int     failed_suspend_noirq;
        int     failed_resume;
        int     failed_resume_early;
        int     failed_resume_noirq;
#define REC_FAILED_NUM  2
        int     last_failed_dev;
        char    failed_devs[REC_FAILED_NUM][40];
        int     last_failed_errno;
        int     errno[REC_FAILED_NUM];
        int     last_failed_step;
        enum suspend_stat_step  failed_steps[REC_FAILED_NUM];
};

#define PM_SUSPEND_FLAG_FW_SUSPEND      BIT(0)
#define PM_SUSPEND_FLAG_FW_RESUME       BIT(1)
#define PM_SUSPEND_FLAG_NO_PLATFORM     BIT(2)

/* Suspend-to-idle state machnine. */
enum s2idle_states {
        S2IDLE_STATE_NONE,      /* Not suspended/suspending. */
        S2IDLE_STATE_ENTER,     /* Enter suspend-to-idle. */
        S2IDLE_STATE_WAKE,      /* Wake up from suspend-to-idle. */
};

/* Hibernation and suspend events */
#define PM_HIBERNATION_PREPARE  0x0001 /* Going to hibernate */
#define PM_POST_HIBERNATION     0x0002 /* Hibernation finished */
#define PM_SUSPEND_PREPARE      0x0003 /* Going to suspend the system */
#define PM_POST_SUSPEND         0x0004 /* Suspend finished */
#define PM_RESTORE_PREPARE      0x0005 /* Going to restore a saved image */
#define PM_POST_RESTORE         0x0006 /* Restore failed */

----------------------------------------------------------------------------
// suspend.c

/**
 * pm_suspend - Externally visible function for suspending the system.
 * @state: System sleep state to enter.
 *
 * Check if the value of @state represents one of the supported states,
 * execute enter_state() and update system suspend statistics.
 */
 // 系统进入suspend模式
 // 更新系统suspend信息
int pm_suspend(suspend_state_t state)
{
        int error;

        if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)
                return -EINVAL;

        pr_info("suspend entry (%s)\n", mem_sleep_labels[state]);
        // 进入supend
        error = enter_state(state);
        // 统计suspend 成功与失败次数
        if (error) {
                suspend_stats.fail++;
                dpm_save_failed_errno(error);
        } else {
                suspend_stats.success++;
        }
        pr_info("suspend exit\n");
        return error;
}

/**
 * enter_state - Do common work needed to enter system sleep state.
 * @state: System sleep state to enter.
 *
 * Make sure that no one else is trying to put the system into a sleep state.
 * Fail if that's not the case.  Otherwise, prepare for system suspend, make the
 * system enter the given sleep state and clean up after wakeup.
 */
static int enter_state(suspend_state_t state)
{
        int error;

        trace_suspend_resume(TPS("suspend_enter"), state, true);
        if (state == PM_SUSPEND_TO_IDLE) {
#ifdef CONFIG_PM_DEBUG
                if (pm_test_level != TEST_NONE && pm_test_level <= TEST_CPUS) {
                        pr_warn("Unsupported test mode for suspend to idle, please choose none/freezer/devices/platform.\n");
                        return -EAGAIN;
                }
#endif
} else if (!valid_state(state)) {
                return -EINVAL;
        }
        if (!mutex_trylock(&system_transition_mutex))
                return -EBUSY;
        // 如果是进入suspend to idle模式,会先调用s2idle_begin函数,将设置s2idle_state = S2IDLE_STATE_NONE
        if (state == PM_SUSPEND_TO_IDLE)
                s2idle_begin();

        if (sync_on_suspend_enabled) {
                trace_suspend_resume(TPS("sync_filesystems"), 0, true);
                ksys_sync_helper();
                trace_suspend_resume(TPS("sync_filesystems"), 0, false);
        }

        pm_pr_dbg("Preparing system for sleep (%s)\n", mem_sleep_labels[state]);
        pm_suspend_clear_flags();
        // 1. 进入suspend第一阶段
        error = suspend_prepare(state);
        if (error)
                goto Unlock;

        if (suspend_test(TEST_FREEZER))
                goto Finish;

        trace_suspend_resume(TPS("suspend_enter"), state, false);
        pm_pr_dbg("Suspending system (%s)\n", mem_sleep_labels[state]);
        pm_restrict_gfp_mask();

        pr_info("%s:Suspend_devices.\n", STR_KERNEL_LOG_ENTER);
        // 2. 进入suspend的第二阶段
        error = suspend_devices_and_enter(state);
        pm_restore_gfp_mask();

 Finish:
        events_check_enabled = false;
        pm_pr_dbg("Finishing wakeup.\n");
        suspend_finish();
 Unlock:
        mutex_unlock(&system_transition_mutex);
        return error;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154

2.3.4.1 kernel suspend流程的第一阶段 —suspend_prepare

/**
 * suspend_prepare - Prepare for entering system sleep state.
 *
 * Common code run for every system sleep state that can be entered (except for
 * hibernation).  Run suspend notifiers, allocate the "suspend" console and
 * freeze processes.
 */
 // suspend idle、standby、mem模式都会走该流程,suspend disk另走hibernate()流程
 // 1. suspend流程的第一阶段
static int suspend_prepare(suspend_state_t state)
{
        int error;

        if (!sleep_state_supported(state))
                return -EPERM;
        // 1.1 切换控制台,使用suspend下的控制台,重定向到kmsg
        pm_prepare_console();
        // 1.2 suspend前调用阻塞通知链,通知PM_SUSPEND_PREPARE事件
        error = pm_notifier_call_chain_robust(PM_SUSPEND_PREPARE, PM_POST_SUSPEND);
        if (error)
                goto Restore;

        trace_suspend_resume(TPS("freeze_processes"), 0, true);
        // 1.3 冻结用户进程和部分内核线程及worker线程
        error = suspend_freeze_processes();
        trace_suspend_resume(TPS("freeze_processes"), 0, false);
        if (!error)
                return 0;
        log_suspend_abort_reason("One or more tasks refusing to freeze");
        suspend_stats.failed_freeze++;
        dpm_save_failed_step(SUSPEND_FREEZE);
        pm_notifier_call_chain(PM_POST_SUSPEND);
 Restore:
        pm_restore_console();
        return error;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
// console.c 
void pm_prepare_console(void)
{
        if (!pm_vt_switch())
                return;
        // 切换到suspend控制台
        orig_fgconsole = vt_move_to_console(SUSPEND_CONSOLE, 1); 
        if (orig_fgconsole < 0)
                return;
        // suspend控制台日志重定向到kmsg,方便调试
        orig_kmsg = vt_kmsg_redirect(SUSPEND_CONSOLE);
        return;
}
--------------------------------------------------------------------------------
// mian.c
int pm_notifier_call_chain_robust(unsigned long val_up, unsigned long val_down)
{
        int ret;

        ret = blocking_notifier_call_chain_robust(&pm_chain_head, val_up, val_down, NULL);

        return notifier_to_errno(ret);
}
--------------------------------------------------------------------------------
// notifier.c
// 调用阻塞通知链,接收suspend prepaer事件,通知注册在该链上的所有回调函数做处理
int blocking_notifier_call_chain_robust(struct blocking_notifier_head *nh,
                unsigned long val_up, unsigned long val_down, void *v) 
{
        int ret = NOTIFY_DONE;

        /*
         * We check the head outside the lock, but if this access is
         * racy then it does not matter what the result of the test
         * is, we re-check the list after having taken the lock anyway:
         */
        if (rcu_access_pointer(nh->head)) {
                down_read(&nh->rwsem);
                ret = notifier_call_chain_robust(&nh->head, val_up, val_down, v); 
                up_read(&nh->rwsem);
        }
        return ret;
}
--------------------------------------------------------------------------------
// power.h

// 1.3 进程冻结
#ifdef CONFIG_SUSPEND_FREEZER
static inline int suspend_freeze_processes(void)
{
        int error;
        // 1.3.1 冻结用户进程,如果冻结失败,则直接返回
        error = freeze_processes();
        /*  
         * freeze_processes() automatically thaws every task if freezing
         * fails. So we need not do anything extra upon error.
         */
        if (error)
                return error;
        // 1.3.2 冻结内核线程,如果冻结失败,则已被冻结的内核线程
        error = freeze_kernel_threads();
        /*
         * freeze_kernel_threads() thaws only kernel threads upon freezing
         * failure. So we have to thaw the userspace tasks ourselves.
         */
        if (error)
                // 如果内核线程冻结失败,则已被冻结的用户进程
                thaw_processes();

        return error;
}

----------------------------------------------------------------------
// process.c

/**
 * freeze_processes - Signal user space processes to enter the refrigerator.
 * The current thread will not be frozen.  The same process that calls
 * freeze_processes must later call thaw_processes.
 *
 * On success, returns 0.  On failure, -errno and system is fully thawed.
 */
 // 1.3.1 冻结用户进程
int freeze_processes(void)
{
        int error;

        error = __usermodehelper_disable(UMH_FREEZING);
        if (error)
                return error;

        /* Make sure this task doesn't get frozen */
        current->flags |= PF_SUSPEND_TASK;

        if (!pm_freezing)
                atomic_inc(&system_freezing_cnt);
                pm_wakeup_clear(0);
        pr_info("%s:Freezing user space processes ... ", STR_KERNEL_LOG_ENTER);
        pm_freezing = true;
        // // 参数传入true,表示冻结用户进程
        error = try_to_freeze_tasks(true);
        if (!error) {
                __usermodehelper_set_disable_depth(UMH_DISABLED);
                pr_cont("done.");
        }
        pr_cont("\n");
        BUG_ON(in_atomic());

        /*
         * Now that the whole userspace is frozen we need to disable
         * the OOM killer to disallow any further interference with
         * killable tasks. There is no guarantee oom victims will
         * ever reach a point they go away we have to wait with a timeout.
         */
         // 用户进程冻结完后, OOM killer做disable处理,防止对任何对可杀死任务的进一步干扰
        if (!error && !oom_killer_disable(msecs_to_jiffies(freeze_timeout_msecs)))
                error = -EBUSY;

        if (error)
                // 如果用户进程冻结失败,则解冻所有已被冻结的用户进程
                thaw_processes();
        return error;
}

// 解冻所有已被冻结的用户进程
void thaw_processes(void)
{
        struct task_struct *g, *p;
        struct task_struct *curr = current;

        trace_suspend_resume(TPS("thaw_processes"), 0, true);
        if (pm_freezing)
                atomic_dec(&system_freezing_cnt);
        pm_freezing = false;
        pm_nosig_freezing = false;
        // kernel oom killer重新使能
        oom_killer_enable();

        pr_info("%s:Restarting tasks ... ", STR_KERNEL_LOG_EXIT);

        __usermodehelper_set_disable_depth(UMH_FREEZING);
        // 解冻worker线程
        thaw_workqueues();

        cpuset_wait_for_hotplug();

        read_lock(&tasklist_lock);
        for_each_process_thread(g, p) {
                /* No other threads should have PF_SUSPEND_TASK set */
                WARN_ON((p != curr) && (p->flags & PF_SUSPEND_TASK));
                __thaw_task(p);  // freezer模块提供的解冻函数入口,根据P判断是用户进程还是内核线程
        }
        read_unlock(&tasklist_lock);

        WARN_ON(!(curr->flags & PF_SUSPEND_TASK));
        curr->flags &= ~PF_SUSPEND_TASK;

        usermodehelper_enable();

        schedule();
        pr_cont("done.\n");
        trace_suspend_resume(TPS("thaw_processes"), 0, false);
}

// 冻结用户进程和内核线程都会走到这个函数;user_only true表示用户进程,false表示内核线程
static int try_to_freeze_tasks(bool user_only)
{
        struct task_struct *g, *p;
        unsigned long end_time;
        unsigned int todo;
        bool wq_busy = false;
        ktime_t start, end, elapsed;
        unsigned int elapsed_msecs;
        bool wakeup = false;
        int sleep_usecs = USEC_PER_MSEC;
        char last_process[TASK_COMM_LEN];
        char last_thread[TASK_COMM_LEN];
        int last_process_pid;
        int last_thread_tid;

        start = ktime_get_boottime();

        end_time = jiffies + msecs_to_jiffies(freeze_timeout_msecs);
        // 如果是内核线程,则先冻结worker线程
        if (!user_only)
                freeze_workqueues_begin();

        while (true) {
                todo = 0;
                read_lock(&tasklist_lock);
                for_each_process_thread(g, p) {
                        if (p == current || !freeze_task(p)) // 调用cgroup freezer的冻结函数
                                continue;

                        if (!freezer_should_skip(p)) {
                                strcpy(last_process, g->comm);
                                last_process_pid = g->pid;
                                strcpy(last_thread, p->comm);
                                last_thread_tid = p->pid;
                                todo++;
                        }
                }
                read_unlock(&tasklist_lock);

                if (!user_only) {
                        wq_busy = freeze_workqueues_busy();
                        todo += wq_busy;
                }

                if (!todo || time_after(jiffies, end_time))
                        break;

                if (pm_wakeup_pending()) {
                        wakeup = true;
                        break;
                }

                /*
                 * We need to retry, but first give the freezing tasks some
                 * time to enter the refrigerator.  Start with an initial
                 * 1 ms sleep followed by exponential backoff until 8 ms.
                 */
                usleep_range(sleep_usecs / 2, sleep_usecs);
                if (sleep_usecs < 8 * USEC_PER_MSEC)
                        sleep_usecs *= 2;
        }

        end = ktime_get_boottime();
        elapsed = ktime_sub(end, start);
        elapsed_msecs = ktime_to_ms(elapsed);

        if (wakeup) {
                pr_cont("\n");
                pr_err("Freezing of tasks aborted after %d.%03d seconds",
                       elapsed_msecs / 1000, elapsed_msecs % 1000);
                pr_err("%s:wakeup true. Process is [%s](pid = %d), thread is [%s](tid = %d)!\n", STR_KERNEL_LOG_ENTER_ERR, last_process, last_process_pid, last_thread, last_thread_tid);
        } else if (todo) {
                pr_cont("\n");
                pr_err("Freezing of tasks failed after %d.%03d seconds"
                       " (%d tasks refusing to freeze, wq_busy=%d):\n",
                       elapsed_msecs / 1000, elapsed_msecs % 1000,
                       todo - wq_busy, wq_busy);

                pr_err("%s:todo true. Process is [%s](pid = %d), thread is [%s](tid = %d)!\n", STR_KERNEL_LOG_ENTER_ERR, last_process, last_process_pid, last_thread, last_thread_tid);

                if (wq_busy)
                        show_workqueue_state();

                if (pm_debug_messages_on) {
                        read_lock(&tasklist_lock);
                        for_each_process_thread(g, p) {
                                if (p != current && !freezer_should_skip(p)
                                    && freezing(p) && !frozen(p)) {
                                        sched_show_task(p);
                                        trace_android_vh_try_to_freeze_todo_unfrozen(p);
                                }
                        }
                        read_unlock(&tasklist_lock);
                }

                trace_android_vh_try_to_freeze_todo(todo, elapsed_msecs, wq_busy);
        } else {
                pr_cont("(elapsed %d.%03d seconds) ", elapsed_msecs / 1000,
                        elapsed_msecs % 1000);
        }

        return todo ? -EBUSY : 0;
}

/**
 * freeze_kernel_threads - Make freezable kernel threads go to the refrigerator.
 *
 * On success, returns 0.  On failure, -errno and only the kernel threads are
 * thawed, so as to give a chance to the caller to do additional cleanups
 * (if any) before thawing the userspace tasks. So, it is the responsibility
 * of the caller to thaw the userspace tasks, when the time is right.
 */
 // 冻结内核线程
int freeze_kernel_threads(void)
{
        int error;

        pr_info("%s:Freezing remaining freezable tasks ... ", STR_KERNEL_LOG_ENTER);

        pm_nosig_freezing = true;
        // 参数传入false,表示冻结内核线程
        error = try_to_freeze_tasks(false);
        if (!error)
                pr_cont("done.");

        pr_cont("\n");
        BUG_ON(in_atomic());

        if (error)
                // 若内核线程冻结失败,则解冻已被冻结的内核线程
                thaw_kernel_threads();
        return error;
}

// 解冻内核线程,包括worker线程和其它的内核线程
void thaw_kernel_threads(void)
{
        struct task_struct *g, *p;

        pm_nosig_freezing = false;
        pr_info("%s:Restarting kernel threads ... ", STR_KERNEL_LOG_EXIT);
        // 解冻worker线程
        thaw_workqueues();

        read_lock(&tasklist_lock);
        for_each_process_thread(g, p) {
                // 对struct p->flags赋值
                if (p->flags & (PF_KTHREAD | PF_WQ_WORKER))
                        // 解冻其它的内核线程,根据P判断是内核线程还是用户进程
                        __thaw_task(p);
        }
        read_unlock(&tasklist_lock);

        schedule();
        pr_cont("done.\n");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321

Linux kernel cgroup freezer模块具体的冻结与解冻策略,另起一篇文章讲述。
小结:

  1. suspend第一阶段主要做了三个事情
    a. 切换控制台,使用suspend下的控制台,重定向到kmsg
    b.suspend前调用阻塞通知链,通知PM_SUSPEND_PREPARE事件
    c.冻结用户进程和内核worker线程及其它线程
  2. freezer模块的入口函数
    冻结入口函数:freeze_task(struct task_struct *p)
    解冻入口函数:__thaw_task(struct task_struct *p)
    2.3.4.2 kernel suspend流程的第二阶段 —suspend_devices_and_enter
// suspend.c

/**
 * suspend_devices_and_enter - Suspend devices and enter system sleep state.
 * @state: System sleep state to enter.
 */
int suspend_devices_and_enter(suspend_state_t state)
{
        int error;
        bool wakeup = false;
        // 如果当前系统不支持该类型的suspend模式,则return
        if (!sleep_state_supported(state))
                return -ENOSYS;

        pm_suspend_target_state = state;
        // 若为suspend idle模式
        if (state == PM_SUSPEND_TO_IDLE)
                pm_set_suspend_no_platform();
        // 1. platform suspend
        error = platform_suspend_begin(state);
        if (error)
                goto Close;
        // 2. console suspend
        suspend_console();
        suspend_test_start();
        // 3. dpm suspend
        error = dpm_suspend_start(PMSG_SUSPEND);
        if (error) {
                pr_err("Some devices failed to suspend, or early wake event detected\n");
                log_suspend_abort_reason(
                                "Some devices failed to suspend, or early wake event detected");
                goto Recover_platform;
        }
        suspend_test_finish("suspend devices");
        if (suspend_test(TEST_DEVICES))
                goto Recover_platform;

        do {
                // 4. system suspend
                error = suspend_enter(state, &wakeup);
        } while (!error && !wakeup && platform_suspend_again(state));

 Resume_devices:

        pr_info("%s:Resume_devices start.\n", STR_KERNEL_LOG_EXIT);

        suspend_test_start();
        dpm_resume_end(PMSG_RESUME);
        suspend_test_finish("resume devices");
        trace_suspend_resume(TPS("resume_console"), state, true);
        resume_console();
        trace_suspend_resume(TPS("resume_console"), state, false);

        pr_info("%s:Resume_devices end.\n", STR_KERNEL_LOG_EXIT);

 Close:
        platform_resume_end(state);
        pm_suspend_target_state = PM_SUSPEND_ON;
        return error;

 Recover_platform:
        platform_recover(state);
        goto Resume_devices;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

1)platform suspend —platform_suspend_begin

// 1. platform suspend
// suspend.c

static int platform_suspend_begin(suspend_state_t state)
{
        if (state == PM_SUSPEND_TO_IDLE && s2idle_ops && s2idle_ops->begin)
                return s2idle_ops->begin();
        else if (suspend_ops && suspend_ops->begin)
                return suspend_ops->begin(state);
        else
                return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

2)console suspend —suspend_console

// 2. console suspend
// printk.c
/**
 * suspend_console - suspend the console subsystem
 *
 * This disables printk() while we go into suspend states
 */
void suspend_console(void)
{
        if (!console_suspend_enabled)
                return;
        pr_info("Suspending console(s) (use no_console_suspend to debug)\n");
        console_lock();
        console_suspended = 1; 
        up_console_sem();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

3)dpm suspend —dpm_suspend_start

// 3. dpm suspend
// kernel/drivers/base/power/main.c
/**
 * dpm_suspend_start - Prepare devices for PM transition and suspend them.
 * @state: PM transition of the system being carried out.
 *
 * Prepare all non-sysdev devices for system PM transition and execute "suspend"
 * callbacks for them.
 */
 // 3.系统进入睡眠状态之前对非sysdev设备进行准备并将其挂起
int dpm_suspend_start(pm_message_t state)
{
        ktime_t starttime = ktime_get();
        int error;

        error = dpm_prepare(state);
        if (error) {
                suspend_stats.failed_prepare++;
                dpm_save_failed_step(SUSPEND_PREPARE);
        } else 
                // 非sysdev设备进入suspend
                error = dpm_suspend(state);
        dpm_show_time(starttime, state, error, "start");
        return error;
}
EXPORT_SYMBOL_GPL(dpm_suspend_start);

/**
 * dpm_suspend - Execute "suspend" callbacks for all non-sysdev devices.
 * @state: PM transition of the system being carried out.
 */
int dpm_suspend(pm_message_t state)
{
        ktime_t starttime = ktime_get();
        int error = 0;

        trace_suspend_resume(TPS("dpm_suspend"), state.event, true);
        might_sleep();
        // devfreq & cpufreq suspend
        // 暂停设备的频率调节
        devfreq_suspend();
        cpufreq_suspend();

        mutex_lock(&dpm_list_mtx);
        pm_transition = state;
        async_error = 0;
        while (!list_empty(&dpm_prepared_list)) {
                struct device *dev = to_device(dpm_prepared_list.prev);

                get_device(dev);
                mutex_unlock(&dpm_list_mtx);
                // device进入suspend
                error = device_suspend(dev);

                mutex_lock(&dpm_list_mtx);
                if (error) {
                        pm_dev_err(dev, state, "", error);
                        dpm_save_failed_dev(dev_name(dev));
                        put_device(dev);
                        break;
                }
                if (!list_empty(&dev->power.entry))
                        list_move(&dev->power.entry, &dpm_suspended_list);
                put_device(dev);
                if (async_error)
                        break;
        }
        mutex_unlock(&dpm_list_mtx);
        async_synchronize_full();
        if (!error)
                error = async_error;
        if (error) {
                suspend_stats.failed_suspend++;
                dpm_save_failed_step(SUSPEND_SUSPEND);
        }
        dpm_show_time(starttime, state, error, NULL);
        trace_suspend_resume(TPS("dpm_suspend"), state.event, false);
        return error;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79

4)system suspend —suspend_enter

// 4. system suspend
// suspend.c

/**
 * suspend_enter - Make the system enter the given sleep state.
 * @state: System sleep state to enter.
 * @wakeup: Returns information that the sleep state should not be re-entered.
 *
 * This function should be called after devices have been suspended.
 */
 // 4. 系统进入休眠状态
static int suspend_enter(suspend_state_t state, bool *wakeup)
{
        int error, last_dev;
        // 1) 调用平台的prepare回调函数
        error = platform_suspend_prepare(state);
        if (error)
                goto Platform_finish;
        // 2) 执行所有设备的suspend late回调函数
        error = dpm_suspend_late(PMSG_SUSPEND);
        if (error) {
                last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1;
                last_dev %= REC_FAILED_NUM;
                pr_err("late suspend of devices failed\n");
                log_suspend_abort_reason("late suspend of %s device failed",
                                         suspend_stats.failed_devs[last_dev]);
                goto Platform_finish;
        }
        // 3) 执行s2idle_ops的prepare回调函数
        error = platform_suspend_prepare_late(state);
        if (error)
                goto Devices_early_resume;
        // 4) 执行所有设备的noirq回调,暂停cpuidle,关闭中断
        error = dpm_suspend_noirq(PMSG_SUSPEND);
        if (error) {
                last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1;
                last_dev %= REC_FAILED_NUM;
                pr_err("noirq suspend of devices failed\n");
                log_suspend_abort_reason("noirq suspend of %s device failed",
                                         suspend_stats.failed_devs[last_dev]);
                goto Platform_early_resume;
        }
        // 5) 执行prepare_late回调函数
        error = platform_suspend_prepare_noirq(state);
        if (error)
                goto Platform_wake;

        if (suspend_test(TEST_PLATFORM))
                goto Platform_wake;

        if (state == PM_SUSPEND_TO_IDLE) {
                s2idle_loop();
                goto Platform_wake;
        }
        // 6) 关闭non-boot cpu
        // "Boot" CPU是系统启动时作为第一个启动的CPU核心,
        // 它负责执行引导加载程序(bootloader)的任务,以及进行系统初始化的工作
        error = suspend_disable_secondary_cpus();
        if (error || suspend_test(TEST_CPUS)) {
                log_suspend_abort_reason("Disabling non-boot cpus failed");
                goto Enable_cpus;
        }
        // 7) 关闭本地中断
        arch_suspend_disable_irqs();
        BUG_ON(!irqs_disabled());

        system_state = SYSTEM_SUSPEND;
        // 8) 调用syscoe suspend回调函数
        error = syscore_suspend();

        pr_info("%s:<< end >>, kernel suspend end.\n", STR_KERNEL_LOG_ENTER);

        if (!error) {
                // 9) 检查系统中是否有wakelock,若存在,则停止suspend
                *wakeup = pm_wakeup_pending();
                if (!(suspend_test(TEST_CORE) || *wakeup)) {
                        trace_suspend_resume(TPS("machine_suspend"),
                                state, true);
                        // 10) 真正发起系统休眠,有可能通过SMC陷入EL3,经由ATF再通过SCMI告诉其它MCU做系统休眠
                        error = suspend_ops->enter(state);
                        trace_suspend_resume(TPS("machine_suspend"),
                                state, false);
                } else if (*wakeup) {
                        error = -EBUSY;
                }

                pr_info("%s:(( start )), kernel resume start.\n", STR_KERNEL_LOG_EXIT);

                syscore_resume();
        }

        system_state = SYSTEM_RUNNING;

        arch_suspend_enable_irqs();
        BUG_ON(irqs_disabled());

        pr_info("%s:Enable_cpus start.\n", STR_KERNEL_LOG_EXIT);

 Enable_cpus:
        suspend_enable_secondary_cpus();

        pr_info("%s:Platform_wake start.\n", STR_KERNEL_LOG_EXIT);

 Platform_wake:
        platform_resume_noirq(state);
        dpm_resume_noirq(PMSG_RESUME);

        pr_info("%s:Platform_early_resume start.\n", STR_KERNEL_LOG_EXIT);

 Platform_early_resume:
        platform_resume_early(state);

        pr_info("%s:Devices_early_resume start.\n", STR_KERNEL_LOG_EXIT);

 Devices_early_resume:
        dpm_resume_early(PMSG_RESUME);

        pr_info("%s:Platform_finish start.\n", STR_KERNEL_LOG_EXIT);

 Platform_finish:
        platform_resume_finish(state);
        return error;
}       
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123

接下来看下pm_wakeup_pending(void)函数,判断系统中是否存在wakeup source。存在,则susepnd失败。
wakeup source在wakelock管理模块中被申请或创建。参考:https://blog.csdn.net/youthcowboy/article/details/134312903?spm=1001.2014.3001.5502 中wakeup_source_register(struct device *dev, const char *name)函数。

// kernel/drivers/base/power/wakeup.c
bool pm_wakeup_pending(void)
{
        unsigned long flags;
        bool ret = false;
        char suspend_abort[MAX_SUSPEND_ABORT_LEN];

        raw_spin_lock_irqsave(&events_lock, flags);
        if (events_check_enabled) {
                unsigned int cnt, inpr;

                split_counters(&cnt, &inpr); 
                ret = (cnt != saved_count || inpr > 0);
                events_check_enabled = !ret;
        }
        raw_spin_unlock_irqrestore(&events_lock, flags);

        if (ret) {
                pm_pr_dbg("Wakeup pending, aborting suspend\n");
                pm_print_active_wakeup_sources();
                pm_get_active_wakeup_sources(suspend_abort,
                                             MAX_SUSPEND_ABORT_LEN);
                log_suspend_abort_reason(suspend_abort);
                pr_info("PM: %s\n", suspend_abort);
        }

        return ret || atomic_read(&pm_abort_suspend) > 0;
}

void pm_get_active_wakeup_sources(char *pending_wakeup_source, size_t max) 
{
        struct wakeup_source *ws, *last_active_ws = NULL;
        int len = 0; 
        bool active = false;

        rcu_read_lock();
        // 遍历wakeup_sources list,判断系统中是否存在wakeup source。存在,则susepnd失败。
        // wakeup source在wakelock管理模块中被申请或创建
        list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
                if (ws->active && len < max) {
                        if (!active)
                                len += scnprintf(pending_wakeup_source, max, 
                                                "Pending Wakeup Sources: ");
                        len += scnprintf(pending_wakeup_source + len, max - len, 
                                "%s ", ws->name);
                        active = true;
                } else if (!active &&
                           (!last_active_ws ||
                            ktime_to_ns(ws->last_time) >
                            ktime_to_ns(last_active_ws->last_time))) {
                        last_active_ws = ws;
                }
        }
        if (!active && last_active_ws) {
                scnprintf(pending_wakeup_source, max, 
                                "Last active Wakeup Source: %s",
                                last_active_ws->name);
        }
        rcu_read_unlock();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
// 1) 调用平台的prepare回调函数
// suspend.c
static int platform_suspend_prepare(suspend_state_t state)
{
    return state != PM_SUSPEND_TO_IDLE && suspend_ops->prepare ?
            suspend_ops->prepare() : 0;
}

// 2) 执行所有设备的suspend late回调函数
// kernel/drivers/base/power/main.c
/**
 * dpm_suspend_late - Execute "late suspend" callbacks for all devices.
 * @state: PM transition of the system being carried out.
 */
int dpm_suspend_late(pm_message_t state)
{
        ktime_t starttime = ktime_get();
        int error = 0;

        trace_suspend_resume(TPS("dpm_suspend_late"), state.event, true);
        mutex_lock(&dpm_list_mtx);
        pm_transition = state;
        async_error = 0;
        // 遍历 apm suspend list
        while (!list_empty(&dpm_suspended_list)) {
                struct device *dev = to_device(dpm_suspended_list.prev);

                get_device(dev);
                mutex_unlock(&dpm_list_mtx);
                // device进入suspend
                error = device_suspend_late(dev);

                mutex_lock(&dpm_list_mtx);
                if (!list_empty(&dev->power.entry))
                        list_move(&dev->power.entry, &dpm_late_early_list);

                if (error) {
                        pm_dev_err(dev, state, " late", error);
                        dpm_save_failed_dev(dev_name(dev));
                        put_device(dev);
                        break;
                }
                put_device(dev);

                if (async_error)
                        break;
        }
        mutex_unlock(&dpm_list_mtx);
        async_synchronize_full();
        if (!error)
                error = async_error;
        if (error) {
                suspend_stats.failed_suspend_late++;
                dpm_save_failed_step(SUSPEND_SUSPEND_LATE);
                dpm_resume_early(resume_event(state));
        }
        dpm_show_time(starttime, state, error, "late");
        trace_suspend_resume(TPS("dpm_suspend_late"), state.event, false);
        return error;
}

/**
 * __device_suspend_late - Execute a "late suspend" callback for given device.
 * @dev: Device to handle.
 * @state: PM transition of the system being carried out.
 * @async: If true, the device is being suspended asynchronously.
 *
 * Runtime PM is disabled for @dev while this function is being executed.
 */
 // 该函数会让设备进入挂起状态。在执行挂起操作之前,
 // 该函数会禁用设备的运行时电源管理,并等待设备的子设备完成挂起操作。
 // 然后,该函数通过执行各种挂起回调函数来完成设备的挂起操作。
 // 最后,该函数将设置设备的is_late_suspended标志为true,表示设备已经完成晚期挂起操作。
 // 这意味着在该函数执行完毕后,设备应该处于挂起状态。
static int __device_suspend_late(struct device *dev, pm_message_t state, bool async)
{
        pm_callback_t callback = NULL;
        const char *info = NULL;
        int error = 0;

        TRACE_DEVICE(dev);
        TRACE_SUSPEND(0);
        // runtme PM disable
        __pm_runtime_disable(dev, false);
        // 等待设备的子设备完成suspend操作
        dpm_wait_for_subordinate(dev, async);

        if (async_error)
                goto Complete;

        if (pm_wakeup_pending()) {
                async_error = -EBUSY;
                goto Complete;
        }

        if (dev->power.syscore || dev->power.direct_complete)
                goto Complete;
        // 检查设备的电源域、设备类型、设备类别和设备总线是否定义了suspend回调函数。
        // 如果存在相应的回调函数,则执行suspend回调函数。
        if (dev->pm_domain) {
                info = "late power domain ";
                callback = pm_late_early_op(&dev->pm_domain->ops, state);
        } else if (dev->type && dev->type->pm) {
                info = "late type ";
                callback = pm_late_early_op(dev->type->pm, state);
        } else if (dev->class && dev->class->pm) {
                info = "late class ";
                callback = pm_late_early_op(dev->class->pm, state);
        } else if (dev->bus && dev->bus->pm) {
                info = "late bus ";
                callback = pm_late_early_op(dev->bus->pm, state);
        }
        if (callback)
                goto Run;

        if (dev_pm_skip_suspend(dev))
                goto Skip;
        // 如果设备的驱动程序定义了supend回调函数,也执行该回调函数
        if (dev->driver && dev->driver->pm) {
                info = "late driver ";
                callback = pm_late_early_op(dev->driver->pm, state);
        }

Run:
        error = dpm_run_callback(callback, dev, state, info);
        if (error) {
                async_error = error;
                log_suspend_abort_reason("Device %s failed to %s late: error %d",
                                         dev_name(dev), pm_verb(state.event), error);
                goto Complete;
        }
        dpm_propagate_wakeup_to_parent(dev);

Skip:
        dev->power.is_late_suspended = true;

Complete:
        TRACE_SUSPEND(error);
        complete_all(&dev->power.completion);
        return error;
}

// 3) 执行s2idle_ops的prepare回调函数
static int platform_suspend_prepare_late(suspend_state_t state)
{
        return state == PM_SUSPEND_TO_IDLE && s2idle_ops && s2idle_ops->prepare ?
                s2idle_ops->prepare() : 0;
}

// 4) 执行所有设备的noirq回调,暂停cpuidle,关闭设备中断
/**
 * dpm_suspend_noirq - Execute "noirq suspend" callbacks for all devices.
 * @state: PM transition of the system being carried out.
 *
 * Prevent device drivers' interrupt handlers from being called and invoke
 * "noirq" suspend callbacks for all non-sysdev devices.
 */
int dpm_suspend_noirq(pm_message_t state)
{
        int ret; 
        // 暂定空闲的cpu
        cpuidle_pause();
        // 激活系统所有的唤醒中断,以确保在系统进入挂起状态后能够正常被唤醒
        device_wakeup_arm_wake_irqs();
        // supend中断程序
        suspend_device_irqs();
        // 执行设备进入suspend状态的操作
        ret = dpm_noirq_suspend_devices(state);
        if (ret)
                dpm_resume_noirq(resume_event(state));

        return ret; 
}

// 5) 执行prepare_late回调函数
static int platform_suspend_prepare_noirq(suspend_state_t state)
{
        if (state == PM_SUSPEND_TO_IDLE)
                return s2idle_ops && s2idle_ops->prepare_late ?
                        s2idle_ops->prepare_late() : 0;

        return suspend_ops->prepare_late ? suspend_ops->prepare_late() : 0;
}

// kernel/drivers/acpi/sleep.c
static const struct platform_s2idle_ops acpi_s2idle_ops = {
        .begin = acpi_s2idle_begin,
        .prepare = acpi_s2idle_prepare,
        .prepare_late = acpi_s2idle_prepare_late,
        .wake = acpi_s2idle_wake,
        .restore_early = acpi_s2idle_restore_early,
        .restore = acpi_s2idle_restore,
        .end = acpi_s2idle_end,
};

static int acpi_s2idle_prepare_late(void)
{
        if (!lps0_device_handle || sleep_no_lps0)
                return 0;

        if (pm_debug_messages_on)
                lpi_check_constraints();

        acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_OFF);
        acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY);

        return 0;
}

// 6) 关闭non-boot cpu
// boot cpu ---加载bootloader程序的cpu(cpu 0),其它核cpu均为non-boot
// kernel/include/linux/cpu.h
static inline int suspend_disable_secondary_cpus(void)
{
        int cpu = 0;

        if (IS_ENABLED(CONFIG_PM_SLEEP_SMP_NONZERO_CPU))
                cpu = -1; 

        return freeze_secondary_cpus(cpu);
}

// kernel/kernel/cpu.c
int freeze_secondary_cpus(int primary)  // primary为0,表示cpu 0
{
        int cpu, error = 0;

        cpu_maps_update_begin();
        if (primary == -1) {
                primary = cpumask_first(cpu_online_mask);
                if (!housekeeping_cpu(primary, HK_FLAG_TIMER))
                        primary = housekeeping_any_cpu(HK_FLAG_TIMER);
        } else {
                if (!cpu_online(primary))
                        primary = cpumask_first(cpu_online_mask);
        }

        /*
         * We take down all of the non-boot CPUs in one shot to avoid races
         * with the userspace trying to use the CPU hotplug at the same time
         */
        cpumask_clear(frozen_cpus);

        pr_info("Disabling non-boot CPUs ...\n");
        for_each_online_cpu(cpu) {   // 遍历所有cpu核
                if (cpu == primary)
                        continue;    // 若为cpu0,则continue

                if (pm_wakeup_pending()) {    // 默认为false
                        pr_info("Wakeup pending. Abort CPU freeze\n");
                        error = -EBUSY;
                        break;
                }

                trace_suspend_resume(TPS("CPU_OFF"), cpu, true);
                // non-boot cpu suspend
                error = _cpu_down(cpu, 1, CPUHP_OFFLINE);
                trace_suspend_resume(TPS("CPU_OFF"), cpu, false);
                if (!error)
                        cpumask_set_cpu(cpu, frozen_cpus);
                else {
                        pr_err("Error taking CPU%d down: %d\n", cpu, error);
                        break;
                }
        }

        if (!error)
                BUG_ON(num_online_cpus() > 1);
        else
                pr_err("Non-boot CPUs are not disabled\n");

        /*
         * Make sure the CPUs won't be enabled by someone else. We need to do
         * this even in case of failure as all freeze_secondary_cpus() users are
         * supposed to do thaw_secondary_cpus() on the failure path.
         */
        cpu_hotplug_disabled++;

        cpu_maps_update_done();
        return error;
}

// 7) 关闭本地中断
/* default implementation */
void __weak arch_suspend_disable_irqs(void)
{
        local_irq_disable();
}

// kernel/arch/arm64/include/asm/irqflags.h
static inline void arch_local_irq_disable(void)
{
        if (system_has_prio_mask_debugging()) {
                u32 pmr = read_sysreg_s(SYS_ICC_PMR_EL1);

                WARN_ON_ONCE(pmr != GIC_PRIO_IRQON && pmr != GIC_PRIO_IRQOFF);
        }

        asm volatile(ALTERNATIVE(
                "msr    daifset, #2             // arch_local_irq_disable",
                __msr_s(SYS_ICC_PMR_EL1, "%0"),
                ARM64_HAS_IRQ_PRIO_MASKING)
                :
                : "r" ((unsigned long) GIC_PRIO_IRQOFF)
                : "memory");
}

// 8) 调用syscoe suspend回调函数
 #ifdef CONFIG_PM_SLEEP
/**
 * syscore_suspend - Execute all the registered system core suspend callbacks.
 *
 * This function is executed with one CPU on-line and disabled interrupts.
 */
 // 执行所有的registere到system core的suspend callbacks回调
int syscore_suspend(void)
{
        struct syscore_ops *ops;
        int ret = 0;

        trace_suspend_resume(TPS("syscore_suspend"), 0, true);
        pm_pr_dbg("Checking wakeup interrupts\n");

        /* Return error code if there are any wakeup interrupts pending. */
        if (pm_wakeup_pending())
                return -EBUSY;

        WARN_ONCE(!irqs_disabled(),
                "Interrupts enabled before system core suspend.\n");

        list_for_each_entry_reverse(ops, &syscore_ops_list, node)
                if (ops->suspend) {
                        pm_pr_dbg("Calling %pS\n", ops->suspend);
                        ret = ops->suspend();
                        if (ret)
                                goto err_out;
                        WARN_ONCE(!irqs_disabled(),
                                "Interrupts enabled after %pS\n", ops->suspend);
                }

        trace_suspend_resume(TPS("syscore_suspend"), 0, false);
        return 0;

 err_out:
        log_suspend_abort_reason("System core suspend callback %pS failed",
                ops->suspend);
        pr_err("PM: System core suspend callback %pF failed.\n", ops->suspend);

        list_for_each_entry_continue(ops, &syscore_ops_list, node)
                if (ops->resume)
                        ops->resume();

        return ret;
}
EXPORT_SYMBOL_GPL(syscore_suspend);

// 9) 检查系统中是否有wakelock,若存在,则停止suspend
 pm_wakeup_pending()    ---默认return false

// 10) 真正发起系统休眠,有可能通过SMC陷入EL3,经由ATF再通过SCMI告诉其它MCU做系统休眠
suspend_ops->enter(state);

// kernel/drivers/acpi/sleep.c
#ifdef CONFIG_SUSPEND
static u32 acpi_suspend_states[] = {
        [PM_SUSPEND_ON] = ACPI_STATE_S0,
        [PM_SUSPEND_STANDBY] = ACPI_STATE_S1,
        [PM_SUSPEND_MEM] = ACPI_STATE_S3,
        [PM_SUSPEND_MAX] = ACPI_STATE_S5
};

static const struct platform_suspend_ops acpi_suspend_ops = {
        .valid = acpi_suspend_state_valid,
        .begin = acpi_suspend_begin,
        .prepare_late = acpi_pm_prepare,
        .enter = acpi_suspend_enter,
        .wake = acpi_pm_finish,
        .end = acpi_pm_end,
};

/**
 *        acpi_suspend_enter - Actually enter a sleep state.
 *        @pm_state: ignored
 *
 *        Flush caches and go to sleep. For STR we have to call arch-specific
 *        assembly, which in turn call acpi_enter_sleep_state().
 *        It's unfortunate, but it works. Please fix if you're feeling frisky.
 */
 // 默认进入ACPI_STATE_S3(suspend to ram),执行acpi_suspend_lowlevel()
 // ACPI是“高级配置与电源管理接口”,它是计算机系统中用于配置硬件和管理电源的开放标准
static int acpi_suspend_enter(suspend_state_t pm_state)
{
        acpi_status status = AE_OK;
         // 状态值在之前的platform_suspend_begin(suspend_state_t state)传进来
        u32 acpi_state = acpi_target_sleep_state;   
        int error;
        // 刷新CPU缓存,确保数据已经写回内存
        ACPI_FLUSH_CPU_CACHE();

        trace_suspend_resume(TPS("acpi_suspend"), acpi_state, true);
        switch (acpi_state) {
        case ACPI_STATE_S1: // suspend to standby
                barrier();
                status = acpi_enter_sleep_state(acpi_state);
                break;

        case ACPI_STATE_S3:    // suspend to ram
                if (!acpi_suspend_lowlevel)
                        return -ENOSYS;
                error = acpi_suspend_lowlevel();
                if (error)
                        return error;
                pr_info(PREFIX "Low-level resume complete\n");
                pm_set_resume_via_firmware();
                break;
        }
        trace_suspend_resume(TPS("acpi_suspend"), acpi_state, false);

        /* This violates the spec but is required for bug compatibility. */
        acpi_write_bit_register(ACPI_BITREG_SCI_ENABLE, 1);

        /* Reprogram control registers */
        acpi_leave_sleep_state_prep(acpi_state);

        /* ACPI 3.0 specs (P62) says that it's the responsibility
         * of the OSPM to clear the status bit [ implying that the
         * POWER_BUTTON event should not reach userspace ]
         *
         * However, we do generate a small hint for userspace in the form of
         * a wakeup event. We flag this condition for now and generate the
         * event later, as we're currently too early in resume to be able to
         * generate wakeup events.
         */
        if (ACPI_SUCCESS(status) && (acpi_state == ACPI_STATE_S3)) {
                acpi_event_status pwr_btn_status = ACPI_EVENT_FLAG_DISABLED;

                acpi_get_event_status(ACPI_EVENT_POWER_BUTTON, &pwr_btn_status);

                if (pwr_btn_status & ACPI_EVENT_FLAG_STATUS_SET) {
                        acpi_clear_event(ACPI_EVENT_POWER_BUTTON);
                        /* Flag for later */
                        pwr_btn_event_pending = true;
                }
        }

        /*
         * Disable and clear GPE status before interrupt is enabled. Some GPEs
         * (like wakeup GPE) haven't handler, this can avoid such GPE misfire.
         * acpi_leave_sleep_state will reenable specific GPEs later
         */
        acpi_disable_all_gpes();
        /* Allow EC transactions to happen. */
        acpi_ec_unblock_transactions();

        suspend_nvs_restore();

        return ACPI_SUCCESS(status) ? 0 : -EFAULT;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430
  • 431
  • 432
  • 433
  • 434
  • 435
  • 436
  • 437
  • 438
  • 439
  • 440
  • 441
  • 442
  • 443
  • 444
  • 445
  • 446
  • 447
  • 448
  • 449
  • 450
  • 451
  • 452
  • 453
  • 454
  • 455
  • 456
  • 457
  • 458

小结:
1)suspend第二阶段做了以下4个步骤
a.platform suspend
b.console suspend,控制台进行suspend
c.dpm suspend,系统进入睡眠状态之前对非sysdev设备进行准备并将其挂起
d.system suspend,系统进入suspend
调用平台的prepare回调函数;
执行所有设备的suspend late回调函数;
执行s2idle_ops的prepare回调函数;
行所有设备的noirq回调,暂停cpuidle,关闭中断;
关闭non-boot cpu;
关闭本地中断;
调用syscoe suspend回调函数;
检查系统中是否有wakelock,若存在,则停止suspend;
真正发起系统休眠,有可能通过SMC陷入EL3,经由ATF再通过SCMI告诉其它MCU做系统休眠;
2.4 系统进入休眠的场景
按键、灭屏等
2.5 系统休眠与睡眠的区别
suspend常见的几种状态模式:idle、standby、mem、disk
休眠模式(Suspend):当系统进入休眠状态时,它会暂停所有进程的运行,并将系统状态保存到内存中(suspend to ram)。这包括当前的工作以及系统的各种设置和状态。在休眠模式下,计算机会关闭显示器和硬盘,并且减少对其他硬件设备的供电,以节省电量。当用户再次唤醒计算机时,系统会恢复之前的状态,并继续进行未完成的任务。
睡眠模式(Hibernate):与休眠模式相比,睡眠模式更加节能。当系统进入睡眠状态时,它会将当前的工作和系统状态保存到硬盘上(suspend to disk)的一个特定文件中,然后关闭计算机。在睡眠模式下,计算机几乎不消耗电量,因为所有硬件设备都被关闭。当用户再次启动计算机时,系统会从保存的文件中恢复,并继续之前的工作。(可理解为深度休眠模式)
2.6 system hibernate流程
Suspend to disk模式,一般都称作系统睡眠。
在state_store函数中,休眠模式执行pm_suspend函数,睡眠模式执行hibernate函数。

// mian.c

// state_store是一个syscall调用函数,为进入suspend的入口函数,
// 接收user space传递的"mem",准备进入suspend to ram模式
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
                           const char *buf, size_t n)
{
        suspend_state_t state;
        int error;
        
        pr_info("%s:Userspace to kernel, process start write command \"echo mem > /sys/power/state\". The process is \"%s\"(pid %i). The thread is \"%s\"(tid %i).\n",
                STR_KERNEL_LOG_ENTER, current->group_leader->comm, current->group_leader->pid, current->comm, current->pid);
        
        error = pm_autosleep_lock();
        if (error)
                return error;
        
        if (pm_autosleep_state() > PM_SUSPEND_ON) {
                error = -EBUSY;
                goto out;
        }
        // "mem"字符串封装到suspend_state_t数据结构
        state = decode_state(buf, n);
        // 检查user space传递的suspend模式是否符合规定的范围
        if (state < PM_SUSPEND_MAX) {
            if (state == PM_SUSPEND_MEM)
                    state = mem_sleep_current;
            // 调用suspend.c函数,进入suspend阶段
            // 返回值为0表示suspend successed,否则supend failed,常用于suspend debug调试
            error = pm_suspend(state);
            if (!error) {
                    pr_info("%s:(( end )), kernel resume end.\n", STR_KERNEL_LOG_EXIT);
        } else if (state == PM_SUSPEND_MAX) {
        // PM_SUSPEND_MAX为suspend to disk,进入hibernate睡眠模式,走另一条流程
        error = hibernate();
        } else {
                error = -EINVAL;
        }

 out:
        pm_autosleep_unlock();
        return error ? error : n;
}

// hibernate.c
/**
 * hibernate - Carry out system hibernation, including saving the image.
 */
int hibernate(void)
{
        bool snapshot_test = false;
        int error;

        if (!hibernation_available()) {
                pm_pr_dbg("Hibernation not available.\n");
                return -EPERM;
        }

        lock_system_sleep();
        /* The snapshot device should not be opened while we're running */
        if (!hibernate_acquire()) {
                error = -EBUSY;
                goto Unlock;
        }

        pr_info("hibernation entry\n");
        pm_prepare_console();
        error = pm_notifier_call_chain_robust(PM_HIBERNATION_PREPARE, PM_POST_HIBERNATION);
        if (error)
                goto Restore;

        ksys_sync_helper();

        error = freeze_processes();
        if (error)
                goto Exit;

        lock_device_hotplug();
        /* Allocate memory management structures */
        error = create_basic_memory_bitmaps();
        if (error)
                goto Thaw;

        error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM);
        if (error || freezer_test_done)
                goto Free_bitmaps;

        if (in_suspend) {
                unsigned int flags = 0;

                if (hibernation_mode == HIBERNATION_PLATFORM)
                        flags |= SF_PLATFORM_MODE;
                if (nocompress)
                        flags |= SF_NOCOMPRESS_MODE;
                else
                        flags |= SF_CRC32_MODE;

                pm_pr_dbg("Writing hibernation image.\n");
                error = swsusp_write(flags);
                swsusp_free();
                if (!error) {
                        if (hibernation_mode == HIBERNATION_TEST_RESUME)
                                snapshot_test = true;
                        else
                                power_down();
                }
                in_suspend = 0;
                pm_restore_gfp_mask();
        } else {
                pm_pr_dbg("Hibernation image restored successfully.\n");
        }

 Free_bitmaps:
        free_basic_memory_bitmaps();
 Thaw:
        unlock_device_hotplug();
        if (snapshot_test) {
                pm_pr_dbg("Checking hibernation image\n");
                error = swsusp_check();
                if (!error)
                        error = load_image_and_restore();
        }
        thaw_processes();

        /* Don't bother checking whether freezer_test_done is true */
        freezer_test_done = false;
 Exit:
        pm_notifier_call_chain(PM_POST_HIBERNATION);
 Restore:
        pm_restore_console();
        hibernate_release();
 Unlock:
        unlock_system_sleep();
        pr_info("hibernation exit\n");

        return error;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138

三、系统唤醒机制
3.1 system resume核心代码逻辑
按照1.3流程框架中resume部分进行模块化介绍.
3.1.1 platform dependent suspend
这部分代码由各平台厂商实现
3.1.2 syscore resume
执行所有的resume register callback函数,并在当前cpu上执行,且关闭当前cpu的中断。

// kernel/drivers/base/syscore.c

/**
 * syscore_resume - Execute all the registered system core resume callbacks.
 *
 * This function is executed with one CPU on-line and disabled interrupts.
 */
void syscore_resume(void)
{
        struct syscore_ops *ops;

        trace_suspend_resume(TPS("syscore_resume"), 0, true);
        WARN_ONCE(!irqs_disabled(),
                "Interrupts enabled before system core resume.\n");
        // 遍历注册在syscore resume中所有的call list
        list_for_each_entry(ops, &syscore_ops_list, node)
                if (ops->resume) {
                        if (initcall_debug)
                                pr_info("PM: Calling %pS\n", ops->resume);
                        // 调用各自的resume函数
                        ops->resume();
                        // 关闭当前cpu上的本地中断
                        WARN_ONCE(!irqs_disabled(),
                                "Interrupts enabled after %pS\n", ops->resume);
                }
        trace_suspend_resume(TPS("syscore_resume"), 0, false);
}
EXPORT_SYMBOL_GPL(syscore_resume);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

3.1.3 irqs PM

// arch/arm64/include/asm/irqflags.h
static inline void arch_local_irq_enable(void)
{
    if (system_has_prio_mask_debugging()) {
            u32 pmr = read_sysreg_s(SYS_ICC_PMR_EL1);

            WARN_ON_ONCE(pmr != GIC_PRIO_IRQON && pmr != GIC_PRIO_IRQOFF);
    }
    // 使用了ARM64的特定指令(msr daifclr, #3)来清除中断寄存器寄存器的IRQ位,从而启用中断
    // 使能(Enabled)状态:IRQ位被设置为"0"; 禁止(Disabled)状态:IRQ位被设置为"1"
    asm volatile(ALTERNATIVE(
            "msr        daifclr, #3  // #3表示要清除中断寄存器中的"I"位和"F"位,即IRQ和FIQ位设置为0
            __msr_s(SYS_ICC_PMR_EL1, "%0"),
            ARM64_HAS_IRQ_PRIO_MASKING)
            :
            : "r" ((unsigned long) GIC_PRIO_IRQON)
            : "memory");

    pmr_sync();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

3.1.4 cpu PM
打开non-boot cpu.

// kernel/include/linux/cpu.h
static inline void suspend_enable_secondary_cpus(void)
{
        return thaw_secondary_cpus();
}

// kernel/cpu.c
void thaw_secondary_cpus(void)
{
        int cpu, error;

        /* Allow everyone to use the CPU hotplug again */
        cpu_maps_update_begin();
        __cpu_hotplug_enable();
        if (cpumask_empty(frozen_cpus))
                goto out;

        pr_info("Enabling non-boot CPUs ...\n");

        arch_thaw_secondary_cpus_begin();

        for_each_cpu(cpu, frozen_cpus) {
                trace_suspend_resume(TPS("CPU_ON"), cpu, true);
                error = _cpu_up(cpu, 1, CPUHP_ONLINE);
                trace_suspend_resume(TPS("CPU_ON"), cpu, false);
                if (!error) {
                        pr_info("CPU%d is up\n", cpu);
                        continue;
                }
                pr_warn("Error taking CPU%d up: %d\n", cpu, error);
        }

        arch_thaw_secondary_cpus_end();

        cpumask_clear(frozen_cpus);
out:
        cpu_maps_update_done();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

3.1.5 device PM

// kernel/drivers/base/power/main.c
/**
 * dpm_resume_noirq - Execute "noirq resume" callbacks for all devices.
 * @state: PM transition of the system being carried out.
 *
 * Invoke the "noirq" resume callbacks for all devices in dpm_noirq_list and
 * allow device drivers' interrupt handlers to be called.
 */
void dpm_resume_noirq(pm_message_t state)
{
        // 无IRQ情况下的设备恢复操作
        dpm_noirq_resume_devices(state);
        // 重新启用设备的IRQ中断
        resume_device_irqs();
        // 取消设备的唤醒IRQ中断警报
        device_wakeup_disarm_wake_irqs();
}

// kernel/irq/pm.c
void resume_device_irqs(void)
{
        resume_irqs(false);
}
EXPORT_SYMBOL_GPL(resume_device_irqs);

static void resume_irqs(bool want_early)
{
        struct irq_desc *desc;
        int irq;

        for_each_irq_desc(irq, desc) {
                unsigned long flags;
                bool is_early = desc->action &&
                        desc->action->flags & IRQF_EARLY_RESUME;

                if (!is_early && want_early)
                        continue;
                if (irq_settings_is_nested_thread(desc))
                        continue;

                raw_spin_lock_irqsave(&desc->lock, flags);
                resume_irq(desc);
                raw_spin_unlock_irqrestore(&desc->lock, flags);
        }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

3.1.6 PM core
恢复控制台

// kernel/printk/printk.c
void resume_console(void)
{
        if (!console_suspend_enabled)
                return;
        down_console_sem();
        console_suspended = 0;
        console_unlock();
        pr_flush(1000, true);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

线程解冻

// kernel/power/suspend.c
static void suspend_finish(void)
{
        suspend_thaw_processes();
        pm_notifier_call_chain(PM_POST_SUSPEND);
        pm_restore_console();
}

// kernel/power/power.h
static inline void suspend_thaw_processes(void)
{
        // 最终调用到freezer模块进行解冻
        thaw_processes();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

3.4 系统被唤醒的场景
按键、RTC、亮屏、USB拔插等。

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/817956
推荐阅读
相关标签
  

闽ICP备14008679号