[095]Binder调用的优先级降级

2024-04-20 11:11:03 浏览数 (3)

背景

这是一个来自朋友的疑问,在sf调用hwcbinder_f1的函数中hwc调用sfbinder_f2,会导致线程的优先级从97降级为120

代码语言:javascript复制
请教一下,binder嵌套调用的优先级是怎么设定的呀
现在嵌套流程是这样的
1, sf sync binder to HWC    SF优先级是97,call到HWC,HWC的优先级是97
2, HWC sync binder to sf     这里binder嵌套,HWC call到SF,SF的优先级被改为120了,从trace上看HWC的优先级全程是97,不知道这个120是哪来的
3, sf reply hwc
4, hwc reply sf;

一、基础知识-Binder调用的优先级继承

我们要知道,Binder默认支持client端调用server端的时候,将client端的线程优先级传递给server端。

用的知识点可以参考我这篇《[051]Binder线程优先级继承》

二、为什么线程优先级反而降了呢?

按照上面的知识点,从表面来看,不应该降级啊,我也很好奇为什么,接下来我来讲讲我的分析历程。

首先我拿到的信息就是120,我查看了binder驱动中的定义binder_priority接口体。

代码语言:javascript复制
/**
 * struct binder_priority - scheduler policy and priority
 * @sched_policy            scheduler policy
 * @prio                    [100..139] for SCHED_NORMAL, [0..99] for FIFO/RT //这个线索很关键
 *
 * The binder driver supports inheriting the following scheduler policies:
 * SCHED_NORMAL
 * SCHED_BATCH
 * SCHED_FIFO
 * SCHED_RR
 */
struct binder_priority {
    unsigned int sched_policy;
    int prio;
};

根据[100..139] for SCHED_NORMAL, [0..99] for FIFO/RT这个注释,可以知道肯定在binder驱动中将sched_policy改成了SCHED_NORMAL

继续在binder驱动中搜SCHED_NORMAL,只有三处 = SCHED_NORMAL代码,我们来一一排除分析一下。

第一处:这是初始化,设置的进程的default_priority,应该不是这里降级的。
代码语言:javascript复制
static int binder_open(struct inode *nodp, struct file *filp)
{
...
    if (binder_supported_policy(current->policy)) {
        proc->default_priority.sched_policy = current->policy;
        proc->default_priority.prio = current->normal_prio;
    } else {
        proc->default_priority.sched_policy = SCHED_NORMAL;
        proc->default_priority.prio = NICE_TO_PRIO(0);
    }
...
}
第二处:MIN_NICE是-20,对应优先级应该是100,所以也不是这里改的
代码语言:javascript复制
static void binder_do_set_priority(struct binder_thread *thread,
                   const struct binder_priority *desired,
                   bool verify)
{
...
    if (verify && is_rt_policy(policy) && !has_cap_nice) {
        long max_rtprio = task_rlimit(task, RLIMIT_RTPRIO);

        if (max_rtprio == 0) {
            policy = SCHED_NORMAL;
            priority = MIN_NICE;//-20
        } else if (priority > max_rtprio) {
            priority = max_rtprio;
        }
    }
...
}
第三处:NICE_TO_PRIO(0)正好是120,应用就是这里改的。
代码语言:javascript复制
static void binder_transaction_priority(struct binder_thread *thread,
                    struct binder_transaction *t,
                    struct binder_node *node)
{
...
    if (!node->inherit_rt && is_rt_policy(desired.sched_policy)) {//条件正好满足
        desired.prio = NICE_TO_PRIO(0);
        desired.sched_policy = SCHED_NORMAL;
    }
...
现场还原

HWC作为97的优先级调用SF的时候,会调用binder_transaction_priority这个函数,由于调用sfbinder nodeinherit_rtfalse,并且desired.sched_policy也就是HWCsched_policyis_rt_policy,因为97就是FIFO/RT,这里具体是哪个policy就不重要了。最后就将desired.prio设置成了120

代码语言:javascript复制
static bool is_rt_policy(int policy)
{
    return policy == SCHED_FIFO || policy == SCHED_RR;
}

三、为什么sf的binder node的inherit_rt为false

因为默认aidl的binder对象inherit_rt就是false

代码语言:javascript复制
frameworks/native/libs/binder/Parcel.cpp
status_t Parcel::flattenBinder(const sp<IBinder>& binder) {
    if (binder != nullptr) {
        if (!local) {
            ...
        } 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);
            }
            obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;
            if (local->isRequestingSid()) {
                obj.flags |= FLAT_BINDER_FLAG_TXN_SECURITY_CTX;
            }
            if (local->isInheritRt()) {//调用BBinder的isInheritRt
                obj.flags |= FLAT_BINDER_FLAG_INHERIT_RT;
            }
            obj.hdr.type = BINDER_TYPE_BINDER;
            obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
            obj.cookie = reinterpret_cast<uintptr_t>(local);
        }
    } else {
    ...
    }
}

