Skip to content

Commit c973198

Browse files
Rishabh Bhatnagarandersson
Rishabh Bhatnagar
authored andcommitted
remoteproc: Add inline coredump functionality
The current coredump implementation uses vmalloc area to copy all the segments. But this might put strain on low memory targets as the firmware size sometimes is in tens of MBs. The situation becomes worse if there are multiple remote processors undergoing recovery at the same time. This patch adds inline coredump functionality that avoids extra memory usage. This requires recovery to be halted until data is read by userspace and free function is called. Reviewed-by: Bjorn Andersson <[email protected]> Reviewed-by: Sibi Sankar <[email protected]> Reviewed-by: Mathieu Poirier <[email protected]> Signed-off-by: Rishabh Bhatnagar <[email protected]> Tested-by: Sibi Sankar <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Bjorn Andersson <[email protected]>
1 parent 76abf9c commit c973198

File tree

2 files changed

+154
-18
lines changed

2 files changed

+154
-18
lines changed

drivers/remoteproc/remoteproc_coredump.c

+138-18
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,20 @@
55
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
66
*/
77

8+
#include <linux/completion.h>
89
#include <linux/devcoredump.h>
910
#include <linux/device.h>
1011
#include <linux/kernel.h>
1112
#include <linux/remoteproc.h>
1213
#include "remoteproc_internal.h"
1314
#include "remoteproc_elf_helpers.h"
1415

16+
struct rproc_coredump_state {
17+
struct rproc *rproc;
18+
void *header;
19+
struct completion dump_done;
20+
};
21+
1522
/**
1623
* rproc_coredump_cleanup() - clean up dump_segments list
1724
* @rproc: the remote processor handle
@@ -115,12 +122,110 @@ int rproc_coredump_set_elf_info(struct rproc *rproc, u8 class, u16 machine)
115122
}
116123
EXPORT_SYMBOL(rproc_coredump_set_elf_info);
117124

125+
static void rproc_coredump_free(void *data)
126+
{
127+
struct rproc_coredump_state *dump_state = data;
128+
129+
vfree(dump_state->header);
130+
complete(&dump_state->dump_done);
131+
}
132+
133+
static void *rproc_coredump_find_segment(loff_t user_offset,
134+
struct list_head *segments,
135+
size_t *data_left)
136+
{
137+
struct rproc_dump_segment *segment;
138+
139+
list_for_each_entry(segment, segments, node) {
140+
if (user_offset < segment->size) {
141+
*data_left = segment->size - user_offset;
142+
return segment;
143+
}
144+
user_offset -= segment->size;
145+
}
146+
147+
*data_left = 0;
148+
return NULL;
149+
}
150+
151+
static void rproc_copy_segment(struct rproc *rproc, void *dest,
152+
struct rproc_dump_segment *segment,
153+
size_t offset, size_t size)
154+
{
155+
void *ptr;
156+
157+
if (segment->dump) {
158+
segment->dump(rproc, segment, dest, offset, size);
159+
} else {
160+
ptr = rproc_da_to_va(rproc, segment->da + offset, size);
161+
if (!ptr) {
162+
dev_err(&rproc->dev,
163+
"invalid copy request for segment %pad with offset %zu and size %zu)\n",
164+
&segment->da, offset, size);
165+
memset(dest, 0xff, size);
166+
} else {
167+
memcpy(dest, ptr, size);
168+
}
169+
}
170+
}
171+
172+
static ssize_t rproc_coredump_read(char *buffer, loff_t offset, size_t count,
173+
void *data, size_t header_sz)
174+
{
175+
size_t seg_data, bytes_left = count;
176+
ssize_t copy_sz;
177+
struct rproc_dump_segment *seg;
178+
struct rproc_coredump_state *dump_state = data;
179+
struct rproc *rproc = dump_state->rproc;
180+
void *elfcore = dump_state->header;
181+
182+
/* Copy the vmalloc'ed header first. */
183+
if (offset < header_sz) {
184+
copy_sz = memory_read_from_buffer(buffer, count, &offset,
185+
elfcore, header_sz);
186+
187+
return copy_sz;
188+
}
189+
190+
/*
191+
* Find out the segment memory chunk to be copied based on offset.
192+
* Keep copying data until count bytes are read.
193+
*/
194+
while (bytes_left) {
195+
seg = rproc_coredump_find_segment(offset - header_sz,
196+
&rproc->dump_segments,
197+
&seg_data);
198+
/* EOF check */
199+
if (!seg) {
200+
dev_info(&rproc->dev, "Ramdump done, %lld bytes read",
201+
offset);
202+
break;
203+
}
204+
205+
copy_sz = min_t(size_t, bytes_left, seg_data);
206+
207+
rproc_copy_segment(rproc, buffer, seg, seg->size - seg_data,
208+
copy_sz);
209+
210+
offset += copy_sz;
211+
buffer += copy_sz;
212+
bytes_left -= copy_sz;
213+
}
214+
215+
return count - bytes_left;
216+
}
217+
118218
/**
119219
* rproc_coredump() - perform coredump
120220
* @rproc: rproc handle
121221
*
122222
* This function will generate an ELF header for the registered segments
123-
* and create a devcoredump device associated with rproc.
223+
* and create a devcoredump device associated with rproc. Based on the
224+
* coredump configuration this function will directly copy the segments
225+
* from device memory to userspace or copy segments from device memory to
226+
* a separate buffer, which can then be read by userspace.
227+
* The first approach avoids using extra vmalloc memory. But it will stall
228+
* recovery flow until dump is read by userspace.
124229
*/
125230
void rproc_coredump(struct rproc *rproc)
126231
{
@@ -130,11 +235,13 @@ void rproc_coredump(struct rproc *rproc)
130235
size_t data_size;
131236
size_t offset;
132237
void *data;
133-
void *ptr;
134238
u8 class = rproc->elf_class;
135239
int phnum = 0;
240+
struct rproc_coredump_state dump_state;
241+
enum rproc_dump_mechanism dump_conf = rproc->dump_conf;
136242

137-
if (list_empty(&rproc->dump_segments))
243+
if (list_empty(&rproc->dump_segments) ||
244+
dump_conf == RPROC_COREDUMP_DISABLED)
138245
return;
139246

140247
if (class == ELFCLASSNONE) {
@@ -144,7 +251,14 @@ void rproc_coredump(struct rproc *rproc)
144251

145252
data_size = elf_size_of_hdr(class);
146253
list_for_each_entry(segment, &rproc->dump_segments, node) {
147-
data_size += elf_size_of_phdr(class) + segment->size;
254+
/*
255+
* For default configuration buffer includes headers & segments.
256+
* For inline dump buffer just includes headers as segments are
257+
* directly read from device memory.
258+
*/
259+
data_size += elf_size_of_phdr(class);
260+
if (dump_conf == RPROC_COREDUMP_DEFAULT)
261+
data_size += segment->size;
148262

149263
phnum++;
150264
}
@@ -183,23 +297,29 @@ void rproc_coredump(struct rproc *rproc)
183297
elf_phdr_set_p_flags(class, phdr, PF_R | PF_W | PF_X);
184298
elf_phdr_set_p_align(class, phdr, 0);
185299

186-
if (segment->dump) {
187-
segment->dump(rproc, segment, data + offset, 0, segment->size);
188-
} else {
189-
ptr = rproc_da_to_va(rproc, segment->da, segment->size);
190-
if (!ptr) {
191-
dev_err(&rproc->dev,
192-
"invalid coredump segment (%pad, %zu)\n",
193-
&segment->da, segment->size);
194-
memset(data + offset, 0xff, segment->size);
195-
} else {
196-
memcpy(data + offset, ptr, segment->size);
197-
}
198-
}
300+
if (dump_conf == RPROC_COREDUMP_DEFAULT)
301+
rproc_copy_segment(rproc, data + offset, segment, 0,
302+
segment->size);
199303

200304
offset += elf_phdr_get_p_filesz(class, phdr);
201305
phdr += elf_size_of_phdr(class);
202306
}
307+
if (dump_conf == RPROC_COREDUMP_DEFAULT) {
308+
dev_coredumpv(&rproc->dev, data, data_size, GFP_KERNEL);
309+
return;
310+
}
311+
312+
/* Initialize the dump state struct to be used by rproc_coredump_read */
313+
dump_state.rproc = rproc;
314+
dump_state.header = data;
315+
init_completion(&dump_state.dump_done);
316+
317+
dev_coredumpm(&rproc->dev, NULL, &dump_state, data_size, GFP_KERNEL,
318+
rproc_coredump_read, rproc_coredump_free);
203319

204-
dev_coredumpv(&rproc->dev, data, data_size, GFP_KERNEL);
320+
/*
321+
* Wait until the dump is read and free is called. Data is freed
322+
* by devcoredump framework automatically after 5 minutes.
323+
*/
324+
wait_for_completion(&dump_state.dump_done);
205325
}

