我在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的调用。

10-04 20:13