bool BBinder::isInheritRt() {
    Extras* e = mExtras.load(std::memory_order_acquire);

    return e && e->mInheritRt;
}

除非你主动调用setInheritRt这个接口

代码语言:javascript复制
void BBinder::setInheritRt(bool inheritRt) {
    LOG_ALWAYS_FATAL_IF(mParceled,
                        "setInheritRt() should not be called after a binder object "
                        "is parceled/sent to another process");

    Extras* e = mExtras.load(std::memory_order_acquire);

    if (!e) {
        if (!inheritRt) {
            return;
        }

        e = getOrCreateExtras();
        if (!e) return; // out of memory
    }

    e->mInheritRt = inheritRt;
}

规范的用法

更加规范的用法是这样子设置,BnCameraDeviceCallback是aidl文件自动生成的,但是要注意,需要在传递给Binder驱动之前,对应的Binder对象就需要设置完成。

代码语言:javascript复制
::ndk::SpAIBinder AidlCamera3Device::AidlCameraDeviceCallbacks::createBinder() {
    auto binder = BnCameraDeviceCallback::createBinder();
    AIBinder_setInheritRt(binder.get(), /*inheritRt*/ true);//调用下面这个函数。
    return binder;
}

void AIBinder_setInheritRt(AIBinder* binder, bool inheritRt) {
    ABBinder* localBinder = binder->asABBinder();
    if (localBinder == nullptr) {
        LOG(FATAL) << "AIBinder_setInheritRt must be called on a local binder";
    }

    localBinder->setInheritRt(inheritRt);
}
小结

至此如何解决问题,只要找到HWC调用SF的接口对应的Binder对象,在初始化的地方,按照上述规范用法就可以了。

四、HWBinder的inherit_rt默认为true

HwBinder有一些特殊,就是默认加了FLAT_BINDER_FLAG_INHERIT_RT,所以HwBinder默认是可以继承RT的调度策略的。

代码语言:javascript复制
system/libhwbinder/Parcel.cpp
status_t flatten_binder(const sp<ProcessState>& /*proc*/,
    const sp<IBinder>& binder, Parcel* out)
{
    if (binder != nullptr) {
        BHwBinder *local = binder->localBinder();
        if (!local) {
        } else {
            // Get policy and convert it
            int policy = local->getMinSchedulingPolicy();
            int priority = local->getMinSchedulingPriority();

            obj.flags = priority & FLAT_BINDER_FLAG_PRIORITY_MASK;
            obj.flags |= FLAT_BINDER_FLAG_ACCEPTS_FDS | FLAT_BINDER_FLAG_INHERIT_RT;//默认就加了
            obj.flags |= (policy & 3) << FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT;
            if (local->isRequestingSid()) {
                obj.flags |= FLAT_BINDER_FLAG_TXN_SECURITY_CTX;
            }
            obj.hdr.type = BINDER_TYPE_BINDER;
            obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
            obj.cookie = reinterpret_cast<uintptr_t>(local);
        }
    } else {

    }
    return finish_flatten_binder(binder, obj, out);
}

但是有意思的Android U上HWC已经改成aidl,朋友的设备恰好是Android U,那问题的开头SF调用HWC的binder的时候为什么没有降级呢。聪明的你应该想到了,就是在HWC初始化Binder服务的时候,主动调用了AIBinder_setInheritRt,具体HWC代码要看供应商,这个是AOSP中代码例子。

代码语言:javascript复制
device/generic/goldfish-opengl/system/hwc3/Composer.cpp
::ndk::SpAIBinder Composer::createBinder() {
    DEBUG_LOG("%s", __FUNCTION__);

    auto binder = BnComposer::createBinder();
    AIBinder_setInheritRt(binder.get(), true);
    return binder;
}

这里还有一个细节大家要知道,因为HWC改成了aidl,也就意味着图中binder_f1的调用和binder_f2的响应是同一个sf线程。

这个知识点可以参考我之前的文章《[031]Binder线程栈复用》

总结

每次自己感觉对binder已经很了解,总会有新的问题触及自己的知识盲区,binder真的博大精深。

1 人点赞