[PATCH v8 1/7] powerpc/pseries: Define common functions for RTAS sequence calls
Mukesh Kumar Chaurasiya
mchauras at linux.ibm.com
Wed Mar 12 16:25:48 AEDT 2025
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.
> + pr_err_once("handle without data\n");
> + return -EIO;
> + }
> +
> + return simple_read_from_buffer(buf, size, off, blob->data, blob->len);
> +}
> +
> +int papr_rtas_common_handle_release(struct inode *inode,
> + struct file *file)
> +{
> + const struct papr_rtas_blob *blob = file->private_data;
> +
> + papr_rtas_blob_free(blob);
> +
> + return 0;
> +}
> +
> +loff_t papr_rtas_common_handle_seek(struct file *file, loff_t off,
> + int whence)
> +{
> + const struct papr_rtas_blob *blob = file->private_data;
> +
> + return fixed_size_llseek(file, off, whence, blob->len);
> +}
> diff --git a/arch/powerpc/platforms/pseries/papr-rtas-common.h b/arch/powerpc/platforms/pseries/papr-rtas-common.h
> new file mode 100644
> index 000000000000..4ceabcaf4905
> --- /dev/null
> +++ b/arch/powerpc/platforms/pseries/papr-rtas-common.h
> @@ -0,0 +1,61 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +#ifndef _ASM_POWERPC_PAPR_RTAS_COMMON_H
> +#define _ASM_POWERPC_PAPR_RTAS_COMMON_H
> +
> +#include <linux/types.h>
> +
> +/*
> + * Return codes for sequence based RTAS calls.
> + * Not listed under PAPR+ v2.13 7.2.8: "Return Codes".
> + * But defined in the specific section of each RTAS call.
> + */
> +#define RTAS_SEQ_COMPLETE 0 /* All data has been retrieved. */
> +#define RTAS_SEQ_MORE_DATA 1 /* More data is available */
> +#define RTAS_SEQ_START_OVER -4 /* Data changed, restart call sequence. */
> +
> +/*
> + * Internal "blob" APIs for accumulating RTAS call results into
> + * an immutable buffer to be attached to a file descriptor.
> + */
> +struct papr_rtas_blob {
> + const char *data;
> + size_t len;
> +};
> +
> +/**
> + * struct papr_sequence - State for managing a sequence of RTAS calls.
> + * @error: Shall be zero as long as the sequence has not encountered an error,
> + * -ve errno otherwise. Use papr_rtas_sequence_set_err() to update.
> + * @params: Parameter block to pass to rtas_*() calls.
> + * @begin: Work area allocation and initialize the needed parameter
> + * values passed to RTAS call
> + * @end: Free the allocated work area
> + * @work: Obtain data with RTAS call and invoke it until the sequence is
> + * completed.
> + *
> + */
> +struct papr_rtas_sequence {
> + int error;
> + void *params;
> + void (*begin)(struct papr_rtas_sequence *seq);
> + void (*end)(struct papr_rtas_sequence *seq);
> + const char *(*work)(struct papr_rtas_sequence *seq, size_t *len);
> +};
> +
> +extern bool papr_rtas_blob_has_data(const struct papr_rtas_blob *blob);
> +extern void papr_rtas_blob_free(const struct papr_rtas_blob *blob);
> +extern int papr_rtas_sequence_set_err(struct papr_rtas_sequence *seq,
> + int err);
> +extern const struct papr_rtas_blob *papr_rtas_retrieve(struct papr_rtas_sequence *seq);
> +extern long papr_rtas_setup_file_interface(struct papr_rtas_sequence *seq,
> + const struct file_operations *fops, char *name);
> +extern bool papr_rtas_sequence_should_stop(const struct papr_rtas_sequence *seq,
> + s32 status, bool init_state);
> +extern ssize_t papr_rtas_common_handle_read(struct file *file,
> + char __user *buf, size_t size, loff_t *off);
> +extern int papr_rtas_common_handle_release(struct inode *inode,
> + struct file *file);
> +extern loff_t papr_rtas_common_handle_seek(struct file *file, loff_t off,
> + int whence);
> +#endif /* _ASM_POWERPC_PAPR_RTAS_COMMON_H */
> +
> diff --git a/arch/powerpc/platforms/pseries/papr-vpd.c b/arch/powerpc/platforms/pseries/papr-vpd.c
> index 1574176e3ffc..f38c188fc4a1 100644
> --- a/arch/powerpc/platforms/pseries/papr-vpd.c
> +++ b/arch/powerpc/platforms/pseries/papr-vpd.c
> @@ -2,7 +2,6 @@
>
> #define pr_fmt(fmt) "papr-vpd: " fmt
>
> -#include <linux/anon_inodes.h>
> #include <linux/build_bug.h>
> #include <linux/file.h>
> #include <linux/fs.h>
> @@ -20,14 +19,7 @@
> #include <asm/rtas-work-area.h>
> #include <asm/rtas.h>
> #include <uapi/asm/papr-vpd.h>
> -
> -/*
> - * Function-specific return values for ibm,get-vpd, derived from PAPR+
> - * v2.13 7.3.20 "ibm,get-vpd RTAS Call".
> - */
> -#define RTAS_IBM_GET_VPD_COMPLETE 0 /* All VPD has been retrieved. */
> -#define RTAS_IBM_GET_VPD_MORE_DATA 1 /* More VPD is available. */
> -#define RTAS_IBM_GET_VPD_START_OVER -4 /* VPD changed, restart call sequence. */
> +#include "papr-rtas-common.h"
>
> /**
> * struct rtas_ibm_get_vpd_params - Parameters (in and out) for ibm,get-vpd.
> @@ -91,13 +83,14 @@ static int rtas_ibm_get_vpd(struct rtas_ibm_get_vpd_params *params)
> case RTAS_INVALID_PARAMETER:
> ret = -EINVAL;
> break;
> - case RTAS_IBM_GET_VPD_START_OVER:
> + case RTAS_SEQ_START_OVER:
> ret = -EAGAIN;
> + pr_info_ratelimited("VPD changed during retrieval, retrying\n");
> break;
> - case RTAS_IBM_GET_VPD_MORE_DATA:
> + case RTAS_SEQ_MORE_DATA:
> params->sequence = rets[0];
> fallthrough;
> - case RTAS_IBM_GET_VPD_COMPLETE:
> + case RTAS_SEQ_COMPLETE:
> params->written = rets[1];
> /*
> * Kernel or firmware bug, do not continue.
> @@ -118,91 +111,6 @@ static int rtas_ibm_get_vpd(struct rtas_ibm_get_vpd_params *params)
> return ret;
> }
>
> -/*
> - * Internal VPD "blob" APIs for accumulating ibm,get-vpd results into
> - * an immutable buffer to be attached to a file descriptor.
> - */
> -struct vpd_blob {
> - const char *data;
> - size_t len;
> -};
> -
> -static bool vpd_blob_has_data(const struct vpd_blob *blob)
> -{
> - return blob->data && blob->len;
> -}
> -
> -static void vpd_blob_free(const struct vpd_blob *blob)
> -{
> - if (blob) {
> - kvfree(blob->data);
> - kfree(blob);
> - }
> -}
> -
> -/**
> - * vpd_blob_extend() - Append data to a &struct vpd_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 vpd_blob_extend(struct vpd_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;
> -}
> -
> -/**
> - * vpd_blob_generate() - Construct a new &struct vpd_blob.
> - * @generator: Function that supplies the blob data.
> - * @arg: Context pointer supplied by caller, passed to @generator.
> - *
> - * The @generator callback is invoked until it returns NULL. @arg is
> - * passed to @generator in its first argument on each call. When
> - * @generator returns data, it should store the data length in its
> - * second argument.
> - *
> - * Context: May sleep.
> - * Return: A completely populated &struct vpd_blob, or NULL on error.
> - */
> -static const struct vpd_blob *
> -vpd_blob_generate(const char * (*generator)(void *, size_t *), void *arg)
> -{
> - struct vpd_blob *blob;
> - const char *buf;
> - size_t len;
> - int err = 0;
> -
> - blob = kzalloc(sizeof(*blob), GFP_KERNEL_ACCOUNT);
> - if (!blob)
> - return NULL;
> -
> - while (err == 0 && (buf = generator(arg, &len)))
> - err = vpd_blob_extend(blob, buf, len);
> -
> - if (err != 0 || !vpd_blob_has_data(blob))
> - goto free_blob;
> -
> - return blob;
> -free_blob:
> - vpd_blob_free(blob);
> - return NULL;
> -}
> -
> /*
> * Internal VPD sequence APIs. A VPD sequence is a series of calls to
> * ibm,get-vpd for a given location code. The sequence ends when an
> @@ -210,31 +118,15 @@ vpd_blob_generate(const char * (*generator)(void *, size_t *), void *arg)
> * returned.
> */
>
> -/**
> - * struct vpd_sequence - State for managing a VPD sequence.
> - * @error: Shall be zero as long as the sequence has not encountered an error,
> - * -ve errno otherwise. Use vpd_sequence_set_err() to update this.
> - * @params: Parameter block to pass to rtas_ibm_get_vpd().
> - */
> -struct vpd_sequence {
> - int error;
> - struct rtas_ibm_get_vpd_params params;
> -};
> -
> /**
> * vpd_sequence_begin() - Begin a VPD retrieval sequence.
> - * @seq: Uninitialized sequence state.
> - * @loc_code: Location code that defines the scope of the VPD to return.
> - *
> - * Initializes @seq with the resources necessary to carry out a VPD
> - * sequence. Callers must pass @seq to vpd_sequence_end() regardless
> - * of whether the sequence succeeds.
> + * @seq: vpd call parameters from sequence struct
> *
> * Context: May sleep.
> */
> -static void vpd_sequence_begin(struct vpd_sequence *seq,
> - const struct papr_location_code *loc_code)
> +static void vpd_sequence_begin(struct papr_rtas_sequence *seq)
> {
> + struct rtas_ibm_get_vpd_params *vpd_params;
> /*
> * Use a static data structure for the location code passed to
> * RTAS to ensure it's in the RMA and avoid a separate work
> @@ -242,6 +134,7 @@ static void vpd_sequence_begin(struct vpd_sequence *seq,
> */
> static struct papr_location_code static_loc_code;
>
> + vpd_params = (struct rtas_ibm_get_vpd_params *)seq->params;
> /*
> * We could allocate the work area before acquiring the
> * function lock, but that would allow concurrent requests to
> @@ -249,14 +142,12 @@ static void vpd_sequence_begin(struct vpd_sequence *seq,
> * allocate the work area under the lock.
> */
> mutex_lock(&rtas_ibm_get_vpd_lock);
> - static_loc_code = *loc_code;
> - *seq = (struct vpd_sequence) {
> - .params = {
> - .work_area = rtas_work_area_alloc(SZ_4K),
> - .loc_code = &static_loc_code,
> - .sequence = 1,
> - },
> - };
> + static_loc_code = *(struct papr_location_code *)vpd_params->loc_code;
> + vpd_params = (struct rtas_ibm_get_vpd_params *)seq->params;
> + vpd_params->work_area = rtas_work_area_alloc(SZ_4K);
> + vpd_params->loc_code = &static_loc_code;
> + vpd_params->sequence = 1;
> + vpd_params->status = 0;
> }
>
> /**
> @@ -265,180 +156,39 @@ static void vpd_sequence_begin(struct vpd_sequence *seq,
> *
> * Releases resources obtained by vpd_sequence_begin().
> */
> -static void vpd_sequence_end(struct vpd_sequence *seq)
> -{
> - rtas_work_area_free(seq->params.work_area);
> - mutex_unlock(&rtas_ibm_get_vpd_lock);
> -}
> -
> -/**
> - * vpd_sequence_should_stop() - Determine whether a VPD retrieval sequence
> - * should continue.
> - * @seq: VPD sequence state.
> - *
> - * Examines the sequence error state and outputs of the last call to
> - * ibm,get-vpd to determine whether the sequence in progress should
> - * continue or stop.
> - *
> - * Return: True if the sequence has encountered an error or if all VPD for
> - * this sequence has been retrieved. False otherwise.
> - */
> -static bool vpd_sequence_should_stop(const struct vpd_sequence *seq)
> -{
> - bool done;
> -
> - if (seq->error)
> - return true;
> -
> - switch (seq->params.status) {
> - case 0:
> - if (seq->params.written == 0)
> - done = false; /* Initial state. */
> - else
> - done = true; /* All data consumed. */
> - break;
> - case 1:
> - done = false; /* More data available. */
> - break;
> - default:
> - done = true; /* Error encountered. */
> - break;
> - }
> -
> - return done;
> -}
> -
> -static int vpd_sequence_set_err(struct vpd_sequence *seq, int err)
> +static void vpd_sequence_end(struct papr_rtas_sequence *seq)
> {
> - /* Preserve the first error recorded. */
> - if (seq->error == 0)
> - seq->error = err;
> + struct rtas_ibm_get_vpd_params *vpd_params;
>
> - return seq->error;
> + vpd_params = (struct rtas_ibm_get_vpd_params *)seq->params;
> + rtas_work_area_free(vpd_params->work_area);
> + mutex_unlock(&rtas_ibm_get_vpd_lock);
> }
>
> /*
> - * Generator function to be passed to vpd_blob_generate().
> + * Generator function to be passed to papr_rtas_blob_generate().
> */
> -static const char *vpd_sequence_fill_work_area(void *arg, size_t *len)
> +static const char *vpd_sequence_fill_work_area(struct papr_rtas_sequence *seq,
> + size_t *len)
> {
> - struct vpd_sequence *seq = arg;
> - struct rtas_ibm_get_vpd_params *p = &seq->params;
> + struct rtas_ibm_get_vpd_params *p;
> + bool init_state;
> +
> + p = (struct rtas_ibm_get_vpd_params *)seq->params;
> + init_state = (p->written == 0) ? true : false;
>
> - if (vpd_sequence_should_stop(seq))
> + if (papr_rtas_sequence_should_stop(seq, p->status, init_state))
> return NULL;
> - if (vpd_sequence_set_err(seq, rtas_ibm_get_vpd(p)))
> + if (papr_rtas_sequence_set_err(seq, rtas_ibm_get_vpd(p)))
> return NULL;
> *len = p->written;
> return rtas_work_area_raw_buf(p->work_area);
> }
>
> -/*
> - * Higher-level VPD retrieval code below. These functions use the
> - * vpd_blob_* and vpd_sequence_* APIs defined above to create fd-based
> - * VPD handles for consumption by user space.
> - */
> -
> -/**
> - * papr_vpd_run_sequence() - Run a single VPD retrieval sequence.
> - * @loc_code: Location code that defines the scope of VPD to return.
> - *
> - * Context: May sleep. Holds a mutex and an RTAS work area for its
> - * duration. Typically performs multiple sleepable slab
> - * allocations.
> - *
> - * Return: A populated &struct vpd_blob on success. Encoded error
> - * pointer otherwise.
> - */
> -static const struct vpd_blob *papr_vpd_run_sequence(const struct papr_location_code *loc_code)
> -{
> - const struct vpd_blob *blob;
> - struct vpd_sequence seq;
> -
> - vpd_sequence_begin(&seq, loc_code);
> - blob = vpd_blob_generate(vpd_sequence_fill_work_area, &seq);
> - if (!blob)
> - vpd_sequence_set_err(&seq, -ENOMEM);
> - vpd_sequence_end(&seq);
> -
> - if (seq.error) {
> - vpd_blob_free(blob);
> - return ERR_PTR(seq.error);
> - }
> -
> - return blob;
> -}
> -
> -/**
> - * papr_vpd_retrieve() - Return the VPD for a location code.
> - * @loc_code: Location code that defines the scope of VPD to return.
> - *
> - * Run VPD sequences against @loc_code 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 VPD blob when successful. Encoded error
> - * pointer otherwise.
> - */
> -static const struct vpd_blob *papr_vpd_retrieve(const struct papr_location_code *loc_code)
> -{
> - const struct vpd_blob *blob;
> -
> - /*
> - * EAGAIN means the sequence errored with a -4 (VPD changed)
> - * status from ibm,get-vpd, and we should attempt a new
> - * sequence. PAPR+ v2.13 R1–7.3.20–5 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_vpd_run_sequence(loc_code);
> - if (!IS_ERR(blob)) /* Success. */
> - break;
> - if (PTR_ERR(blob) != -EAGAIN) /* Hard error. */
> - break;
> - pr_info_ratelimited("VPD changed during retrieval, retrying\n");
> - cond_resched();
> - } while (!fatal_signal_pending(current));
> -
> - return blob;
> -}
> -
> -static ssize_t papr_vpd_handle_read(struct file *file, char __user *buf, size_t size, loff_t *off)
> -{
> - const struct vpd_blob *blob = file->private_data;
> -
> - /* bug: we should not instantiate a handle without any data attached. */
> - if (!vpd_blob_has_data(blob)) {
> - pr_err_once("handle without data\n");
> - return -EIO;
> - }
> -
> - return simple_read_from_buffer(buf, size, off, blob->data, blob->len);
> -}
> -
> -static int papr_vpd_handle_release(struct inode *inode, struct file *file)
> -{
> - const struct vpd_blob *blob = file->private_data;
> -
> - vpd_blob_free(blob);
> -
> - return 0;
> -}
> -
> -static loff_t papr_vpd_handle_seek(struct file *file, loff_t off, int whence)
> -{
> - const struct vpd_blob *blob = file->private_data;
> -
> - return fixed_size_llseek(file, off, whence, blob->len);
> -}
> -
> -
> static const struct file_operations papr_vpd_handle_ops = {
> - .read = papr_vpd_handle_read,
> - .llseek = papr_vpd_handle_seek,
> - .release = papr_vpd_handle_release,
> + .read = papr_rtas_common_handle_read,
> + .llseek = papr_rtas_common_handle_seek,
> + .release = papr_rtas_common_handle_release,
> };
>
> /**
> @@ -460,10 +210,9 @@ static const struct file_operations papr_vpd_handle_ops = {
> */
> static long papr_vpd_create_handle(struct papr_location_code __user *ulc)
> {
> + struct rtas_ibm_get_vpd_params vpd_params = {};
> + struct papr_rtas_sequence seq = {};
> struct papr_location_code klc;
> - const struct vpd_blob *blob;
> - struct file *file;
> - long err;
> int fd;
>
> if (copy_from_user(&klc, ulc, sizeof(klc)))
> @@ -472,31 +221,19 @@ static long papr_vpd_create_handle(struct papr_location_code __user *ulc)
> if (!string_is_terminated(klc.str, ARRAY_SIZE(klc.str)))
> return -EINVAL;
>
> - blob = papr_vpd_retrieve(&klc);
> - if (IS_ERR(blob))
> - return PTR_ERR(blob);
> + seq = (struct papr_rtas_sequence) {
> + .begin = vpd_sequence_begin,
> + .end = vpd_sequence_end,
> + .work = vpd_sequence_fill_work_area,
> + };
>
> - fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
> - if (fd < 0) {
> - err = fd;
> - goto free_blob;
> - }
> + vpd_params.loc_code = &klc;
> + seq.params = (void *)&vpd_params;
>
> - file = anon_inode_getfile("[papr-vpd]", &papr_vpd_handle_ops,
> - (void *)blob, O_RDONLY);
> - if (IS_ERR(file)) {
> - err = PTR_ERR(file);
> - goto put_fd;
> - }
> + fd = papr_rtas_setup_file_interface(&seq, &papr_vpd_handle_ops,
> + "[papr-vpd]");
>
> - file->f_mode |= FMODE_LSEEK | FMODE_PREAD;
> - fd_install(fd, file);
> return fd;
> -put_fd:
> - put_unused_fd(fd);
> -free_blob:
> - vpd_blob_free(blob);
> - return err;
> }
>
> /*
> --
> 2.43.5
>
>
More information about the Linuxppc-dev
mailing list