Skip to content
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

Rust NetDevice and NetDeviceOperationsVtable struct #208

Closed
wants to merge 1 commit into from

Conversation

kloenk
Copy link
Member

@kloenk kloenk commented Apr 19, 2021

add function for net_device functions

Contains #226 and #227

@kloenk
Copy link
Member Author

kloenk commented Apr 20, 2021

I'm a bit struggling with the rtnl_link_ops struct. For the other *_ops table I use a const as the FileOperationsVtable. But rtnl_link_ops has to be writable, even if that is not often (__read_mostly). Any ideas on this?

Also I'm unsure on how to populate the kind field of that struct

@adamrk
Copy link

adamrk commented Apr 21, 2021

Maybe I'm misunderstanding your issue, but if you're thinking of doing something similar to the existing FileOperationsVtable then I'd think it's ok. The VTABLE there needs to be const so that it can be constructed at compile time - when the device is registered, a reference to the file_operations is passed to the C side and could be mutated.

For the kind field, I'd think you'd want to have the user implement a trait with const KIND: &str and convert that to the kind entry in rtnl_link_ops.

@kloenk
Copy link
Member Author

kloenk commented Apr 21, 2021

@adamrk I think I understand how the FileOperationsVtable works. My problem with rtnl_link_ops is, that this has to be writable, as (as far as the folks from the kernel told me) there is a linked list inside which is used internally by the kernel. It's only written very rare, but it's written to (that's why it's in .data.read_mosty). My Idea is to try it with a macro which creates a static mut variable, which than can be assigned to that link. Did not get around yet to implement that, maybe only after the 4th of may as I have to write my finals :-)

@kloenk
Copy link
Member Author

kloenk commented Apr 22, 2021

I created a proc macro, which creates a wrapped rtnl_link_ops (wrapped as we need Sync). This seems to work so far, I'm still trying to reimplement the drivers/net/dummy driver

@kloenk
Copy link
Member Author

kloenk commented Apr 25, 2021

Rebased to have rust-analyzer, so now contains #226 and #227.

@alex alex marked this pull request as ready for review April 25, 2021 15:03
@alex alex marked this pull request as draft April 25, 2021 15:03
@kloenk kloenk force-pushed the rust-netdevice branch 4 times, most recently from 14ea91c to 6e3bed3 Compare April 29, 2021 15:58
@kloenk
Copy link
Member Author

kloenk commented Apr 29, 2021

image

I got my first 10 pings via the rust dummy_rs module implemented here. ip li add name test type dummy_rs still creates an oops though

@kloenk
Copy link
Member Author

kloenk commented Apr 29, 2021

and ip li add name test type dummy_rs is working now 🎉🎉

Now I have to document everything. help is appreciated :-)

@kloenk kloenk changed the title WIP: Rust NetDevice and NetDeviceOperationsVtable struct Rust NetDevice and NetDeviceOperationsVtable struct Apr 29, 2021
@kloenk kloenk marked this pull request as ready for review April 30, 2021 10:55
Comment on lines 36 to 38
description: b"Android Binder",
license: b"GPL v2",
params: {},
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should likely go into a different commit -- or is GitHub confused due to having several commits in the PR? Please rebase to have 1 commit anyway.

drivers/net/Kconfig Outdated Show resolved Hide resolved
Comment on lines +5 to +6
//! This is a demonstration of what a small driver looks like in Rust, based on drivers/net/dummy.c.
//! This code is provided as a demonstration only, not as a proposal to mass-rewrite existing drivers in Rust
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comment on #254 (comment).

I guess we can talk about this tomorrow in the call :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that was the wording of @joshtriplett, while talking about this at our last call.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not fixated on the exact wording, I just want to make sure that people don't see a Rust version of an existing C driver and assume we're trying to rewrite the world in Rust. In this context, we're providing samples of what drivers would look like; we're not writing replacements.

In the future, some driver maintainers may choose to use Rust in existing drivers, but that's up to those driver maintainers, not us.

