1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
|
// 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);
}
|