由于簡書對文章有最大長度限制跌榔,這部分內(nèi)容拆解為三篇,分別為:
Binder驅(qū)動之設(shè)備控制binder_ioctl
-- 一
Binder驅(qū)動之設(shè)備控制binder_ioctl
-- 二
Binder驅(qū)動之設(shè)備控制binder_ioctl
-- 三
概述
ioctl
是Linux中常見的系統(tǒng)調(diào)用郊楣,它用于對底層設(shè)備的一些特性進行控制的用戶態(tài)接口营曼,應(yīng)用程序在調(diào)用ioctl
進行設(shè)備控制時,最后會調(diào)用到設(shè)備注冊struct file_operations
結(jié)構(gòu)體對象時的unlocked_ioctl
或者compat_ioctl
兩個鉤子上棚蓄,具體是調(diào)用哪個鉤子判斷標(biāo)準如下:
-
compat_ioctl
: 32位的應(yīng)用運行在64位的內(nèi)核上,這個鉤子被調(diào)用碍脏。 -
unlocked_ioctl
: 64位的應(yīng)用運行在64位的內(nèi)核或者32位的應(yīng)用運行在32位的內(nèi)核上梭依,則調(diào)用這個鉤子。
Binder做為Android中進程間高效通信的核心組件典尾,其底層是以misc設(shè)備驅(qū)動的形式實現(xiàn)的役拴,但它本身并沒有實現(xiàn)read
,write
操作,所有的控制都是通過ioctl
操作來實現(xiàn)钾埂。在Binder驅(qū)動的struct file_operations
定義中可見河闰,它的compat_ioctl
和unlocked_ioctl
兩個鉤子的的實現(xiàn)都是對應(yīng)到binder_ioctl
上的。
static const struct file_operations binder_fops = {
.owner = THIS_MODULE,
.poll = binder_poll,
.unlocked_ioctl = binder_ioctl,
.compat_ioctl = binder_ioctl,
.mmap = binder_mmap,
.open = binder_open,
.flush = binder_flush,
.release = binder_release,
};
接下來褥紫,我們來先看看binder_ioctl
的實現(xiàn)有關(guān)的結(jié)構(gòu)體姜性。
相關(guān)數(shù)據(jù)結(jié)構(gòu)
2.1 struct binder_thread
struct binder_thread {
struct binder_proc *proc; /*該thread相關(guān)聯(lián)的binder_proc*/
struct rb_node rb_node; /*用于鏈入proc的threads紅黑樹*/
int pid;
int looper; /* 狀態(tài)標(biāo)識位,用于表示當(dāng)前線程所處的狀態(tài)髓考,具體包括以下幾種狀態(tài):
* enum {
* BINDER_LOOPER_STATE_REGISTERED = 0x01, /*進程的非主線程進入Binder循環(huán)狀態(tài)*/
* BINDER_LOOPER_STATE_ENTERED = 0x02, /*進程的主線程進入Binder循環(huán)狀態(tài)*/
* BINDER_LOOPER_STATE_EXITED = 0x04, /*線程退出Binder循環(huán)狀態(tài)*/
* BINDER_LOOPER_STATE_INVALID = 0x08, /*線程處在一個無效的狀態(tài)部念,表示出錯*/
* BINDER_LOOPER_STATE_WAITING = 0x10, /*線程的todo隊列為空,進入等待請求的狀態(tài)*/
* BINDER_LOOPER_STATE_NEED_RETURN = 0x20 /*線程是否需要返回數(shù)據(jù)給進程的用戶態(tài)*/
* };
*/
struct binder_transaction *transaction_stack; /*該線程的事務(wù)棧氨菇。通過struct binder_transaction的
* from_parent和to_parent分別鏈入客戶端和服務(wù)端線程的
* transaction_stack事務(wù)棧中(即本字段)儡炼。詳見2.7 */
struct list_head todo; /*binder_work隊列,管理本線程所有待處理的binder_work*/
uint32_t return_error; /* Write failed, return error code in read buf */
uint32_t return_error2; /* Write failed, return error code in read
* buffer. Used when **sending a reply to a dead process** that */
* we are also waiting on 查蓉。發(fā)送reply時發(fā)生錯誤乌询,該錯誤碼用于返回給發(fā)送進程 */
wait_queue_head_t wait; /*binder線程空閑時,用于等待隊列相關(guān)的結(jié)構(gòu)豌研,是Linux內(nèi)核的一個數(shù)據(jù)結(jié)構(gòu)*/
struct binder_stats stats; /*統(tǒng)計有關(guān)的結(jié)構(gòu)*/
};
2.2 struct binder_proc
中的相關(guān)成員
struct binder_proc{
...
struct rb_root threads; /*管理thread的紅黑樹根節(jié)點妹田,以線程的id為序*/
struct rb_root nodes; /*管理binder_node的紅黑樹根節(jié)點,以binder_node中的ptr大小為序*/
struct rb_root refs_by_desc; /*管理binder_ref的紅黑樹根節(jié)點鹃共,以binder_ref中的desc大小為序*/
struct rb_root refs_by_node; /*管理binder_ref的紅黑樹根節(jié)點鬼佣,以binder_ref對應(yīng)的binder_node的地址為序*/
...
int max_threads; /*最大線程數(shù)*/
...
};
2.3 用于用戶態(tài)向內(nèi)核態(tài)傳輸數(shù)據(jù)的結(jié)構(gòu)體 —— struct binder_write_read
struct binder_write_read {
/* process ----> kernel */
binder_size_t write_size; /* bytes to write, 進程用戶態(tài)地址空間傳遞到內(nèi)核數(shù)據(jù)的大小*/
binder_size_t write_consumed; /* bytes consumed by driver 進程用戶態(tài)地址空間傳遞到內(nèi)核數(shù)據(jù)中已經(jīng)被內(nèi)核態(tài)處理的大小*/
binder_uintptr_t write_buffer; /*進程用戶態(tài)地址空間傳遞到內(nèi)核數(shù)據(jù)的起始地址*/
/* kernel ----> process */
binder_size_t read_size; /* bytes to read及汉, 總共可供給驅(qū)動寫入的字節(jié)數(shù)沮趣,read_buffer可供內(nèi)核使用的大小*/
binder_size_t read_consumed; /* bytes consumed by driver屯烦, 內(nèi)核Binder驅(qū)動發(fā)送給用戶態(tài)進程的字節(jié)數(shù)*/
binder_uintptr_t read_buffer; /*內(nèi)核驅(qū)動發(fā)送給進程數(shù)據(jù)buffer的起始地址*/
};
2.4 Binder C/S通信架構(gòu)中坷随,C端的驅(qū)動層表示 —— struct binder_ref
struct binder_ref {
/* Lookups needed: */
/* node + proc => ref (transaction) */
/* desc + proc => ref (transaction, inc/dec ref) */
/* node => refs + procs (proc exit) */
int debug_id; /*每個binder_ref的唯一標(biāo)識符房铭,主要用于debug*/
struct rb_node rb_node_desc; /*用來鏈入proc->refs_by_desc紅黑樹中,該紅黑樹以desc域為序組織的*/
struct rb_node rb_node_node; /*用來鏈入proc->refs_by_node紅黑樹中, 該紅黑樹以該binder_ref所對應(yīng)的binder_node的地址為序組織的*/
struct hlist_node node_entry; /*用來鏈入binder_node的refs哈希鏈表中温眉。*/
struct binder_proc *proc; /*指向該binder_ref中所屬的binder_proc*/
struct binder_node *node; /*指向該binder_ref所對應(yīng)(引用)的binder_node*/
uint32_t desc; /*binder_ref的描述符缸匪。用來返回給進程用戶態(tài)地址空間,標(biāo)識所對應(yīng)的binder_ref*/
int strong; /*強引用計數(shù)*/
int weak; /*弱引用計數(shù)*/
struct binder_ref_death *death; /*Binder“死亡訃告”相關(guān)的一個結(jié)構(gòu)體,詳見:2.8*/
};
2.5 Binder C/S通信架構(gòu)中类溢,S端的驅(qū)動層表示 —— struct binder_node
struct binder_node {
int debug_id;
struct binder_work work;
union {
struct rb_node rb_node; /*用來鏈入proc的nodes紅黑樹凌蔬,該紅黑樹以binder_node的ptr的大小為序*/
struct hlist_node dead_node;
};
struct binder_proc *proc;
struct hlist_head refs; /*所有引用這個binder_node的binder_ref通過它的node_entry加入這個哈希鏈表中,
* 這樣binder_node通過查看這個哈希鏈表就知道有哪些binder_ref在引用它*/
int internal_strong_refs; /*binder_ref的強引用計數(shù)闯冷,即有多少個binder_ref強引用這個binder_node*/
int local_weak_refs; /*BBinder弱引用計數(shù)*/
int local_strong_refs; /*binder_buffer.target_node及BBinder的強引用計數(shù)*/
binder_uintptr_t ptr; /*對應(yīng)BBinder基類RefBase中mRef成員的的地址砂心,它是一個引用計數(shù)器,類型為weakref_impl*/
binder_uintptr_t cookie; /*對應(yīng)BBinder的地址*/**
unsigned has_strong_ref:1; /*標(biāo)識是否已經(jīng)增加了用戶態(tài)對應(yīng)binder service(BBinder)對象的強引用計數(shù)*/
unsigned pending_strong_ref:1; /*標(biāo)識是否有未處理的BR_ACQUIRE命令蛇耀,在執(zhí)行BR_ACQUIRE請求命令前設(shè)為1辩诞,在BC_ACQUIRE_DONE中設(shè)為0*/
unsigned has_weak_ref:1; /*標(biāo)識是否已經(jīng)增加了用戶態(tài)對應(yīng)binder service(BBinder)對象的弱引用計數(shù)*/
unsigned pending_weak_ref:1; /*標(biāo)識是否有未處理的BR_INCREFS命令,在執(zhí)行BR_INCREFS請求命令前設(shè)為1纺涤,在BC_INCREFS_DONE中設(shè)為0*/
unsigned has_async_transaction:1; /*標(biāo)識是否有異步事務(wù)要處理译暂。異步事務(wù)的含義是:客戶端發(fā)送了帶有TF_ONE_WAY標(biāo)識的請求。*/
unsigned accept_fds:1; /*是否接受文件描述符*/
unsigned min_priority:8;
struct list_head async_todo; /*異步事務(wù)待處理鏈表*/
};
2.6 用于Binder用戶態(tài)向內(nèi)核驅(qū)動傳輸事務(wù)數(shù)據(jù) —— struct binder_transaction_data
struct binder_transaction_data {
/* The first two are only used for bcTRANSACTION and brTRANSACTION,
* identifying the target and contents of the transaction.
*/
union {
/* target descriptor of command transaction */
__u32 handle;
/* target descriptor of return transaction */
binder_uintptr_t ptr;
} target;
binder_uintptr_t cookie; /* target object cookie */
__u32 code; /* transaction command */
/* General information about the transaction. */
__u32 flags; /*標(biāo)志位撩炊,如:TF_ONE_WAY*/
pid_t sender_pid; /*發(fā)送者進程id*/
uid_t sender_euid; /*發(fā)送者有效用戶id*/
binder_size_t data_size; /* number of bytes of data */
binder_size_t offsets_size; /* number of bytes of offsets */
/* If this transaction is inline, the data immediately
* follows here; otherwise, it ends with a pointer to
* the data buffer.
*/
union { /*存放事務(wù)的數(shù)據(jù)部分外永,如果是inline,則數(shù)據(jù)直接放在buf數(shù)組中拧咳;
* 如果不是伯顶,則放在ptr結(jié)構(gòu)體中的buffer和offsets的指針中。一般情況都是通過ptr結(jié)構(gòu)體*/
struct {
/* transaction data */
binder_uintptr_t buffer;
/* offsets from buffer to flat_binder_object structs */
binder_uintptr_t offsets;
} ptr;
__u8 buf[8]; /*inline數(shù)據(jù)直接放在這個buf中, 在4.0.9的內(nèi)核中骆膝,這個字段沒有看見使用的地方*/
} data;
};
2.7 用于Binder內(nèi)核態(tài)驅(qū)動表示Binder通信事務(wù)數(shù)據(jù)結(jié)構(gòu) —— struct binder_transaction
struct binder_transaction {
int debug_id;
struct binder_work work; /*用于鏈入線程/進程todo隊列的成員*/
struct binder_thread *from; /*事務(wù)發(fā)起線程thread的地址砾淌,如果是binder server回復(fù)給client,該域為NULL*/
struct binder_transaction ***from_parent**; /* 用于鏈入事務(wù)發(fā)起線程的事務(wù)棧中谭网,
* 加入的時機是binder_transaction_data傳入驅(qū)動并被binder_transaction處理的時候*/
struct binder_proc *to_proc; /*目標(biāo)線程的proc地址*/
struct binder_thread *to_thread; /*事務(wù)目標(biāo)線程thread的地址*/
struct binder_transaction *to_parent; /* 用于鏈入目標(biāo)線程的事務(wù)棧中汪厨,
* 加入的時機是目標(biāo)線程在調(diào)用binder_thread_read處理thread->todo隊列
* 類型為BINDER_WORK_TRANACTION的binder_work時*/
unsigned need_reply:1;
/* unsigned is_dead:1; */ /* not used at the moment */
struct binder_buffer *buffer; /*存儲數(shù)據(jù)的地方*/
unsigned int code; /*一個binder調(diào)用所對應(yīng)的代號*/
unsigned int flags;
long priority; /*請求/回復(fù) 線程的優(yōu)先級*/
long saved_priority; /*存儲線程優(yōu)先級備份,當(dāng)需要修改一個線程的優(yōu)先級時愉择,先將它當(dāng)前值放在該變量中劫乱,以便于稍后恢復(fù)。*/
kuid_t sender_euid;
};
進程用戶態(tài)傳輸進來的struct binder_transaction_data
到內(nèi)核態(tài)后锥涕,會轉(zhuǎn)化成相應(yīng)的struct binder_transaction
衷戈。該數(shù)據(jù)結(jié)構(gòu)主要用于承載Binder請求和回復(fù)通信中的數(shù)據(jù)。
2.8 用于注冊Binder service死亡通知的數(shù)據(jù)結(jié)構(gòu) —— binder_ref_death
struct binder_ref_death {
struct binder_work work; /*binder service死亡時层坠,通過該work的entry域鏈入thread或者proc的todo隊列*/
binder_uintptr_t cookie; /*binder service死亡時殖妇,要通知的BpBinder對象的地址*/
};
3. 設(shè)備驅(qū)動控制 --- binder_ioctl
Binder驅(qū)動沒有提供read/write操作,所有數(shù)據(jù)傳輸破花、控制都是通過binder_ioctl
進行谦趣,因此該部分是Binder驅(qū)動的核心內(nèi)容疲吸,承載了Binder數(shù)據(jù)傳輸部分的主要業(yè)務(wù)。
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
struct binder_proc *proc = filp->private_data;
struct binder_thread *thread;
/*讀取命令的大小*/
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
/*pr_info("binder_ioctl: %d:%d %x %lx\n",
proc->pid, current->pid, cmd, arg);*/
trace_binder_ioctl(cmd, arg);
/* 如果binder_stop_on_user_error < 2 則直接返回0前鹅;
* 否則摘悴,調(diào)用_wait_event_interruptible進入可中斷的掛起狀態(tài),接著讓出處理器舰绘,
* 直到被wake_up且條件(binder_stop_on_user_error < 2)為真時才返回
*/
ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
if (ret)
goto err_unlocked;
/*獲取binder_main_lock鎖*/
binder_lock(__func__);
/*在proc->threads紅黑樹中查找thread蹂喻,該紅黑樹以pid為序,具體詳見3.1*/
thread = **binder_get_thread**(proc);
if (thread == NULL) {
ret = -ENOMEM;
goto err;
}
/*根據(jù)不同的命令捂寿,調(diào)用不同的處理函數(shù)進行處理*/
switch (cmd) {
case BINDER_WRITE_READ:
/*讀寫命令,數(shù)據(jù)傳輸,binder IPC通信的核心邏輯口四,詳見3.2*/
ret = **binder_ioctl_write_read**(filp, cmd, arg, thread);
if (ret)
goto err;
break;
case BINDER_SET_MAX_THREADS:
/*設(shè)置最大線程數(shù),直接將值設(shè)置到proc結(jié)構(gòu)的max_threads域中秦陋。*/
if (copy_from_user(&**proc->max_threads**, ubuf, sizeof(proc->max_threads))) {
ret = -EINVAL;
goto err;
}
break;
case BINDER_SET_CONTEXT_MGR:
/*設(shè)置Context manager窃祝,即將自己設(shè)置為ServiceManager,詳見3.3*/
ret = binder_ioctl_set_ctx_mgr(filp);
if (ret)
goto err;
break;
case BINDER_THREAD_EXIT:
/*binder線程退出命令踱侣,釋放相關(guān)資源,詳見3.4*/
binder_debug(BINDER_DEBUG_THREADS, "%d:%d exit\n",
proc->pid, thread->pid);
binder_free_thread(proc, thread);
thread = NULL;
break;
case BINDER_VERSION: {
/*獲取binder驅(qū)動版本號粪小,在kernel4.4版本中,32位該值為7抡句,64位版本該值為8*/
struct binder_version __user *ver = ubuf;
if (size != sizeof(struct binder_version)) {
ret = -EINVAL;
goto err;
}
/*將版本號信息寫入用戶態(tài)地址空間struct binder_version的protocol_version中*/
if (put_user(BINDER_CURRENT_PROTOCOL_VERSION,
&ver->protocol_version)) {
ret = -EINVAL;
goto err;
}
break;
}
default:
ret = -EINVAL;
goto err;
}
ret = 0;
err:
if (thread)
thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;
binder_unlock(__func__); /*釋放binder_main_lock鎖*/
wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
if (ret && ret != -ERESTARTSYS)
pr_info("%d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret);
err_unlocked:
trace_binder_ioctl_done(ret);
return ret;
}
-
_IOC_SIZE讀取參數(shù)命令的大小探膊。在32位的體系架構(gòu)中,參數(shù)
cmd
由四個域組成:- 讀寫屬性域(
direction: read/write
)待榔, 區(qū)分是讀取命令逞壁,還是寫入命令。bit30~bit31锐锣,占2bit腌闯。可用宏_IOC_DIR
讀取雕憔。 - 數(shù)據(jù)大小域(
size : size of argument
), 表示ioctl
中arg
變量所指內(nèi)存區(qū)域占用內(nèi)存大小姿骏。bit16~bit29,占14bit斤彼》质荩可用宏_IOC_SIZE
讀取 。 - 魔數(shù)域 (
type:usually related to the major number
)琉苇,又叫“幻數(shù)”區(qū)缨恒,用來區(qū)分不同的設(shè)備驅(qū)動柳畔,當(dāng)傳入的值與自身的值不一致時則不進行進一步處理牧氮,是用于防止誤使用的一種狀態(tài)標(biāo)識位文搂。一般用字母A~Z
或者a~z
表示。bit8~bit15,占8bit土陪≈绾梗可用宏_IOC_TYPE
讀取。 - 序號數(shù)或者基數(shù)(
command
):用于區(qū)分各種命令旺坠。bit0~bit7乔遮,占8bit扮超∪∪校可用宏_IOC_NR
讀取。
- 讀寫屬性域(
-
binder_stop_on_user_error
, 該變量是一個全局靜態(tài)變量, 它的值通過模塊參數(shù)stop_on_user_error
控制出刷,當(dāng)系統(tǒng)出現(xiàn)問題時璧疗,可以通過將該值設(shè)置為一個大于或等于2的值,來暫停binder馁龟,來進行debug崩侠。模塊參數(shù)的設(shè)置可以在模塊插入時以參數(shù)傳遞的形式設(shè)定,如insmod xxx.ko arg=xxxx
形式坷檩;如果權(quán)限設(shè)置允許的話却音,也可以通過sysfs來動態(tài)設(shè)置(如echo 3 > /sys/module/binder/parameters/stop_no_user_error
)。相關(guān)代碼如下:
static int binder_stop_on_user_error;
/*定義模塊參數(shù)`stop_on_user_error`的設(shè)置函數(shù)*/
static int binder_set_stop_on_user_error(const char *val,
struct kernel_param *kp)
{
int ret;
ret = param_set_int(val, kp);/*將sysfs中/sys/module/binder/parameters/stop_on_user_error讀入binder_stop_on_user_error*/
if (binder_stop_on_user_error < 2)
wake_up(&binder_user_error_wait);
return ret;
}
module_param_call(stop_on_user_error/*模塊參數(shù)名字,所在路徑為:/sys/module/binder/parameters/stop_on_user_error*/,
binder_set_stop_on_user_error /*模塊參數(shù)`stop_on_user_error`的set函數(shù)*/,
param_get_int/*模塊參數(shù)`stop_on_user_error`的讀取函數(shù)*/,
&binder_stop_on_user_error/*模塊參數(shù)對應(yīng)的變量地址*/,
S_IWUSR | S_IRUGO /*在sysfs中的權(quán)限設(shè)置*/);
-
module_param_call
該宏用于定義一個內(nèi)核模塊參數(shù)矢炼,它的定義為module_param_call(name, set, get, arg, perm)
系瓢,其中:-
name
:內(nèi)核模塊參數(shù)的名字,也是在sysfs中顯示的名字句灌; -
set
:是該內(nèi)核模塊參數(shù)設(shè)定的回調(diào)函數(shù)夷陋,當(dāng)在插入模式時傳遞參數(shù)或者通過sysfs設(shè)定內(nèi)核模塊參數(shù)時,該函數(shù)會被調(diào)用胰锌; -
get
: 是該內(nèi)核模塊參數(shù)讀取的回調(diào)函數(shù)骗绕; -
arg
:內(nèi)核模塊參數(shù)的地址; -
perm
:該內(nèi)核模塊參數(shù)的權(quán)限設(shè)置资昧,可以在sysfs中看到酬土。
-
對于基本的數(shù)據(jù)類型的讀取和設(shè)定回調(diào)函數(shù),內(nèi)核已經(jīng)預(yù)先做了定義格带,一般形式為:param_get_xxx
和param_set_xxx
诺凡,xxx
是int
, short
等〖螅可以參考一下這篇博客腹泌,Linux內(nèi)核模塊的編寫方法和技巧。
- 額外提一下sysfs尔觉,它是Linux2.6開始提供的一種虛擬文件系統(tǒng)凉袱,設(shè)計該文件系統(tǒng)的目的是把原本在procfs關(guān)于設(shè)備的部分獨立出來,以“設(shè)備層次結(jié)構(gòu)架構(gòu)(device tree)”的形式呈現(xiàn)。它可以把設(shè)備和驅(qū)動程序的信息從內(nèi)核輸出到用戶空間专甩,也可以對設(shè)備和驅(qū)動程序做設(shè)置钟鸵。具體詳見sysfs簡介。
3.1 查找thread --- binder_get_thread
static struct binder_thread *binder_get_thread(struct binder_proc *proc)
{
struct binder_thread *thread = NULL;
struct rb_node *parent = NULL;
struct rb_node **p = &proc->threads.rb_node; /*獲取紅黑樹根節(jié)點*/
/*查找pid等于當(dāng)前線程id的thread涤躲,該紅黑樹以pid大小為序組織*/
while (*p) {
parent = *p;
thread = rb_entry(parent, struct binder_thread, rb_node);
/*current->pid 是當(dāng)前運行線程的id棺耍,不是進程的id*/
if (current->pid < thread->pid)
p = &(*p)->rb_left;
else if (current->pid > thread->pid)
p = &(*p)->rb_right;
else
break;
}
if (*p == NULL) {
/*如果沒有找到,則新創(chuàng)建一個*/
thread = kzalloc(sizeof(*thread), GFP_KERNEL);
if (thread == NULL)
return NULL;
/*更新thread創(chuàng)建統(tǒng)計計數(shù)*/
binder_stats_created(BINDER_STAT_THREAD);
/*初始化相關(guān)數(shù)據(jù)成員*/
thread->proc = proc;
thread->pid = current->pid; /*獲取線程id*/
init_waitqueue_head(&thread->wait); /*初始化等待隊列*/
INIT_LIST_HEAD(&thread->todo); /*初始化待處理隊列*/
rb_link_node(&thread->rb_node, parent, p); /*加入到proc的threads紅黑樹中*/
rb_insert_color(&thread->rb_node, &proc->threads);
thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN;
thread->return_error = BR_OK;
thread->return_error2 = BR_OK;
}
return thread;
}