-
Notifications
You must be signed in to change notification settings - Fork 459
WIP: Seq file #232
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: Seq file #232
Conversation
rust/kernel/seq_file.rs
Outdated
/// Create a `/proc` file. | ||
/// | ||
/// The returned value must not be dropped until the module is unloaded. | ||
pub fn proc_create<T: SeqOperations + Sync>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need #[cfg]
on proc
etc.?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Not necessarily on the function itself, it could be on the implementation instead so that callers do not need to care, i.e. skipping the bindings call if PROC
is not there -- please check what the kernel does currently for PROC
-related calls).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm... It looks like other places have an #ifdef CONFIG_PROC_FS
block around all the code for setting up the seq_file
. Not sure which way makes more sense?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ended up splitting this into seq_file.rs
and proc_fs.rs
. The proc specific parts are all under #[cfg(CONFIG_PROC_FS)]
, but the seq_file
parts aren't so we can later add e.g. debugfs seq_file
s.
Thanks for being mindful of that! It does make it easier for me when pushing to
That's perfectly fine!
We have some tests for the printing sample, for instance. But yeah, no particular reason, some tests that could be there were never added -- feel free to send a PR; otherwise I will do it at some point. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comments from a quick pass. I'll take a closer look tomorrow.
rust/kernel/seq_file.rs
Outdated
// SAFETY: Calling a C function. `s` is guaranteed to be null terminated | ||
// because we explicitly constructed it just above. | ||
unsafe { | ||
bindings::seq_puts(m, s.as_ptr() as *const u8 as *const c_types::c_char); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use seq_printf
here and specify the string length so that we don't need to depend on to_string
and concatenation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looked into it a bit, but I can't really see how to do it (at least not in a way that's generic over Display
). Here are some options I found:
- If we use
seq_printf
with the length in the format string, we'll still need to allocate aString
to calculate the length and get the pointer to pass. It would get rid of the concat though. - Instead of having
Item: Display
, we could require afn(&Item) -> &str
. Then the implementer can decide if they want to allocate aString
or not. - We could have the trait define a print function that take
Item
andseq_file
and let the implementer use the variousseq
print functions. But then they'll need to useunsafe
and they'd have the same problem as us if they want to print a string or type that doesn't have aprintf
format. - Instead of having
Item: Display
we could make our own trait for types that can be formatted withprintf
and write safe wrappers aroundseq_printf
for each of those. - There is the VaList type which seems to handle varargs, so we could have that type instead of the generic
Item
, but it seems more for defining Rust code that can be called on the C side with varargs, not for generating a Rust type that can be passed to C functions expecting varargs.
I don't think there's any support for putting a dynamically sized string on the stack.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Went with 2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's fine for now. In the future if there's a need for it, I think it would make sense to do a variant of option 3, where we would provide abstractions that would allow users to use the various seq_file
write variants without having to call unsafe functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy to make a follow-up PR for that.
Just fixed the compiler error in the sample by making |
231f3b4
to
dbb38aa
Compare
samples/rust/rust_seq_file.rs
Outdated
let template = "rust_seq_file: device opened this many times: "; | ||
message.try_reserve_exact(template.len() + 4).ok()?; | ||
// NOPANIC: We reserved space for `template` above. | ||
message.push_str(template); | ||
if *count < 1000 { | ||
// NOPANIC: There are 4 characters remaining in the string which | ||
// leaves space for a 3 digit number and the newline. | ||
write!(&mut message, "{}\n", *count).ok()?; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something more explicit for the 3, 4 and 1000 constants:
const MAX_DIGITS: u32 = 3;
const MAX_LENGTH: u32 = MAX_DIGITS + 1;
const MAX_COUNT: u32 = 10u32.pow(MAX_DIGITS);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would put them local to the function, given they are not used elsewhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
rust/kernel/seq_file.rs
Outdated
/// `m` must have been created from a proc file generated by calling | ||
/// `proc_create::<T>`. | ||
unsafe fn convert(m: *mut bindings::seq_file) -> *const T { | ||
let reg = crate::container_of!((*m).op, Self, seq_ops); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should use (*m).private
to save state. This way op
can be constant shared by everyone.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Had to make some changes to get this to work. I couldn't see how to stare the data in (*m).private
using proc_create_seq_private
because that always uses the default seq_open
which doesn't give us the chance to mutate the seq_file
created on open.
So I switched it to use proc_create_data
which let's us use our own open
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at how others have done this, it looks like it's done this way: call proc_create_seq_private
with your state in data
, then retrieve it using PDE_DATA(file_inode(seq->file))
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, got that working now.
15244a0
to
d96b676
Compare
Think I've addressed everything, but it required quite a few changes so probably needs a fresh review. |
0aeaa42
to
6f05b79
Compare
rust/kernel/bindings_helper.h
Outdated
@@ -14,6 +16,10 @@ | |||
#include <linux/mm.h> | |||
#include <uapi/linux/android/binder.h> | |||
|
|||
#ifdef CONFIG_PROC_FS | |||
#include "../../fs/proc/internal.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need this?
I don't think this would be acceptable to upstream maintainers. We shouldn't be peeking into internal stuff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was using it to get access to the data
field of proc_dir_entry
so that we can drop the data when dropping the entry.
Removed this by having the Rust ProcDirEntry
store it's own copy of the data pointer so that we don't need to extract it from the proc_dir_entry
.
rust/kernel/proc_fs.rs
Outdated
/// # Invariants | ||
/// | ||
/// The pointer [`ProcDirEntry::proc_dir_entry`] is a valid pointer and | ||
/// it's field [`bindings::proc_dir_entry::data`] is a valid pointer to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: its field
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
rust/kernel/proc_fs.rs
Outdated
unsafe { | ||
bindings::proc_remove(self.proc_dir_entry); | ||
} | ||
drop(data); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this drop is implicit, we don't really need to call it explicitly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
rust/kernel/proc_fs.rs
Outdated
proc_ops: &'static bindings::proc_ops, | ||
data: T, | ||
) -> KernelResult<ProcDirEntry<T>> { | ||
let data_ptr = Box::into_raw(Box::try_new(data)?) as *mut c_types::c_void; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need to improve this abstraction.
Forcing data
to move into a Box
means that it cannot contain any self-referential data structures, including mutexes. In the end, I think we'll need this to use PointerWrapper
so that users can choose what wrapper they want (if any).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense. Now the user is responsible for choosing the PointerWrapper
and passing in the data already wrapped.
rust/kernel/seq_file.rs
Outdated
type Iterator: Iterator<Item = Self::Item>; | ||
|
||
/// Called once each time the `seq_file` is opened. | ||
fn start(arg: &Self) -> Option<Box<Peekable<Self::Iterator>>>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here too I think we may want to consider PointerWrapper rather than Box
. (Although here it seems less problematic than in the previous usage.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still looking into this one. The one thing I'm thinking about is that we'll also need to mutate the iterator on the inside, so we can't just make it generic over any PointerWrapper
, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switched this one to using PointerWrapper
.
rust/kernel/seq_file.rs
Outdated
*pos += 1; | ||
} | ||
|
||
iterator.next(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may want to check the return value of next
here, mostly because the documentation says:
Advances the iterator and returns the next value.
Returns None when iteration is finished. Individual iterator implementations may choose to resume iteration, and so calling next() again may or may not eventually start returning Some(Item) again at some point.
That is, if we are at the end already (e.g., because the underlying data structure changed in the case of an iterator that allows mutation), then we risk restarting the iteration process if we don't check the return value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I didn't realize that iterators could resume like that.
I changed it, but I think in this case we might actually be ok without checking next
. Since we have a Peekable
and we've already called peek
on it and checked for None
(in an earlier call to next_callback
or start_callback
) so next
will always return Some
here. See impl of next
for Peekable
.
Thanks for the review @wedsonaf 👍 |
1d5d31a
to
d53b853
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Hi @wedsonaf, just letting you know I think everything's been addressed now. |
This could occur if the file is opened once and then there are concurrent reads on the same file descriptor? |
Also note |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
I opened #394 which contains these changes and uses the new |
You could use the Display implementation and print using This way you would just need to put |
Adds a trait which allows Rust modules to implement the seq_operations interface and use it to create a `/proc` file. Signed-off-by: Adam Bratschi-Kaye <ark.email@gmail.com>
Signed-off-by: Adam Bratschi-Kaye <ark.email@gmail.com>
9e62a22
to
52a09db
Compare
The namespace percpu counter protects pending I/O, and we can only safely diable the namespace once the counter drop to zero. Otherwise we end up with a crash when running blktests/nvme/058 (eg for loop transport): [ 2352.930426] [ T53909] Oops: general protection fault, probably for non-canonical address 0xdffffc0000000005: 0000 [Rust-for-Linux#1] PREEMPT SMP KASAN PTI [ 2352.930431] [ T53909] KASAN: null-ptr-deref in range [0x0000000000000028-0x000000000000002f] [ 2352.930434] [ T53909] CPU: 3 UID: 0 PID: 53909 Comm: kworker/u16:5 Tainted: G W 6.13.0-rc6 Rust-for-Linux#232 [ 2352.930438] [ T53909] Tainted: [W]=WARN [ 2352.930440] [ T53909] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.3-3.fc41 04/01/2014 [ 2352.930443] [ T53909] Workqueue: nvmet-wq nvme_loop_execute_work [nvme_loop] [ 2352.930449] [ T53909] RIP: 0010:blkcg_set_ioprio+0x44/0x180 as the queue is already torn down when calling submit_bio(); So we need to init the percpu counter in nvmet_ns_enable(), and wait for it to drop to zero in nvmet_ns_disable() to avoid having I/O pending after the namespace has been disabled. Fixes: 74d1696 ("nvmet-loop: avoid using mutex in IO hotpath") Signed-off-by: Hannes Reinecke <hare@kernel.org> Reviewed-by: Nilay Shroff <nilay@linux.ibm.com> Reviewed-by: Sagi Grimberg <sagi@grimberg.me> Reviewed-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Chaitanya Kulkarni <kch@nvidia.com> Tested-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com> Signed-off-by: Keith Busch <kbusch@kernel.org>
Add a Rust trait equivalent to the C
seq_operations
interface and the ability to create a/proc
file from a type implementing that trait. Instead of just translating each function in the interface to Rust I used an iterator instead since it maps pretty cleanly. But happy to change it if we thing it makes more sense to keep things close to the C side.A couple notes:
NO PANIC
).seq_file.rs
where I needed to allocate a string, but couldn't get what I needed usingtry_reserve
, so just left aTODO
about fixing it when we have more fallible options.