From 4182c4e2a07e2441fcd5c22eaff0ddfe7f826f61 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Mon, 15 Jul 2013 14:45:07 -0700 Subject: Use AudioSystem::setLowRamDevice() to configure memory Bug: 9798886 Change-Id: I9321e3f369f1ed9429ae222e3926ebdeb012b8b0 --- services/audioflinger/AudioFlinger.cpp | 22 +++++++++++++++++- services/audioflinger/AudioFlinger.h | 11 +++++++++ services/audioflinger/FastMixer.cpp | 42 +++++++++++++++++++++++----------- services/audioflinger/FastMixer.h | 19 ++++++++++++--- services/audioflinger/Threads.cpp | 4 +++- 5 files changed, 80 insertions(+), 18 deletions(-) (limited to 'services') diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 6a3007b..99e077c 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -62,6 +62,7 @@ #include #include #include +#include // ---------------------------------------------------------------------------- @@ -139,7 +140,9 @@ AudioFlinger::AudioFlinger() mMasterMute(false), mNextUniqueId(1), mMode(AUDIO_MODE_INVALID), - mBtNrecIsOff(false) + mBtNrecIsOff(false), + mIsLowRamDevice(true), + mIsDeviceTypeKnown(false) { getpid_cached = getpid(); char value[PROPERTY_VALUE_MAX]; @@ -1381,6 +1384,23 @@ size_t AudioFlinger::getPrimaryOutputFrameCount() // ---------------------------------------------------------------------------- +status_t AudioFlinger::setLowRamDevice(bool isLowRamDevice) +{ + uid_t uid = IPCThreadState::self()->getCallingUid(); + if (uid != AID_SYSTEM) { + return PERMISSION_DENIED; + } + Mutex::Autolock _l(mLock); + if (mIsDeviceTypeKnown) { + return INVALID_OPERATION; + } + mIsLowRamDevice = isLowRamDevice; + mIsDeviceTypeKnown = true; + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module, audio_devices_t *pDevices, uint32_t *pSamplingRate, diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 2df9173..f31619b 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -220,6 +220,8 @@ public: virtual uint32_t getPrimaryOutputSamplingRate(); virtual size_t getPrimaryOutputFrameCount(); + virtual status_t setLowRamDevice(bool isLowRamDevice); + virtual status_t onTransact( uint32_t code, const Parcel& data, @@ -623,6 +625,15 @@ public: static const size_t kTeeSinkTrackFramesDefault = 0x1000; #endif + // This method reads from a variable without mLock, but the variable is updated under mLock. So + // we might read a stale value, or a value that's inconsistent with respect to other variables. + // In this case, it's safe because the return value isn't used for making an important decision. + // The reason we don't want to take mLock is because it could block the caller for a long time. + bool isLowRamDevice() const { return mIsLowRamDevice; } + +private: + bool mIsLowRamDevice; + bool mIsDeviceTypeKnown; }; #undef INCLUDING_FROM_AUDIOFLINGER_H diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp index 819e8ec..5350e2c 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -83,7 +83,7 @@ bool FastMixer::threadLoop() struct timespec oldLoad = {0, 0}; // previous value of clock_gettime(CLOCK_THREAD_CPUTIME_ID) bool oldLoadValid = false; // whether oldLoad is valid uint32_t bounds = 0; - bool full = false; // whether we have collected at least kSamplingN samples + bool full = false; // whether we have collected at least mSamplingN samples #ifdef CPU_FREQUENCY_STATISTICS ThreadCpuUsage tcu; // for reading the current CPU clock frequency in kHz #endif @@ -534,11 +534,11 @@ bool FastMixer::threadLoop() #ifdef FAST_MIXER_STATISTICS if (isWarm) { // advance the FIFO queue bounds - size_t i = bounds & (FastMixerDumpState::kSamplingN - 1); + size_t i = bounds & (dumpState->mSamplingN - 1); bounds = (bounds & 0xFFFF0000) | ((bounds + 1) & 0xFFFF); if (full) { bounds += 0x10000; - } else if (!(bounds & (FastMixerDumpState::kSamplingN - 1))) { + } else if (!(bounds & (dumpState->mSamplingN - 1))) { full = true; } // compute the delta value of clock_gettime(CLOCK_MONOTONIC) @@ -608,27 +608,43 @@ bool FastMixer::threadLoop() // never return 'true'; Thread::_threadLoop() locks mutex which can result in priority inversion } -FastMixerDumpState::FastMixerDumpState() : +FastMixerDumpState::FastMixerDumpState( +#ifdef FAST_MIXER_STATISTICS + uint32_t samplingN +#endif + ) : mCommand(FastMixerState::INITIAL), mWriteSequence(0), mFramesWritten(0), mNumTracks(0), mWriteErrors(0), mUnderruns(0), mOverruns(0), mSampleRate(0), mFrameCount(0), /* mMeasuredWarmupTs({0, 0}), */ mWarmupCycles(0), mTrackMask(0) #ifdef FAST_MIXER_STATISTICS - , mBounds(0) + , mSamplingN(0), mBounds(0) #endif { mMeasuredWarmupTs.tv_sec = 0; mMeasuredWarmupTs.tv_nsec = 0; #ifdef FAST_MIXER_STATISTICS + increaseSamplingN(samplingN); +#endif +} + +#ifdef FAST_MIXER_STATISTICS +void FastMixerDumpState::increaseSamplingN(uint32_t samplingN) +{ + if (samplingN <= mSamplingN || samplingN > kSamplingN || roundup(samplingN) != samplingN) { + return; + } + uint32_t additional = samplingN - mSamplingN; // sample arrays aren't accessed atomically with respect to the bounds, // so clearing reduces chance for dumpsys to read random uninitialized samples - memset(&mMonotonicNs, 0, sizeof(mMonotonicNs)); - memset(&mLoadNs, 0, sizeof(mLoadNs)); + memset(&mMonotonicNs[mSamplingN], 0, sizeof(mMonotonicNs[0]) * additional); + memset(&mLoadNs[mSamplingN], 0, sizeof(mLoadNs[0]) * additional); #ifdef CPU_FREQUENCY_STATISTICS - memset(&mCpukHz, 0, sizeof(mCpukHz)); -#endif + memset(&mCpukHz[mSamplingN], 0, sizeof(mCpukHz[0]) * additional); #endif + mSamplingN = samplingN; } +#endif FastMixerDumpState::~FastMixerDumpState() { @@ -648,7 +664,7 @@ static int compare_uint32_t(const void *pa, const void *pb) } } -void FastMixerDumpState::dump(int fd) +void FastMixerDumpState::dump(int fd) const { if (mCommand == FastMixerState::INITIAL) { fdprintf(fd, "FastMixer not initialized\n"); @@ -699,9 +715,9 @@ void FastMixerDumpState::dump(int fd) uint32_t newestOpen = bounds & 0xFFFF; uint32_t oldestClosed = bounds >> 16; uint32_t n = (newestOpen - oldestClosed) & 0xFFFF; - if (n > kSamplingN) { + if (n > mSamplingN) { ALOGE("too many samples %u", n); - n = kSamplingN; + n = mSamplingN; } // statistics for monotonic (wall clock) time, thread raw CPU load in time, CPU clock frequency, // and adjusted CPU load in MHz normalized for CPU clock frequency @@ -717,7 +733,7 @@ void FastMixerDumpState::dump(int fd) uint32_t *tail = n >= kTailDenominator ? new uint32_t[n] : NULL; // loop over all the samples for (uint32_t j = 0; j < n; ++j) { - size_t i = oldestClosed++ & (kSamplingN - 1); + size_t i = oldestClosed++ & (mSamplingN - 1); uint32_t wallNs = mMonotonicNs[i]; if (tail != NULL) { tail[j] = wallNs; diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h index 2ab1d04..6158925 100644 --- a/services/audioflinger/FastMixer.h +++ b/services/audioflinger/FastMixer.h @@ -85,10 +85,14 @@ struct FastTrackDump { // Only POD types are permitted, and the contents shouldn't be trusted (i.e. do range checks). // It has a different lifetime than the FastMixer, and so it can't be a member of FastMixer. struct FastMixerDumpState { - FastMixerDumpState(); + FastMixerDumpState( +#ifdef FAST_MIXER_STATISTICS + uint32_t samplingN = kSamplingNforLowRamDevice +#endif + ); /*virtual*/ ~FastMixerDumpState(); - void dump(int fd); // should only be called on a stable copy, not the original + void dump(int fd) const; // should only be called on a stable copy, not the original FastMixerState::Command mCommand; // current command uint32_t mWriteSequence; // incremented before and after each write() @@ -106,8 +110,15 @@ struct FastMixerDumpState { #ifdef FAST_MIXER_STATISTICS // Recently collected samples of per-cycle monotonic time, thread CPU time, and CPU frequency. - // kSamplingN is the size of the sampling frame, and must be a power of 2 <= 0x8000. + // kSamplingN is max size of sampling frame (statistics), and must be a power of 2 <= 0x8000. + // The sample arrays are virtually allocated based on this compile-time constant, + // but are only initialized and used based on the runtime parameter mSamplingN. static const uint32_t kSamplingN = 0x8000; + // Compile-time constant for a "low RAM device", must be a power of 2 <= kSamplingN. + // This value was chosen such that each array uses 1 small page (4 Kbytes). + static const uint32_t kSamplingNforLowRamDevice = 0x400; + // Corresponding runtime maximum size of sample arrays, must be a power of 2 <= kSamplingN. + uint32_t mSamplingN; // The bounds define the interval of valid samples, and are represented as follows: // newest open (excluded) endpoint = lower 16 bits of bounds, modulo N // oldest closed (included) endpoint = upper 16 bits of bounds, modulo N @@ -119,6 +130,8 @@ struct FastMixerDumpState { #ifdef CPU_FREQUENCY_STATISTICS uint32_t mCpukHz[kSamplingN]; // absolute CPU clock frequency in kHz, bits 0-3 are CPU# #endif + // Increase sampling window after construction, must be a power of 2 <= kSamplingN + void increaseSamplingN(uint32_t samplingN); #endif }; diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 97a1e43..f27d908 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -2285,6 +2285,8 @@ void AudioFlinger::MixerThread::threadLoop_write() #endif } state->mCommand = FastMixerState::MIX_WRITE; + mFastMixerDumpState.increaseSamplingN(mAudioFlinger->isLowRamDevice() ? + FastMixerDumpState::kSamplingNforLowRamDevice : FastMixerDumpState::kSamplingN); sq->end(); sq->push(FastMixerStateQueue::BLOCK_UNTIL_PUSHED); if (kUseFastMixer == FastMixer_Dynamic) { @@ -3085,7 +3087,7 @@ void AudioFlinger::MixerThread::dumpInternals(int fd, const Vector& ar write(fd, result.string(), result.size()); // Make a non-atomic copy of fast mixer dump state so it won't change underneath us - FastMixerDumpState copy = mFastMixerDumpState; + const FastMixerDumpState copy(mFastMixerDumpState); copy.dump(fd); #ifdef STATE_QUEUE_DUMP -- cgit v1.1