[PATCH v8 1/7] powerpc/pseries: Define common functions for RTAS sequence calls
Haren Myneni
haren at linux.ibm.com
Wed Mar 12 17:30:46 AEDT 2025
On Wed, 2025-03-12 at 10:55 +0530, Mukesh Kumar Chaurasiya wrote:
> On Tue, Mar 11, 2025 at 03:50:42PM -0700, Haren Myneni wrote:
> > The RTAS call can be normal where retrieves the data form the
> > hypervisor once or sequence based RTAS call which has to
> > issue multiple times until the complete data is obtained. For
> > some of these sequence RTAS calls, the OS should not interleave
> > calls with different input until the sequence is completed.
> > The data is collected for each call and copy to the buffer
> > for the entire sequence during ioctl() handle and then expose
> > this buffer to the user space with read() handle.
> >
> > One such sequence RTAS call is ibm,get-vpd and its support is
> > already included in the current code. To add the similar support
> > for other sequence based calls, move the common functions in to
> > separate file and update papr_rtas_sequence struct with the
> > following callbacks so that RTAS call specific code will be
> > defined and executed to complete the sequence.
> >
> > struct papr_rtas_sequence {
> > int error;
> > void params;
> > void (*begin) (struct papr_rtas_sequence *);
> > void (*end) (struct papr_rtas_sequence *);
> > const char * (*work) (struct papr_rtas_sequence *, size_t
> > *);
> > };
> >
> > params: Input parameters used to pass for RTAS call.
> > Begin: RTAS call specific function to initialize data
> > including work area allocation.
> > End: RTAS call specific function to free up resources
> > (free work area) after the sequence is completed.
> > Work: The actual RTAS call specific function which collects
> > the data from the hypervisor.
> >
> > Signed-off-by: Haren Myneni <haren at linux.ibm.com>
> > ---
> > arch/powerpc/platforms/pseries/Makefile | 2 +-
> > .../platforms/pseries/papr-rtas-common.c | 310
> > ++++++++++++++++
> > .../platforms/pseries/papr-rtas-common.h | 61 +++
> > arch/powerpc/platforms/pseries/papr-vpd.c | 351 +++-----------
> > ----
> > 4 files changed, 416 insertions(+), 308 deletions(-)
> > create mode 100644 arch/powerpc/platforms/pseries/papr-rtas-
> > common.c
> > create mode 100644 arch/powerpc/platforms/pseries/papr-rtas-
> > common.h
> >
> > diff --git a/arch/powerpc/platforms/pseries/Makefile
> > b/arch/powerpc/platforms/pseries/Makefile
> > index 7bf506f6b8c8..697c216b70dc 100644
> > --- a/arch/powerpc/platforms/pseries/Makefile
> > +++ b/arch/powerpc/platforms/pseries/Makefile
> > @@ -3,7 +3,7 @@ ccflags-$(CONFIG_PPC_PSERIES_DEBUG) += -DDEBUG
> >
> > obj-y := lpar.o hvCall.o nvram.o reconfig.o \
> > of_helpers.o rtas-work-area.o papr-sysparm.o
> > \
> > - papr-vpd.o \
> > + papr-rtas-common.o papr-vpd.o \
> > setup.o iommu.o event_sources.o ras.o \
> > firmware.o power.o dlpar.o mobility.o rng.o
> > \
> > pci.o pci_dlpar.o eeh_pseries.o msi.o \
> > diff --git a/arch/powerpc/platforms/pseries/papr-rtas-common.c
> > b/arch/powerpc/platforms/pseries/papr-rtas-common.c
> > new file mode 100644
> > index 000000000000..2d0220209de0
> > --- /dev/null
> > +++ b/arch/powerpc/platforms/pseries/papr-rtas-common.c
> > @@ -0,0 +1,310 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +
> > +#define pr_fmt(fmt) "papr-common: " fmt
> > +
> > +#include <linux/types.h>
> > +#include <linux/kernel.h>
> > +#include <linux/signal.h>
> > +#include <linux/slab.h>
> > +#include <linux/file.h>
> > +#include <linux/fs.h>
> > +#include <linux/anon_inodes.h>
> > +#include <linux/sched/signal.h>
> > +#include "papr-rtas-common.h"
> > +
> > +/*
> > + * Sequence based RTAS HCALL has to issue multiple times to
> > retrieve
> > + * complete data from the hypervisor. For some of these RTAS
> > calls,
> > + * the OS should not interleave calls with different input until
> > the
> > + * sequence is completed. So data is collected for these calls
> > during
> > + * ioctl handle and export to user space with read() handle.
> > + * This file provides common functions needed for such sequence
> > based
> > + * RTAS calls Ex: ibm,get-vpd and ibm,get-indices.
> > + */
> > +
> > +bool papr_rtas_blob_has_data(const struct papr_rtas_blob *blob)
> > +{
> > + return blob->data && blob->len;
> > +}
> > +
> > +void papr_rtas_blob_free(const struct papr_rtas_blob *blob)
> > +{
> > + if (blob) {
> > + kvfree(blob->data);
> > + kfree(blob);
> > + }
> > +}
> > +
> > +/**
> > + * papr_rtas_blob_extend() - Append data to a &struct
> > papr_rtas_blob.
> > + * @blob: The blob to extend.
> > + * @data: The new data to append to @blob.
> > + * @len: The length of @data.
> > + *
> > + * Context: May sleep.
> > + * Return: -ENOMEM on allocation failure, 0 otherwise.
> > + */
> > +static int papr_rtas_blob_extend(struct papr_rtas_blob *blob,
> > + const char *data, size_t len)
> > +{
> > + const size_t new_len = blob->len + len;
> > + const size_t old_len = blob->len;
> > + const char *old_ptr = blob->data;
> > + char *new_ptr;
> > +
> > + new_ptr = kvrealloc(old_ptr, new_len, GFP_KERNEL_ACCOUNT);
> > + if (!new_ptr)
> > + return -ENOMEM;
> > +
> > + memcpy(&new_ptr[old_len], data, len);
> > + blob->data = new_ptr;
> > + blob->len = new_len;
> > + return 0;
> > +}
> > +
> > +/**
> > + * papr_rtas_blob_generate() - Construct a new &struct
> > papr_rtas_blob.
> > + * @seq: work function of the caller that is called to obtain
> > + * data with the caller RTAS call.
> > + *
> > + * The @work callback is invoked until it returns NULL. @seq is
> > + * passed to @work in its first argument on each call. When
> > + * @work returns data, it should store the data length in its
> > + * second argument.
> > + *
> > + * Context: May sleep.
> > + * Return: A completely populated &struct papr_rtas_blob, or NULL
> > on error.
> > + */
> > +static const struct papr_rtas_blob *
> > +papr_rtas_blob_generate(struct papr_rtas_sequence *seq)
> > +{
> > + struct papr_rtas_blob *blob;
> > + const char *buf;
> > + size_t len;
> > + int err = 0;
> > +
> > + blob = kzalloc(sizeof(*blob), GFP_KERNEL_ACCOUNT);
> > + if (!blob)
> > + return NULL;
> > +
> > + if (!seq->work)
> > + return ERR_PTR(-EINVAL);
> > +
> > +
> > + while (err == 0 && (buf = seq->work(seq, &len)))
> > + err = papr_rtas_blob_extend(blob, buf, len);
> > +
> > + if (err != 0 || !papr_rtas_blob_has_data(blob))
> > + goto free_blob;
> > +
> > + return blob;
> > +free_blob:
> > + papr_rtas_blob_free(blob);
> > + return NULL;
> > +}
> > +
> > +int papr_rtas_sequence_set_err(struct papr_rtas_sequence *seq, int
> > err)
> > +{
> > + /* Preserve the first error recorded. */
> > + if (seq->error == 0)
> > + seq->error = err;
> > +
> > + return seq->error;
> > +}
> > +
> > +/*
> > + * Higher-level retrieval code below. These functions use the
> > + * papr_rtas_blob_* and sequence_* APIs defined above to create
> > fd-based
> > + * handles for consumption by user space.
> > + */
> > +
> > +/**
> > + * papr_rtas_run_sequence() - Run a single retrieval sequence.
> > + * @seq: Functions of the caller to complete the sequence
> > + *
> > + * Context: May sleep. Holds a mutex and an RTAS work area for its
> > + * duration. Typically performs multiple sleepable slab
> > + * allocations.
> > + *
> > + * Return: A populated &struct papr_rtas_blob on success. Encoded
> > error
> > + * pointer otherwise.
> > + */
> > +static const struct papr_rtas_blob *papr_rtas_run_sequence(struct
> > papr_rtas_sequence *seq)
> > +{
> > + const struct papr_rtas_blob *blob;
> > +
> > + if (seq->begin)
> > + seq->begin(seq);
> > +
> > + blob = papr_rtas_blob_generate(seq);
> > + if (!blob)
> > + papr_rtas_sequence_set_err(seq, -ENOMEM);
> > +
> > + if (seq->end)
> > + seq->end(seq);
> > +
> > +
> > + if (seq->error) {
> > + papr_rtas_blob_free(blob);
> > + return ERR_PTR(seq->error);
> > + }
> > +
> > + return blob;
> > +}
> > +
> > +/**
> > + * papr_rtas_retrieve() - Return the data blob that is exposed to
> > + * user space.
> > + * @seq: RTAS call specific functions to be invoked until the
> > + * sequence is completed.
> > + *
> > + * Run sequences against @param until a blob is successfully
> > + * instantiated, or a hard error is encountered, or a fatal signal
> > is
> > + * pending.
> > + *
> > + * Context: May sleep.
> > + * Return: A fully populated data blob when successful. Encoded
> > error
> > + * pointer otherwise.
> > + */
> > +const struct papr_rtas_blob *papr_rtas_retrieve(struct
> > papr_rtas_sequence *seq)
> > +{
> > + const struct papr_rtas_blob *blob;
> > +
> > + /*
> > + * EAGAIN means the sequence returns error with a -4 (data
> > + * changed and need to start the sequence) status from RTAS
> > calls
> > + * and we should attempt a new sequence. PAPR+ (v2.13
> > R1–7.3.20–5
> > + * - ibm,get-vpd, R1–7.3.17–6 - ibm,get-indices) indicates that
> > + * this should be a transient condition, not something that
> > + * happens continuously. But we'll stop trying on a fatal
> > signal.
> > + */
> > + do {
> > + blob = papr_rtas_run_sequence(seq);
> > + if (!IS_ERR(blob)) /* Success. */
> > + break;
> > + if (PTR_ERR(blob) != -EAGAIN) /* Hard error. */
> > + break;
> > + cond_resched();
> > + } while (!fatal_signal_pending(current));
> > +
> > + return blob;
> > +}
> > +
> > +/**
> > + * papr_rtas_setup_file_interface - Complete the sequence and
> > obtain
> > + * the data and export to user space with fd-based handles. Then
> > the
> > + * user spave gets the data with read() handle.
> > + * @seq: RTAS call specific functions to get the data.
> > + * @fops: RTAS call specific file operations such as read().
> > + * @name: RTAS call specific char device node.
> > + *
> > + * Return: FD handle for consumption by user space
> > + */
> > +long papr_rtas_setup_file_interface(struct papr_rtas_sequence
> > *seq,
> > + const struct file_operations *fops,
> > + char *name)
> > +{
> > + const struct papr_rtas_blob *blob;
> > + struct file *file;
> > + long ret;
> > + int fd;
> > +
> > + blob = papr_rtas_retrieve(seq);
> > + if (IS_ERR(blob))
> > + return PTR_ERR(blob);
> > +
> > + fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
> > + if (fd < 0) {
> > + ret = fd;
> > + goto free_blob;
> > + }
> > +
> > + file = anon_inode_getfile(name, fops, (void *)blob, O_RDONLY);
> > + if (IS_ERR(file)) {
> > + ret = PTR_ERR(file);
> > + goto put_fd;
> > + }
> > +
> > + file->f_mode |= FMODE_LSEEK | FMODE_PREAD;
> > + fd_install(fd, file);
> > + return fd;
> > +
> > +put_fd:
> > + put_unused_fd(fd);
> > +free_blob:
> > + papr_rtas_blob_free(blob);
> > + return ret;
> > +}
> > +
> > +/*
> > + * papr_rtas_sequence_should_stop() - Determine whether RTAS
> > retrieval
> > + * sequence should continue.
> > + *
> > + * Examines the sequence error state and outputs of the last call
> > to
> > + * the specific RTAS to determine whether the sequence in progress
> > + * should continue or stop.
> > + *
> > + * Return: True if the sequence has encountered an error or if all
> > data
> > + * for this sequence has been retrieved. False otherwise.
> > + */
> > +bool papr_rtas_sequence_should_stop(const struct
> > papr_rtas_sequence *seq,
> > + s32 status, bool init_state)
> > +{
> > + bool done;
> > +
> > + if (seq->error)
> > + return true;
> > +
> > + switch (status) {
> > + case RTAS_SEQ_COMPLETE:
> > + if (init_state)
> > + done = false; /* Initial state. */
> > + else
> > + done = true; /* All data consumed. */
> > + break;
> > + case RTAS_SEQ_MORE_DATA:
> > + done = false; /* More data available. */
> > + break;
> > + default:
> > + done = true; /* Error encountered. */
> > + break;
> > + }
> > +
> > + return done;
> > +}
> > +
> > +/*
> > + * User space read to retrieve data for the corresponding RTAS
> > call.
> > + * papr_rtas_blob is filled with the data using the corresponding
> > RTAS
> > + * call sequence API.
> > + */
> > +ssize_t papr_rtas_common_handle_read(struct file *file,
> > + char __user *buf, size_t size, loff_t *off)
> > +{
> > + const struct papr_rtas_blob *blob = file->private_data;
> > +
> > + /* bug: we should not instantiate a handle without any data
> > attached. */ if (!papr_rtas_blob_has_data(blob)) {
> Line break is missing here.
Thanks for finding it. I did not find this with checkpatch initially.
Looks like made this mistake in v4 revision.
More information about the Linuxppc-dev
mailing list