Skip to content

Commit bcd1464

Browse files
authored
Merge pull request #280 from nbdd0121/print
Support Rust `core::fmt::Argument` in vsprintf
2 parents 7451cda + 9e8bd67 commit bcd1464

File tree

3 files changed

+73
-133
lines changed

3 files changed

+73
-133
lines changed

lib/vsprintf.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2090,6 +2090,10 @@ char *fwnode_string(char *buf, char *end, struct fwnode_handle *fwnode,
20902090
return widen_string(buf, buf - buf_start, end, spec);
20912091
}
20922092

2093+
#ifdef CONFIG_RUST
2094+
char *rust_fmt_argument(char* buf, char* end, void *ptr);
2095+
#endif
2096+
20932097
/* Disable pointer hashing if requested */
20942098
bool no_hash_pointers __ro_after_init;
20952099
EXPORT_SYMBOL_GPL(no_hash_pointers);
@@ -2236,6 +2240,10 @@ early_param("no_hash_pointers", no_hash_pointers_enable);
22362240
*
22372241
* Note: The default behaviour (unadorned %p) is to hash the address,
22382242
* rendering it useful as a unique identifier.
2243+
*
2244+
* There is also a '%pA' format specifier, but it is only intended to be used
2245+
* from Rust code to format core::fmt::Arguments. Do *not* use it from C.
2246+
* See rust/kernel/print.rs for details.
22392247
*/
22402248
static noinline_for_stack
22412249
char *pointer(const char *fmt, char *buf, char *end, void *ptr,
@@ -2306,6 +2314,10 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
23062314
return device_node_string(buf, end, ptr, spec, fmt + 1);
23072315
case 'f':
23082316
return fwnode_string(buf, end, ptr, spec, fmt + 1);
2317+
#ifdef CONFIG_RUST
2318+
case 'A':
2319+
return rust_fmt_argument(buf, end, ptr);
2320+
#endif
23092321
case 'x':
23102322
return pointer_string(buf, end, ptr, spec);
23112323
case 'e':

rust/Makefile

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

rust/kernel/print.rs

Lines changed: 60 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,52 @@ 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_void};
14+
15+
// Called from `vsprintf` with format specifier `%pA`.
16+
#[no_mangle]
17+
unsafe fn rust_fmt_argument(buf: *mut c_char, end: *mut c_char, ptr: *const c_void) -> *mut c_char {
18+
use fmt::Write;
19+
20+
// Use `usize` to use `saturating_*` functions.
21+
struct Writer {
22+
buf: usize,
23+
end: usize,
24+
}
25+
26+
impl Write for Writer {
27+
fn write_str(&mut self, s: &str) -> fmt::Result {
28+
// `buf` value after writing `len` bytes. This does not have to be bounded
29+
// by `end`, but we don't want it to wrap around to 0.
30+
let buf_new = self.buf.saturating_add(s.len());
31+
32+
// Amount that we can copy. `saturating_sub` ensures we get 0 if
33+
// `buf` goes past `end`.
34+
let len_to_copy = cmp::min(buf_new, self.end).saturating_sub(self.buf);
35+
36+
// SAFETY: In any case, `buf` is non-null and properly aligned.
37+
// If `len_to_copy` is non-zero, then we know `buf` has not past
38+
// `end` yet and so is valid.
39+
unsafe {
40+
core::ptr::copy_nonoverlapping(
41+
s.as_bytes().as_ptr(),
42+
self.buf as *mut u8,
43+
len_to_copy,
44+
)
45+
};
46+
47+
self.buf = buf_new;
48+
Ok(())
49+
}
50+
}
51+
52+
let mut w = Writer {
53+
buf: buf as _,
54+
end: end as _,
55+
};
56+
let _ = w.write_fmt(*(ptr as *const fmt::Arguments<'_>));
57+
w.buf as _
58+
}
1459

1560
/// Format strings.
1661
///
@@ -23,7 +68,7 @@ pub mod format_strings {
2368
const LENGTH_PREFIX: usize = 2;
2469

2570
/// The length of the fixed format strings.
26-
pub const LENGTH: usize = 11;
71+
pub const LENGTH: usize = 10;
2772

2873
/// Generates a fixed format string for the kernel's [`printk`].
2974
///
@@ -42,14 +87,14 @@ pub mod format_strings {
4287
assert!(prefix[2] == b'\x00');
4388

4489
let suffix: &[u8; LENGTH - LENGTH_PREFIX] = if is_cont {
45-
b"%.*s\0\0\0\0\0"
90+
b"%pA\0\0\0\0\0"
4691
} else {
47-
b"%s: %.*s\0"
92+
b"%s: %pA\0"
4893
};
4994

5095
[
5196
prefix[0], prefix[1], suffix[0], suffix[1], suffix[2], suffix[3], suffix[4], suffix[5],
52-
suffix[6], suffix[7], suffix[8],
97+
suffix[6], suffix[7],
5398
]
5499
}
55100

@@ -84,14 +129,13 @@ pub mod format_strings {
84129
pub unsafe fn call_printk(
85130
format_string: &[u8; format_strings::LENGTH],
86131
module_name: &[u8],
87-
string: &[u8],
132+
args: fmt::Arguments<'_>,
88133
) {
89134
// `printk` does not seem to fail in any path.
90135
bindings::printk(
91136
format_string.as_ptr() as _,
92137
module_name.as_ptr(),
93-
string.len() as c_int,
94-
string.as_ptr(),
138+
&args as *const _ as *const c_void,
95139
);
96140
}
97141

@@ -101,112 +145,26 @@ pub unsafe fn call_printk(
101145
///
102146
/// [`printk`]: ../../../../include/linux/printk.h
103147
#[doc(hidden)]
104-
pub fn call_printk_cont(string: &[u8]) {
148+
pub fn call_printk_cont(args: fmt::Arguments<'_>) {
105149
// `printk` does not seem to fail in any path.
106150
//
107151
// SAFETY: The format string is fixed.
108152
unsafe {
109153
bindings::printk(
110154
format_strings::CONT.as_ptr() as _,
111-
string.len() as c_int,
112-
string.as_ptr(),
155+
&args as *const _ as *const c_void,
113156
);
114157
}
115158
}
116159

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-
202160
/// Performs formatting and forwards the string to [`call_printk`].
203161
///
204162
/// Public but hidden since it should only be used from public macros.
205163
#[doc(hidden)]
206164
#[macro_export]
207165
macro_rules! print_macro (
208-
// Without extra arguments: no need to format anything.
209-
($format_string:path, false, $fmt:expr) => (
166+
// The non-continuation cases (most of them, e.g. `INFO`).
167+
($format_string:path, false, $($arg:tt)+) => (
210168
// SAFETY: This hidden macro should only be called by the documented
211169
// printing macros which ensure the format string is one of the fixed
212170
// ones. All `__LOG_PREFIX`s are null-terminated as they are generated
@@ -216,47 +174,17 @@ macro_rules! print_macro (
216174
$crate::print::call_printk(
217175
&$format_string,
218176
crate::__LOG_PREFIX,
219-
$fmt.as_bytes(),
177+
format_args!($($arg)+),
220178
);
221179
}
222180
);
223181

224-
// Without extra arguments: no need to format anything (`CONT` case).
225-
($format_string:path, true, $fmt:expr) => (
182+
// The `CONT` case.
183+
($format_string:path, true, $($arg:tt)+) => (
226184
$crate::print::call_printk_cont(
227-
$fmt.as_bytes(),
185+
format_args!($($arg)+),
228186
);
229187
);
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-
);
260188
);
261189

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

0 commit comments

Comments
 (0)