[PATCH v7 04/11] sframe: Separate reading of FRE from reading of FRE data words
From: Dylan Hatch
Date: Fri Sep 18 2026 - 18:44:00 EST
From: Jens Remus <jremus@xxxxxxxxxxxxx>
__find_fre() performs linear search for a matching SFrame FRE for a
given IP. For that purpose it uses __read_fre(), which reads the whole
FRE. That is the variable-size FRE structure as well as the trailing
variable-length array of variable-size data words. For the search logic
to skip over the FRE it would be sufficient to read the variable-size
FRE structure only, which includes the count and size of data words.
Add fields to struct sframe_fre_internal to store the FRE data word's
address, count, and size. Change __read_fre() to read the variable-
size FRE structure only and populate those new fields. Change
__read_fre_datawords() to use those new fields. Change __find_fre()
to use __read_fre_datawords() to read the FRE data words only after a
matching FRE has been found.
[ Dylan Hatch: Adapt for in-kernel unwinding without flexible FDEs. ]
Reviewed-by: Indu Bhagat <ibhagatgnu@xxxxxxxxx>
Signed-off-by: Jens Remus <jremus@xxxxxxxxxxxxx>
Signed-off-by: Steven Rostedt <rostedt@xxxxxxxxxxx>
Signed-off-by: Dylan Hatch <dylanbhatch@xxxxxxxxxx>
---
This patch is adapted from commit 745489faa10c ("unwind_user/sframe:
Separate reading of FRE from reading of FRE data words") and squashes
changes from:
- fdaf91d4fc0d ("unwind_user/sframe: Add support for SFrame V3
flexible FDEs")
- 79313210e44e ("unwind_user/sframe: Add support for outermost frame
indication")
all of which are from Steven's sframe/core branch.
Changes include:
- Drop support for flexible FDEs (SFRAME_FDE_TYPE_FLEX).
- Sqaush in creation of __read_default_fre_datawords() and
__read_fre_datawords().
- Squash in outermost frame handling logic.
- Deferred sframe_validate_section() changes to a later patch.
---
kernel/unwind/sframe.c | 97 +++++++++++++++++++++++++++++++-----------
1 file changed, 73 insertions(+), 24 deletions(-)
diff --git a/kernel/unwind/sframe.c b/kernel/unwind/sframe.c
index f3975c7e89493..9aac703f7ce6e 100644
--- a/kernel/unwind/sframe.c
+++ b/kernel/unwind/sframe.c
@@ -36,6 +36,9 @@ struct sframe_fre_internal {
s32 ra_off;
s32 fp_off;
u8 info;
+ unsigned long dw_addr;
+ unsigned char dw_count;
+ unsigned char dw_size;
};
static __always_inline unsigned char fre_type_to_size(unsigned char fre_type)
@@ -179,6 +182,67 @@ static __always_inline int __find_fde(struct sframe_section *sec,
s32 : GET_SIGNED_AND_INC(to, from, size), \
s64 : GET_SIGNED_AND_INC(to, from, size))
+static __always_inline int
+__read_default_fre_datawords(struct sframe_section *sec,
+ struct sframe_fde_internal *fde,
+ struct sframe_fre_internal *fre)
+{
+ unsigned char dataword_count = fre->dw_count;
+ unsigned char dataword_size = fre->dw_size;
+ unsigned long cur = fre->dw_addr;
+ s32 cfa_off, ra_off, fp_off;
+
+ GET_AND_INC(cfa_off, cur, dataword_size);
+ dataword_count--;
+
+ ra_off = sec->ra_off;
+ if (!ra_off && dataword_count) {
+ dataword_count--;
+ GET_AND_INC(ra_off, cur, dataword_size);
+ }
+
+ fp_off = sec->fp_off;
+ if (!fp_off && dataword_count) {
+ dataword_count--;
+ GET_AND_INC(fp_off, cur, dataword_size);
+ }
+
+ if (dataword_count)
+ return -EFAULT;
+
+ fre->cfa_off = cfa_off;
+ fre->ra_off = ra_off;
+ fre->fp_off = fp_off;
+
+ return 0;
+}
+
+static __always_inline int
+__read_fre_datawords(struct sframe_section *sec,
+ struct sframe_fde_internal *fde,
+ struct sframe_fre_internal *fre)
+{
+ unsigned char fde_type = SFRAME_V3_FDE_TYPE(fde->info2);
+ unsigned char dataword_count = fre->dw_count;
+
+ if (!dataword_count) {
+ /* A FRE without data words indicates an outermost frame. */
+ fre->cfa_off = 0;
+ fre->ra_off = 0;
+ fre->fp_off = 0;
+
+ return 0;
+ }
+
+ switch (fde_type) {
+ case SFRAME_FDE_TYPE_DEFAULT:
+ return __read_default_fre_datawords(sec, fde, fre);
+ /* Flexible FDEs not supported */
+ default:
+ return -EFAULT;
+ }
+}
+
static __always_inline int __read_fre(struct sframe_section *sec,
struct sframe_fde_internal *fde,
unsigned long fre_addr,
@@ -188,7 +252,6 @@ static __always_inline int __read_fre(struct sframe_section *sec,
unsigned char fde_pctype = SFRAME_V3_FDE_PCTYPE(fde->info);
unsigned char fre_type = SFRAME_V3_FDE_FRE_TYPE(fde->info);
unsigned char dataword_count, dataword_size;
- s32 cfa_off, ra_off, fp_off;
unsigned long cur = fre_addr;
unsigned char addr_size;
u32 ip_off;
@@ -208,7 +271,7 @@ static __always_inline int __read_fre(struct sframe_section *sec,
GET_AND_INC(info, cur, 1);
dataword_count = SFRAME_V3_FRE_DATAWORD_COUNT(info);
dataword_size = dataword_size_enum_to_size(SFRAME_V3_FRE_DATAWORD_SIZE(info));
- if (!dataword_size || !dataword_count)
+ if (!dataword_size)
return -EFAULT;
if (cur + (dataword_count * dataword_size) > sec->fres_end)
@@ -219,30 +282,11 @@ static __always_inline int __read_fre(struct sframe_section *sec,
return -EFAULT;
fre->size = addr_size + 1 + (dataword_count * dataword_size);
-
- GET_AND_INC(cfa_off, cur, dataword_size);
- dataword_count--;
-
- ra_off = sec->ra_off;
- if (!ra_off && dataword_count) {
- dataword_count--;
- GET_AND_INC(ra_off, cur, dataword_size);
- }
-
- fp_off = sec->fp_off;
- if (!fp_off && dataword_count) {
- dataword_count--;
- GET_AND_INC(fp_off, cur, dataword_size);
- }
-
- if (dataword_count)
- return -EFAULT;
-
fre->ip_off = ip_off;
- fre->cfa_off = cfa_off;
- fre->ra_off = ra_off;
- fre->fp_off = fp_off;
fre->info = info;
+ fre->dw_addr = cur;
+ fre->dw_count = dataword_count;
+ fre->dw_size = dataword_size;
return 0;
}
@@ -292,6 +336,7 @@ static __always_inline int __find_fre(struct sframe_section *sec,
bool which = false;
unsigned int i;
u32 ip_off;
+ int ret;
ip_off = ip - fde->func_addr;
@@ -332,6 +377,10 @@ static __always_inline int __find_fre(struct sframe_section *sec,
return -EINVAL;
fre = prev_fre;
+ ret = __read_fre_datawords(sec, fde, fre);
+ if (ret)
+ return ret;
+
if (sframe_init_cfa_rule_data(&frame->cfa, fre->info, fre->cfa_off))
return -EINVAL;
--
2.55.0.1082.g2b9226bbc0-goog