include/linux/remoteproc.h

+16
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,20 @@ enum rproc_crash_type {
439439
RPROC_FATAL_ERROR,
440440
};
441441

442+
/**
443+
* enum rproc_dump_mechanism - Coredump options for core
444+
* @RPROC_COREDUMP_DEFAULT: Copy dump to separate buffer and carry on with
445+
recovery
446+
* @RPROC_COREDUMP_INLINE: Read segments directly from device memory. Stall
447+
recovery until all segments are read
448+
* @RPROC_COREDUMP_DISABLED: Don't perform any dump
449+
*/
450+
enum rproc_dump_mechanism {
451+
RPROC_COREDUMP_DEFAULT,
452+
RPROC_COREDUMP_INLINE,
453+
RPROC_COREDUMP_DISABLED,
454+
};
455+
442456
/**
443457
* struct rproc_dump_segment - segment info from ELF header
444458
* @node: list node related to the rproc segment list
@@ -471,6 +485,7 @@ struct rproc_dump_segment {
471485
* @dev: virtual device for refcounting and common remoteproc behavior
472486
* @power: refcount of users who need this rproc powered up
473487
* @state: state of the device
488+
* @dump_conf: Currently selected coredump configuration
474489
* @lock: lock which protects concurrent manipulations of the rproc
475490
* @dbg_dir: debugfs directory of this rproc device
476491
* @traces: list of trace buffers
@@ -505,6 +520,7 @@ struct rproc {
505520
struct device dev;
506521
atomic_t power;
507522
unsigned int state;
523+
enum rproc_dump_mechanism dump_conf;
508524
struct mutex lock;
509525
struct dentry *dbg_dir;
510526
struct list_head traces;

0 commit comments

Comments
 (0)