Skip to content

Commit c961c4f

Browse files
committed
Support Rust core::fmt::Argument in vsprintf
Signed-off-by: Gary Guo <[email protected]>
1 parent da31291 commit c961c4f

File tree

4 files changed

+100
-138
lines changed

4 files changed

+100
-138
lines changed

lib/vsprintf.c

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2069,14 +2069,9 @@ char *fwnode_string(char *buf, char *end, struct fwnode_handle *fwnode,
20692069

20702070
str_spec.field_width = -1;
20712071

2072-
if (*fmt != 'w')
2073-
return error_string(buf, end, "(%pf?)", spec);
2074-
20752072
if (check_pointer(&buf, end, fwnode, spec))
20762073
return buf;
20772074

2078-
fmt++;
2079-
20802075
switch (*fmt) {
20812076
case 'P': /* name */
20822077
buf = string(buf, end, fwnode_get_name(fwnode), str_spec);
@@ -2090,6 +2085,19 @@ char *fwnode_string(char *buf, char *end, struct fwnode_handle *fwnode,
20902085
return widen_string(buf, buf - buf_start, end, spec);
20912086
}
20922087

2088+
#ifdef CONFIG_RUST
2089+
char *rust_fmt_argument(char* buf, char* end, void *ptr, int limit);
2090+
2091+
static char *rust_fmt_string(char *buf, char *end, void *ptr,
2092+
struct printf_spec spec)
2093+
{
2094+
char *buf_new;
2095+
buf_new = rust_fmt_argument(buf, end, ptr, spec.precision);
2096+
return widen_string(buf_new, buf_new - buf, end, spec);
2097+
}
2098+
2099+
#endif
2100+
20932101
/* Disable pointer hashing if requested */
20942102
bool no_hash_pointers __ro_after_init;
20952103
EXPORT_SYMBOL_GPL(no_hash_pointers);
@@ -2225,6 +2233,7 @@ early_param("no_hash_pointers", no_hash_pointers_enable);
22252233
* Without an option prints the full name of the node
22262234
* f full name
22272235
* P node name, including a possible unit address
2236+
* - 'fr' For a Rust `core::fmt::Argument` pointer.
22282237
* - 'x' For printing the address. Equivalent to "%lx".
22292238
* - '[ku]s' For a BPF/tracing related format specifier, e.g. used out of
22302239
* bpf_trace_printk() where [ku] prefix specifies either kernel (k)
@@ -2305,7 +2314,16 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
23052314
case 'O':
23062315
return device_node_string(buf, end, ptr, spec, fmt + 1);
23072316
case 'f':
2308-
return fwnode_string(buf, end, ptr, spec, fmt + 1);
2317+
switch (fmt[1]) {
2318+
case 'w':
2319+
return fwnode_string(buf, end, ptr, spec, fmt + 2);
2320+
#ifdef CONFIG_RUST
2321+
case 'r':
2322+
return rust_fmt_string(buf, end, ptr, spec);
2323+
#endif
2324+
default:
2325+
return error_string(buf, end, "(einval)", spec);
2326+
}
23092327
case 'x':
23102328
return pointer_string(buf, end, ptr, spec);
23112329
case 'e':

rust/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ $(objtree)/rust/bindings_generated.rs: $(srctree)/rust/kernel/bindings_helper.h
9999
quiet_cmd_exports = EXPORTS $@
100100
cmd_exports = \
101101
$(NM) -p --defined-only $< \
102-
| grep -E ' (T|R|D) ' | cut -d ' ' -f 3 | grep -E '^(__rust_|_R)' \
102+
| grep -E ' (T|R|D) ' | cut -d ' ' -f 3 \
103103
| xargs -Isymbol \
104104
echo 'EXPORT_SYMBOL_RUST_GPL(symbol);' > $@
105105

rust/kernel/print.rs

Lines changed: 74 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,69 @@ use core::cmp;
1010
use core::fmt;
1111

1212
use crate::bindings;
13-
use crate::c_types::c_int;
13+
use crate::c_types::{c_char, c_int, c_void};
14+
15+
// Called from vsprintf with format specifier %pfr.
16+
#[no_mangle]
17+
unsafe fn rust_fmt_argument(
18+
buf: *mut c_char,
19+
end: *mut c_char,
20+
ptr: *const c_void,
21+
limit: c_int,
22+
) -> *mut c_char {
23+
use fmt::Write;
24+
25+
// Use `usize` to use saturating_* functions.
26+
struct Writer {
27+
buf: usize,
28+
end: usize,
29+
limit: usize,
30+
}
31+
32+
impl Write for Writer {
33+
fn write_str(&mut self, s: &str) -> fmt::Result {
34+
// Maximum possible length to write, bounded by limit.
35+
let len = cmp::min(s.len(), self.limit);
36+
37+
// `buf` value after writing `len` bytes. This does not have to be bounded
38+
// by `end`, but we don't want it to wrap around to 0.
39+
let buf_new = self.buf.saturating_add(len);
40+
41+
// Amount that we can copy. `saturating_sub` ensures we get 0 if
42+
// `buf` goes past `end`.
43+
let len_to_copy = cmp::min(buf_new, self.end).saturating_sub(self.buf);
44+
45+
// SAFETY: In any case, `buf` is non-null and properly aligned.
46+
// If `len_to_copy` is non-zero, then we know `buf` has not past
47+
// `end` yet and so is valid.
48+
unsafe {
49+
core::ptr::copy_nonoverlapping(
50+
s.as_bytes().as_ptr(),
51+
self.buf as *mut u8,
52+
len_to_copy,
53+
)
54+
};
55+
56+
self.buf = buf_new;
57+
if s.len() <= self.limit {
58+
self.limit -= s.len();
59+
Ok(())
60+
} else {
61+
Err(fmt::Error)
62+
}
63+
}
64+
}
65+
66+
let mut w = Writer {
67+
buf: buf as _,
68+
end: end as _,
69+
// On C-side, unlimited is represented as -1, this cast will change it
70+
// to usize::MAX, which serves the same purpose.
71+
limit: limit as _,
72+
};
73+
let _ = w.write_fmt(*(ptr as *const fmt::Arguments));
74+
w.buf as _
75+
}
1476

1577
/// Format strings.
1678
///
@@ -42,9 +104,9 @@ pub mod format_strings {
42104
assert!(prefix[2] == b'\x00');
43105

44106
let suffix: &[u8; LENGTH - LENGTH_PREFIX] = if is_cont {
45-
b"%.*s\0\0\0\0\0"
107+
b"%pfr\0\0\0\0\0"
46108
} else {
47-
b"%s: %.*s\0"
109+
b"%s: %pfr\0"
48110
};
49111

50112
[
@@ -84,14 +146,13 @@ pub mod format_strings {
84146
pub unsafe fn call_printk(
85147
format_string: &[u8; format_strings::LENGTH],
86148
module_name: &[u8],
87-
string: &[u8],
149+
args: fmt::Arguments,
88150
) {
89151
// `printk` does not seem to fail in any path.
90152
bindings::printk(
91153
format_string.as_ptr() as _,
92154
module_name.as_ptr(),
93-
string.len() as c_int,
94-
string.as_ptr(),
155+
&args as *const _ as *const c_void,
95156
);
96157
}
97158

@@ -101,112 +162,25 @@ pub unsafe fn call_printk(
101162
///
102163
/// [`printk`]: ../../../../include/linux/printk.h
103164
#[doc(hidden)]
104-
pub fn call_printk_cont(string: &[u8]) {
165+
pub fn call_printk_cont(args: fmt::Arguments) {
105166
// `printk` does not seem to fail in any path.
106167
//
107168
// SAFETY: The format string is fixed.
108169
unsafe {
109170
bindings::printk(
110171
format_strings::CONT.as_ptr() as _,
111-
string.len() as c_int,
112-
string.as_ptr(),
172+
&args as *const _ as *const c_void,
113173
);
114174
}
115175
}
116176

117-
/// The maximum size of a log line in the kernel.
118-
///
119-
/// From `kernel/printk/printk.c`.
120-
const LOG_LINE_MAX: usize = 1024 - 32;
121-
122-
/// The maximum size of a log line in our side.
123-
///
124-
/// FIXME: We should be smarter than this, but for the moment, to reduce stack
125-
/// usage, we only allow this much which should work for most purposes.
126-
const LOG_LINE_SIZE: usize = 300;
127-
crate::static_assert!(LOG_LINE_SIZE <= LOG_LINE_MAX);
128-
129-
/// Public but hidden since it should only be used from public macros.
130-
#[doc(hidden)]
131-
pub struct LogLineWriter {
132-
data: [u8; LOG_LINE_SIZE],
133-
pos: usize,
134-
}
135-
136-
impl LogLineWriter {
137-
/// Creates a new [`LogLineWriter`].
138-
pub fn new() -> LogLineWriter {
139-
LogLineWriter {
140-
data: [0u8; LOG_LINE_SIZE],
141-
pos: 0,
142-
}
143-
}
144-
145-
/// Returns the internal buffer as a byte slice.
146-
pub fn as_bytes(&self) -> &[u8] {
147-
&self.data[..self.pos]
148-
}
149-
}
150-
151-
impl Default for LogLineWriter {
152-
fn default() -> Self {
153-
Self::new()
154-
}
155-
}
156-
157-
impl fmt::Write for LogLineWriter {
158-
fn write_str(&mut self, s: &str) -> fmt::Result {
159-
let copy_len = cmp::min(LOG_LINE_SIZE - self.pos, s.as_bytes().len());
160-
self.data[self.pos..self.pos + copy_len].copy_from_slice(&s.as_bytes()[..copy_len]);
161-
self.pos += copy_len;
162-
Ok(())
163-
}
164-
}
165-
166-
/// Helper function for the [`print_macro!`] to reduce stack usage.
167-
///
168-
/// Public but hidden since it should only be used from public macros.
169-
///
170-
/// # Safety
171-
///
172-
/// The format string must be one of the ones in [`format_strings`], and
173-
/// the module name must be null-terminated.
174-
#[doc(hidden)]
175-
pub unsafe fn format_and_call<const CONT: bool>(
176-
format_string: &[u8; format_strings::LENGTH],
177-
module_name: &[u8],
178-
args: fmt::Arguments,
179-
) {
180-
// Careful: this object takes quite a bit of stack.
181-
let mut writer = LogLineWriter::new();
182-
183-
match fmt::write(&mut writer, args) {
184-
Ok(_) => {
185-
if CONT {
186-
call_printk_cont(writer.as_bytes());
187-
} else {
188-
call_printk(format_string, module_name, writer.as_bytes());
189-
}
190-
}
191-
192-
Err(_) => {
193-
call_printk(
194-
&format_strings::CRIT,
195-
module_name,
196-
b"Failure to format string.\n",
197-
);
198-
}
199-
};
200-
}
201-
202177
/// Performs formatting and forwards the string to [`call_printk`].
203178
///
204179
/// Public but hidden since it should only be used from public macros.
205180
#[doc(hidden)]
206181
#[macro_export]
207182
macro_rules! print_macro (
208-
// Without extra arguments: no need to format anything.
209-
($format_string:path, false, $fmt:expr) => (
183+
($format_string:path, false, $($arg:tt)+) => (
210184
// SAFETY: This hidden macro should only be called by the documented
211185
// printing macros which ensure the format string is one of the fixed
212186
// ones. All `__LOG_PREFIX`s are null-terminated as they are generated
@@ -216,47 +190,17 @@ macro_rules! print_macro (
216190
$crate::print::call_printk(
217191
&$format_string,
218192
crate::__LOG_PREFIX,
219-
$fmt.as_bytes(),
193+
format_args!($($arg)+),
220194
);
221195
}
222196
);
223197

224-
// Without extra arguments: no need to format anything (`CONT` case).
225-
($format_string:path, true, $fmt:expr) => (
198+
// (`CONT` case).
199+
($format_string:path, true, $($arg:tt)+) => (
226200
$crate::print::call_printk_cont(
227-
$fmt.as_bytes(),
201+
format_args!($($arg)+),
228202
);
229203
);
230-
231-
// With extra arguments: we need to perform formatting.
232-
($format_string:path, $cont:literal, $fmt:expr, $($arg:tt)*) => (
233-
// Forwarding the call to a function to perform the formatting
234-
// is needed here to avoid stack overflows in non-optimized builds when
235-
// invoking the printing macros a lot of times in the same function.
236-
// Without it, the compiler reserves one `LogLineWriter` per macro
237-
// invocation, which is a huge type.
238-
//
239-
// We could use an immediately-invoked closure for this, which
240-
// seems to lower even more the stack usage at `opt-level=0` because
241-
// `fmt::Arguments` objects do not pile up. However, that breaks
242-
// the `?` operator if used in one of the arguments.
243-
//
244-
// At `opt-level=2`, the generated code is basically the same for
245-
// all alternatives.
246-
//
247-
// SAFETY: This hidden macro should only be called by the documented
248-
// printing macros which ensure the format string is one of the fixed
249-
// ones. All `__LOG_PREFIX`s are null-terminated as they are generated
250-
// by the `module!` proc macro or fixed values defined in a kernel
251-
// crate.
252-
unsafe {
253-
$crate::print::format_and_call::<$cont>(
254-
&$format_string,
255-
crate::__LOG_PREFIX,
256-
format_args!($fmt, $($arg)*),
257-
);
258-
}
259-
);
260204
);
261205

262206
// We could use a macro to generate these macros. However, doing so ends

scripts/checkpatch.pl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6608,7 +6608,7 @@ sub process {
66086608
$qualifier = $3;
66096609
if ($extension !~ /[SsBKRraEehMmIiUDdgVCbGNOxtf]/ ||
66106610
($extension eq "f" &&
6611-
defined $qualifier && $qualifier !~ /^w/)) {
6611+
defined $qualifier && $qualifier !~ /^[wr]/)) {
66126612
$bad_specifier = $specifier;
66136613
last;
66146614
}

0 commit comments

Comments
 (0)