From bf1915b6d85d3c222200ef5d5fd7745ed299b17b Mon Sep 17 00:00:00 2001 From: Steven Moreland Date: Thu, 16 Jul 2020 22:43:02 +0000 Subject: libbinder: setMinSchedulerPolicy This function on BBinder allows the minimum scheduling priority for incoming binder transactions to be set by the binder driver. This functionality was originally developed for libhwbinder and ported here. The main difference from libhwbider is that invalid priority sets cause an abort, whereas for libhwbinder, an error is returned. Unfortunately, that error is frequently ignored, and since this reflects static configuration, abort is considered a benefit. Bug: 149933095 Test: binderLibTest Change-Id: Ie93aebb09f4142cd66d841ca8b3ca39d1e0bb75e --- libs/binder/Binder.cpp | 44 +++++++++++++++++++++++++++++++++++++ libs/binder/Parcel.cpp | 24 +++++++++++++++----- libs/binder/include/binder/Binder.h | 16 ++++++++++++++ libs/binder/tests/binderLibTest.cpp | 32 +++++++++++++++++++++++++++ 4 files changed, 110 insertions(+), 6 deletions(-) (limited to 'libs') diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp index e0fb54384e..6ca3b16324 100644 --- a/libs/binder/Binder.cpp +++ b/libs/binder/Binder.cpp @@ -24,6 +24,7 @@ #include #include +#include #include namespace android { @@ -133,6 +134,8 @@ public: // unlocked objects bool mRequestingSid = false; sp mExtension; + int mPolicy = SCHED_NORMAL; + int mPriority = 0; // for below objects Mutex mLock; @@ -279,6 +282,47 @@ sp BBinder::getExtension() { return e->mExtension; } +void BBinder::setMinSchedulerPolicy(int policy, int priority) { + switch (policy) { + case SCHED_NORMAL: + LOG_ALWAYS_FATAL_IF(priority < -20 || priority > 19, "Invalid priority for SCHED_NORMAL: %d", priority); + break; + case SCHED_RR: + case SCHED_FIFO: + LOG_ALWAYS_FATAL_IF(priority < 1 || priority > 99, "Invalid priority for sched %d: %d", policy, priority); + break; + default: + LOG_ALWAYS_FATAL("Unrecognized scheduling policy: %d", policy); + } + + Extras* e = mExtras.load(std::memory_order_acquire); + + if (e == nullptr) { + // Avoid allocations if called with default. + if (policy == SCHED_NORMAL && priority == 0) { + return; + } + + e = getOrCreateExtras(); + if (!e) return; // out of memory + } + + e->mPolicy = policy; + e->mPriority = priority; +} + +int BBinder::getMinSchedulerPolicy() { + Extras* e = mExtras.load(std::memory_order_acquire); + if (e == nullptr) return SCHED_NORMAL; + return e->mPolicy; +} + +int BBinder::getMinSchedulerPriority() { + Extras* e = mExtras.load(std::memory_order_acquire); + if (e == nullptr) return 0; + return e->mPriority; +} + pid_t BBinder::getDebugPid() { return getpid(); } diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 660b96a112..c7cb495d15 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -188,16 +189,18 @@ status_t Parcel::finishUnflattenBinder( return OK; } +static constexpr inline int schedPolicyMask(int policy, int priority) { + return (priority & FLAT_BINDER_FLAG_PRIORITY_MASK) | ((policy & 3) << FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT); +} + status_t Parcel::flattenBinder(const sp& binder) { flat_binder_object obj; + obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS; - if (IPCThreadState::self()->backgroundSchedulingDisabled()) { - /* minimum priority for all nodes is nice 0 */ - obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS; - } else { - /* minimum priority for all nodes is MAX_NICE(19) */ - obj.flags = 0x13 | FLAT_BINDER_FLAG_ACCEPTS_FDS; + int schedBits = 0; + if (!IPCThreadState::self()->backgroundSchedulingDisabled()) { + schedBits = schedPolicyMask(SCHED_NORMAL, 19); } if (binder != nullptr) { @@ -213,6 +216,13 @@ status_t Parcel::flattenBinder(const sp& binder) obj.handle = handle; obj.cookie = 0; } else { + int policy = local->getMinSchedulerPolicy(); + int priority = local->getMinSchedulerPriority(); + + if (policy != 0 || priority != 0) { + // override value, since it is set explicitly + schedBits = schedPolicyMask(policy, priority); + } if (local->isRequestingSid()) { obj.flags |= FLAT_BINDER_FLAG_TXN_SECURITY_CTX; } @@ -226,6 +236,8 @@ status_t Parcel::flattenBinder(const sp& binder) obj.cookie = 0; } + obj.flags |= schedBits; + return finishFlattenBinder(binder, obj); } diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h index 74e52db53f..f3fea163cd 100644 --- a/libs/binder/include/binder/Binder.h +++ b/libs/binder/include/binder/Binder.h @@ -72,6 +72,22 @@ public: // This must be called before the object is sent to another process. Not thread safe. void setExtension(const sp& extension); + // This must be called before the object is sent to another process. Not thread safe. + // + // This function will abort if improper parameters are set. This is like + // sched_setscheduler. However, it sets the minimum scheduling policy + // only for the duration that this specific binder object is handling the + // call in a threadpool. By default, this API is set to SCHED_NORMAL/0. In + // this case, the scheduling priority will not actually be modified from + // binder defaults. See also IPCThreadState::disableBackgroundScheduling. + // + // Appropriate values are: + // SCHED_NORMAL: -20 <= priority <= 19 + // SCHED_RR/SCHED_FIFO: 1 <= priority <= 99 + void setMinSchedulerPolicy(int policy, int priority); + int getMinSchedulerPolicy(); + int getMinSchedulerPriority(); + pid_t getDebugPid(); protected: diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index 8cb06e17e9..f8ee32c70d 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -48,6 +48,9 @@ static char *binderservername; static char *binderserversuffix; static char binderserverarg[] = "--binderserver"; +static constexpr int kSchedPolicy = SCHED_RR; +static constexpr int kSchedPriority = 7; + static String16 binderLibTestServiceName = String16("test.binderLib"); enum BinderLibTestTranscationCode { @@ -73,6 +76,7 @@ enum BinderLibTestTranscationCode { BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION, BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, + BINDER_LIB_TEST_GET_SCHEDULING_POLICY, BINDER_LIB_TEST_ECHO_VECTOR, }; @@ -1012,6 +1016,22 @@ TEST_F(BinderLibTest, WorkSourcePropagatedForAllFollowingBinderCalls) EXPECT_EQ(NO_ERROR, ret2); } +TEST_F(BinderLibTest, SchedPolicySet) { + sp server = addServer(); + ASSERT_TRUE(server != nullptr); + + Parcel data, reply; + status_t ret = server->transact(BINDER_LIB_TEST_GET_SCHEDULING_POLICY, data, &reply); + EXPECT_EQ(NO_ERROR, ret); + + int policy = reply.readInt32(); + int priority = reply.readInt32(); + + EXPECT_EQ(kSchedPolicy, policy & (~SCHED_RESET_ON_FORK)); + EXPECT_EQ(kSchedPriority, priority); +} + + TEST_F(BinderLibTest, VectorSent) { Parcel data, reply; sp server = addServer(); @@ -1301,6 +1321,16 @@ class BinderLibTestService : public BBinder reply->writeInt32(IPCThreadState::self()->getCallingWorkSourceUid()); return NO_ERROR; } + case BINDER_LIB_TEST_GET_SCHEDULING_POLICY: { + int policy = 0; + sched_param param; + if (0 != pthread_getschedparam(pthread_self(), &policy, ¶m)) { + return UNKNOWN_ERROR; + } + reply->writeInt32(policy); + reply->writeInt32(param.sched_priority); + return NO_ERROR; + } case BINDER_LIB_TEST_ECHO_VECTOR: { std::vector vector; auto err = data.readUint64Vector(&vector); @@ -1334,6 +1364,8 @@ int run_server(int index, int readypipefd, bool usePoll) { sp testService = new BinderLibTestService(index); + testService->setMinSchedulerPolicy(kSchedPolicy, kSchedPriority); + /* * Normally would also contain functionality as well, but we are only * testing the extension mechanism. -- cgit v1.2.3-59-g8ed1b