我在Linux内核中观察到以下代码模式,例如net/sched/act_api.c
或许多其他地方:
rtnl_lock();
rtnetlink_rcv_msg(skb, ...);
replay:
ret = process_msg(skb);
...
/* try to obtain symbol which is in module. */
/* if fail, try to load the module, otherwise use the symbol */
a = get_symbol();
if (a == NULL) {
rtnl_unlock();
request_module();
rtnl_lock();
/* now verify that we can obtain symbols from requested module and return EAGAIN.*/
a = get_symbol();
module_put();
return -EAGAIN;
}
...
if (ret == -EAGAIN)
goto replay;
...
rtnl_unlock();
request_module
成功后,我们感兴趣的符号在内核内存空间中可用,我们可以使用它。但是我不明白为什么要返回EAGAIN并重新读取符号,为什么不能仅在request_module()
之后继续? 最佳答案
如果您查看Linux内核中的current implementation,则在上面的代码(即get_symbol()
)中的等效于tc_lookup_action_n()
的第二次调用后立即有一条注释,它确切解释了原因:
rtnl_unlock();
request_module("act_%s", act_name);
rtnl_lock();
a_o = tc_lookup_action_n(act_name);
/* We dropped the RTNL semaphore in order to
* perform the module load. So, even if we
* succeeded in loading the module we have to
* tell the caller to replay the request. We
* indicate this using -EAGAIN.
*/
if (a_o != NULL) {
err = -EAGAIN;
goto err_mod;
}
即使可以请求并加载该模块,由于删除了信号量是为了加载该模块(这是可以 hibernate 的操作(不是执行此函数的“标准方式”)),因此该函数返回
EAGAIN
对其进行信号通知。编辑澄清:
如果在添加新操作(可能会导致加载所需的模块)时查看调用序列,则有以下序列:
tc_ctl_action()
-> tcf_action_add()
-> tcf_action_init()
-> tcf_action_init_1()
。现在,如果将
EAGAIN
错误“移回”到tc_ctl_action()
中的case RTM_NEWACTION:
,我们将看到EAGAIN
ret值重复了对tcf_action_add
的调用。