// 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_fmode(name, fops, (void *)blob,
O_RDONLY, FMODE_LSEEK | FMODE_PREAD);
if (IS_ERR(file)) {
ret = PTR_ERR(file);
goto put_fd;
}
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;
/* We should not instantiate a handle without any data attached. */
if (!papr_rtas_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);
}
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);
}
Messung V0.5 in Prozent C=95 H=93 G=93
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.13Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-28)
¤
*Bot Zugriff