поведение модуля разблокировки и запроса мьютекса ()


Я наблюдал следующий шаблон кода в ядре 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()?

1 2

1 ответ:

Если вы посмотрите на текущую реализацию в ядре Linux, сразу после второго вызова есть комментарий, эквивалентный 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;
}

Несмотря на то, что модуль может быть запрошен и загружен, поскольку семафор был отброшен, чтобы загрузить модуль, который является операцией, которая может спать (и не является "стандартным способом" выполнения этой функции), функция возвращает 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 повторяется.