• Nathan Lynch's avatar
    powerpc/rtas: Facilitate high-level call sequences · adf7a019
    Nathan Lynch authored
    On RTAS platforms there is a general restriction that the OS must not
    enter RTAS on more than one CPU at a time. This low-level
    serialization requirement is satisfied by holding a spin
    lock (rtas_lock) across most RTAS function invocations.
    
    However, some pseries RTAS functions require multiple successive calls
    to complete a logical operation. Beginning a new call sequence for such a
    function may disrupt any other sequences of that function already in
    progress. Safe and reliable use of these functions effectively
    requires higher-level serialization beyond what is already done at the
    level of RTAS entry and exit.
    
    Where a sequence-based RTAS function is invoked only through
    sys_rtas(), with no in-kernel users, there is no issue as far as the
    kernel is concerned. User space is responsible for appropriately
    serializing its call sequences. (Whether user space code actually
    takes measures to prevent sequence interleaving is another matter.)
    Examples of such functions currently include ibm,platform-dump and
    ibm,get-vpd.
    
    But where a sequence-based RTAS function has both user space and
    in-kernel uesrs, there is a hazard. Even if the in-kernel call sites
    of such a function serialize their sequences correctly, a user of
    sys_rtas() can invoke the same function at any time, potentially
    disrupting a sequence in progress.
    
    So in order to prevent disruption of kernel-based RTAS call sequences,
    they must serialize not only with themselves but also with sys_rtas()
    users, somehow. Preferably without adding more function-specific hacks
    to sys_rtas(). This is a prerequisite for adding an in-kernel call
    sequence of ibm,get-vpd, which is in a change to follow.
    
    Note that it has never been feasible for the kernel to prevent
    sys_rtas()-based sequences from being disrupted because control
    returns to user space on every call. sys_rtas()-based users of these
    functions have always been, and continue to be, responsible for
    coordinating their call sequences with other users, even those which
    may invoke the RTAS functions through less direct means than
    sys_rtas(). This is an unavoidable consequence of exposing
    sequence-based RTAS functions through sys_rtas().
    
    * Add an optional mutex member to struct rtas_function.
    
    * Statically define a mutex for each RTAS function with known call
      sequence serialization requirements, and assign its address to the
      .lock member of the corresponding function table entry, along with
      justifying commentary.
    
    * In sys_rtas(), if the table entry for the RTAS function being
      called has a populated lock member, acquire it before taking
      rtas_lock and entering RTAS.
    
    * Kernel-based RTAS call sequences are expected to access the
      appropriate mutex explicitly by name. For example, a user of the
      ibm,activate-firmware RTAS function would do:
    
            int token = rtas_function_token(RTAS_FN_IBM_ACTIVATE_FIRMWARE);
            int fwrc;
    
            mutex_lock(&rtas_ibm_activate_firmware_lock);
    
            do {
                    fwrc = rtas_call(token, 0, 1, NULL);
            } while (rtas_busy_delay(fwrc));
    
            mutex_unlock(&rtas_ibm_activate_firmware_lock);
    
    There should be no perceivable change introduced here except that
    concurrent callers of the same RTAS function via sys_rtas() may block
    on a mutex instead of spinning on rtas_lock.
    Signed-off-by: default avatarNathan Lynch <nathanl@linux.ibm.com>
    Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
    Link: https://msgid.link/20231212-papr-sys_rtas-vs-lockdown-v6-6-e9eafd0c8c6c@linux.ibm.com
    adf7a019
rtas.h 23.3 KB