drivers/net/dummy_rs.rs Outdated Show resolved Hide resolved
drivers/net/dummy_rs.rs Outdated Show resolved Hide resolved
rust/helpers.c Outdated
// See https://github.com/rust-lang/rust-bindgen/issues/1671
#if !defined(CONFIG_ARM)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

? Perhaps GitHub is confused -- please rebase.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tried that yesterday, but not even the rust branch builds on my system currently. Something with exports.o and symbols

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after a git clean -xfd (as make mrproper does not work #255 ) it builds the rebased version.
I pushed the rebased version, but I'm sot sure if/why it still makes this as a change.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, now it is clear -- somehow you ended up with the comment moved outside of the #ifdef.

Comment on lines 111 to 112
/// Used by the rtnl_link_ops macro to interface with C
pub fn c_from_kernel_result<T>(r: KernelResult<T>) -> T
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is not intended to be used by users, hide the docs.

}
}

/// register_locked - register a network device if the RtnlLock is already hold
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this is copied from C docs, but do not put the name of the function etc.

///
/// # Safety
///
/// caller must hold the RtnlLock and semaphore
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// caller must hold the RtnlLock and semaphore
/// Caller must hold the [`RtnlLock`] and semaphore.

And similar everywhere else.

Comment on lines 36 to 38
/*pub unsafe fn from_ptr(ptr: *const bindings::nlattr) -> &'static mut Self {
(ptr as *mut NlAttr).as_mut().unwrap()
}*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see a bunch of commented out code etc.

@ojeda
Copy link
Member

ojeda commented May 8, 2021

Now I have to document everything. help is appreciated :-)

From the quick review I did above, I assume you are still working on this (i.e. there is a bunch of commented out code, SAFETY comments needed, etc.).

}
}

impl<I: NetDeviceAdapter> Deref for NetDevice<I> {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we implement this Deref and DerefMut or should we consider this unsafe, as this exposes the direct bindings?

@kloenk kloenk force-pushed the rust-netdevice branch 2 times, most recently from f3ddd55 to 018db0a Compare May 8, 2021 11:05
@kloenk
Copy link
Member Author

kloenk commented May 8, 2021

To summarise what I understood in the meeting. I will remove the line saying it is just a test driver. I finish up the abstraction in the rust/ folder, than we will submit the rust folder, and if it is merged into upstream linux, I will submit the drivers/net/dummy_rs.rs file myself.
Or would you recommend another way?

@ojeda
Copy link
Member

ojeda commented May 8, 2021

If you prefer, you can put it here if you want, too. Like with Binder, for the moment we have it in this tree to have everything in one place. While they will not be merged with the Rust support, I can submit it as an extra patch (with you as the author etc.) like I did with Binder from Wedson in the RFC.

@wedsonaf @TheSven73 since they have the other two potential drivers.

@kloenk
Copy link
Member Author

kloenk commented May 8, 2021

If you prefer, you can put it here if you want, too. Like with Binder, for the moment we have it in this tree to have everything in one place. While they will not be merged with the Rust support, I can submit it as an extra patch (with you as the author etc.) like I did with Binder from Wedson in the RFC.

Sounds great. I will create a second commit like Wedson explained, so one with abstractions and one with the driver.
Author is not important for me, if it is easier with something else, that's also fine for me. I just looked at the C dummy driver and what is there as author.

@ojeda
Copy link
Member

ojeda commented May 8, 2021

If you prefer, different PRs is also fine (and allows us to merge things bit by bit).

@kloenk
Copy link
Member Author

kloenk commented May 8, 2021

I would prefer 2 commits. One pr is easier I guess, but two is also fine. Whatever you prefer.

@alex
Copy link
Member

alex commented May 12, 2021

FYI: there's a merge conflict here.

@kloenk kloenk force-pushed the rust-netdevice branch 2 times, most recently from 6ca2cb4 to f973478 Compare May 12, 2021 14:05
@ksquirrel

This comment has been minimized.

Copy link

@soruh soruh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are quite a few places where I'd like more safety comments and the address functions in net/mod.rs could probably be safer (see my comment there).
(I didn't look at the changes to module.rs)

pub get_ts_info: bool,
}

/// This trait does not include any functions.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These comments should probably be updated to reflect that this struct describes which methods are implemented by an implementation of EthToolOps not which ones are defined by the trait itself.

/// Operations table needed for ethtool.
/// [`Self::TO_USE`] defines which functions are implemented by this type.
pub trait EthToolOps<T: NetDeviceAdapter>: Send + Sync + Sized {
/// Struct [`EthToolToUse`] which signals which function are ipmlemeted by this type.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/ipmlemeted/implemeted/

pub const RTNL_LINK_OPS_EMPTY: bindings::rtnl_link_ops = bindings::rtnl_link_ops {
list: bindings::list_head {
next: ptr::null::<bindings::list_head>() as *mut bindings::list_head,
prev: ptr::null::<bindings::list_head>() as *mut bindings::list_head,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could use ptr::null_mut


/// Get a pointer to this struct.
pub unsafe fn get_pointer(&self) -> *const bindings::rtnl_link_ops {
&self.0 as *const _ as *const bindings::rtnl_link_ops
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the as *const _ seems redundant

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but I think rust wants them, as this is not directly the right type for the view of rust

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in this case you don't need it is since you're turning a &rtnl_link_ops into a *const rtnl_link_ops. I agree that the other ones are necessary.

(I could of course be wrong about that and it's ultimately not that important)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be that I missed .0 befor. And then you need this. Is it is valid, but rust doesn't know that.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was probably the case, you can only do &T as *const T but not &U as *const T, even if U only contains a T.

Copy link
Member Author

@kloenk kloenk May 20, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, as U has repr Transparent. It would also be valid, but rust isn't able to check that. That's the reason to go over *const _

But yeah, will change it.


/// Deregister the op table from the kernel.
pub fn unregister(&self) {
let ptr = self as *const _ as *mut bindings::rtnl_link_ops;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could be self.get_pointer() as *mut bindings::rtnl_link_ops

/// Specifically, one should make absolutely sure that this function is
/// called before TX completion of this packet can trigger. Otherwise
/// the packet could potentially already be freed.
pub fn tx_timestamp(&mut self) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Otherwise the packet could potentially already be freed.

This sounds like this function should potentially be unsafe.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think It couldn't, as then we don't have self anymore? But not completely sure right now

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't follow completely, are you saying that this is indeed safe/you're not sure about it's safety or that this could't be an unsafe method?

If we're not certain about about the safety of a method, the default should really be making it unsafe.

#[cfg(not(target_pointer_width = "64"))]
fn end_pointer(&self) -> *mut u8 {
let sk_reff = self.get_internal();
(sk_reff.end) as *mut u8
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unnecessary parenthesis

}

fn start_xmit(skb: SkBuff, dev: &mut NetDevice<Self>) -> kernel::net::device::NetdevTX {
let mut skb = skb;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use fn start_xmit(mut skb: SkBuff, ... and remove the let mut skb = skb;.

dev.carrier_set(new_carrier);

Ok(())
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this return a Result if it can't fail?

unsafe {
kernel::bindings::strlcpy(
&(info.driver) as *const _ as *mut i8,
b"dummy_rs\0" as *const _ as *mut i8,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you could use the cstr! macro here

/// Please note: addr must be aligned to u16.
#[cfg(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)]
pub unsafe fn is_zero_ether_addr(addr: *const u8) -> bool {
*(addr as *const u32) | (*((addr as usize + 4) as *const u16) as u32) == 0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am pretty sure this will need to use ptr::read_unaligned. Even if the underlying cpu architecture allows efficient unaligned accesses using normal load instructions, LLVM can optimize based on the assumption that addr as usize % 4 == 0. For example it could vectorize in ways that would cause overlap if the address is not aligned to 4 bytes. Or it could optimize away manual alignment checks. By the way you can use addr.offset(4) instead of addr as usize + 4.

Comment on lines +75 to +79
*(addr as *const u16)
| *((addr as usize + 2) as *const u16)
| *((addr as usize + 4) as *const u16)
== 0
Copy link
Member

@bjorn3 bjorn3 May 20, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking that

Suggested change
*(addr as *const u16)
| *((addr as usize + 2) as *const u16)
| *((addr as usize + 4) as *const u16)
== 0
*(addr as *const [u16; 3]) == [0; 3]

would be nicer to read. When I tested if LLVM would be able to optimize it I noticed the following: On x86_64 and aarch64 the current version uses 3 loads while the suggested version does 2 loads just like the CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS version. On armv7 this results in a branch + load from a constant pool for the suggested version while version uses 3 loads and no constant pool.

Because of this I think this suggestion should be used for CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS to avoid UB at no perf cost, but not(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) should remain the same.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it maybe make sense for all of these methods to take a &[u8; 6] and be completely safe to call or is there an inherent reason we need to treat these values as u16s?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS there would be no change. for not(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) that would require 6 loads (one for each u8) instead of 3 (one for each u16).


unsafe impl<T: NetDeviceAdapter> Sync for NetDevice<T> {}

impl<T: NetDeviceAdapter> NetDevice<T> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that Rust's polymorphize support is not yet mature, so one copy of each function might be monomorphized for each T. Not a big concern here though.


/// Iff flags
#[repr(u32)]
#[allow(non_camel_case_types)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any particular reason to use SCREAMING_SNAKE_CASE here?

use core::convert::{From, Into};
use core::ops::{Add, AddAssign, Deref, DerefMut, Sub, SubAssign};

/// Holds multiple flags to give to an interface via [`NetDevice::set_flag`].
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is likely to be very common in bindings. We may want a mini version of bitflags!.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excluding tests and docs the bitflags crate is a little less than 600 lines big. bitflags is a slow moving crate and doesn't implement a huge set of features. I don't know what the policy on vendoring extern crates is, but I don't think there is much benefit of writing a mini version over just vendoring bitflags. At most it would save codegen of a couple of methods, though most methods are marked as #[inline] and thus only codegened when used.

}

/// Represents which fields of [`struct ethtool_ops`] should pe populated with pointers for the trait [`EthToolOps`]
pub struct EthToolToUse {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also seems to be pretty common for bindings. Maybe we need to have a attribute macro. I'll think about a sensible design.

})
}

/// Return a refference to the private data of the [`NetDevice`].
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/refference/reference/g

}
}

/// Represents which fields of [`struct ethtool_ops`] should pe populated with pointers for the trait [`EthToolOps`]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Represents which fields of [`struct ethtool_ops`] should pe populated with pointers for the trait [`EthToolOps`]
/// Represents which fields of [`struct ethtool_ops`] should be populated with pointers for the trait [`EthToolOps`]

nit

This was referenced May 27, 2021
ci: retry add-apt-repository to deal with network issues
@ksquirrel
Copy link
Member

Review of 818532604b6d:

  • ⚠️ This PR adds/deletes more than 500 lines.
  • ❌ Commit 8185326: Private GitHub email address (@users.noreply.github.com) used for commit author.
  • ⚠️ Commit 8185326: The commit committer does not match the commit author.
  • ❌ Commit 8185326: The last line of the commit message must be a Signed-off-by and must match the submitter of the PR.
  • ❌ Commit 8185326: Unknown tag. Known ones are: Acked-by, Cc, Co-developed-by, Fixes, Link, Reported-by, Reviewed-by, Signed-off-by, Suggested-by, Tested-by.

@kloenk
Copy link
Member Author

kloenk commented Jul 10, 2021

Closing this, as it is quite outdated, and the commit history Is completely broken.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

9 participants