Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bf1915b6 authored by Steven Moreland's avatar Steven Moreland
Browse files

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
parent ea830510
Loading
Loading
Loading
Loading
+44 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include <binder/IShellCallback.h>
#include <binder/Parcel.h>

#include <linux/sched.h>
#include <stdio.h>

namespace android {
@@ -133,6 +134,8 @@ public:
    // unlocked objects
    bool mRequestingSid = false;
    sp<IBinder> mExtension;
    int mPolicy = SCHED_NORMAL;
    int mPriority = 0;

    // for below objects
    Mutex mLock;
@@ -279,6 +282,47 @@ sp<IBinder> 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();
}
+18 −6
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <linux/sched.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
@@ -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<IBinder>& binder)
{
    flat_binder_object obj;

    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<IBinder>& 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<IBinder>& binder)
        obj.cookie = 0;
    }

    obj.flags |= schedBits;

    return finishFlattenBinder(binder, obj);
}

+16 −0
Original line number Diff line number Diff line
@@ -72,6 +72,22 @@ public:
    // This must be called before the object is sent to another process. Not thread safe.
    void                setExtension(const sp<IBinder>& 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:
+32 −0
Original line number Diff line number Diff line
@@ -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<IBinder> 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<IBinder> 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, &param)) {
                    return UNKNOWN_ERROR;
                }
                reply->writeInt32(policy);
                reply->writeInt32(param.sched_priority);
                return NO_ERROR;
            }
            case BINDER_LIB_TEST_ECHO_VECTOR: {
                std::vector<uint64_t> vector;
                auto err = data.readUint64Vector(&vector);
@@ -1334,6 +1364,8 @@ int run_server(int index, int readypipefd, bool usePoll)
    {
        sp<BinderLibTestService> testService = new BinderLibTestService(index);

        testService->setMinSchedulerPolicy(kSchedPolicy, kSchedPriority);

        /*
         * Normally would also contain functionality as well, but we are only
         * testing the extension mechanism.