Skip to content

ioctl work#147

Closed
emberian wants to merge 8 commits into
nix-rust:masterfrom
emberian:master
Closed

ioctl work#147
emberian wants to merge 8 commits into
nix-rust:masterfrom
emberian:master

Conversation

@emberian

@emberian emberian commented Jul 5, 2015

Copy link
Copy Markdown
Contributor

No description provided.

emberian added 6 commits July 5, 2015 07:04
This is more type-safe. Also, the old code wasn't cross-platform at all even
though it claimed to be. It wasn't even portable across architectures on
Linux.
@emberian

emberian commented Jul 5, 2015

Copy link
Copy Markdown
Contributor Author

cc @ltratt @utkarshkukreti @posborne @kennytm @fhartwig, people who have touched the previous ioctl implementation.

@emberian

emberian commented Jul 5, 2015

Copy link
Copy Markdown
Contributor Author

Wow, some of the tests are really volatile..

@joekain

joekain commented Jul 5, 2015

Copy link
Copy Markdown
Contributor

@cmr, yes some tests do seem to be failing intermittently. I filed #144 about this yesterday. I think I can fix one of the problems. The one that hit your commit might be harder to fix.

@emberian

emberian commented Jul 5, 2015

Copy link
Copy Markdown
Contributor Author

Yeah, not sure about the tcgetattr test. For the waitpid one, setting RUST_TEST_THREADS=1 should disable all parallellism I think?

@joekain

joekain commented Jul 5, 2015

Copy link
Copy Markdown
Contributor

@cmr, yes RUST_TEST_THREADS=1 should work but it seems heavy handed. Only the one wait() test need to be serialized.

@posborne

posborne commented Jul 6, 2015

Copy link
Copy Markdown
Member

@cmr, ioctls's are "fun" aren't they? I took the last shot at ioctl. Initially, I think this implementation should cover the use cases I have had for library like https://github.com/posborne/rust-spidev while providing better cross-platform support.

The macros for ioctl! don't feel quite intuitive, but I don't have a counter-proposal at this time. The use of bad makes sense but could also be confusing as this type is pretty common, at least in Linux.

@emberian

emberian commented Jul 6, 2015

Copy link
Copy Markdown
Contributor Author

Fortunately, the "bad" is only exposed internally.

I have no particular feelings about the syntax that the ioctl! macro accepts, but it shouldn't be used frequently. Ideally, nix would expose everything.

@ltratt

ltratt commented Jul 6, 2015

Copy link
Copy Markdown
Contributor

Given this doesn't seem to touch the OpenBSD parts I added, I don't have any useful comments :)

@carllerche

Copy link
Copy Markdown
Contributor

cc @posborne. Hoping to get some feedback (given my lack of ioctl knowledge)

@emberian

emberian commented Jul 6, 2015

Copy link
Copy Markdown
Contributor Author

@ltratt Note that this patch removes support for all other platforms but Linux, as I haven't personally looked into their command encoding etc.

@posborne

posborne commented Jul 7, 2015

Copy link
Copy Markdown
Member

Ideally nix would expose everything

I really feel that this is an unrealistic expectation based on the real work I have done with ioctls in the past, particularly on embedded Linux systems where you end up talking to a larger variety of devices than one might see in a desktop or server environment. I think we need to leave the door open for users and library providers to be able to define their own ioctls. The question for me is whether it is even worth trying to "automatically" provide any kind of interface for the specific ioctls available on any given system.

Providing an automatic interface that provides a good and safe interface to most ioctls is, in my opinion, a losing battle (which is why new drivers are not accepted using ioctl in Linux). I would rather not provide methods for accessing system interfaces than to provide ones that are broken.

I'm working to see if I can port my SPI library to using the new interface as a trial. Do you have example code using the interface that you can show as well?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The previous implementation returned a nix::Result with Ok for res >= 0. 0 is typical for a return value but a length is sometimes returned that is greater than 0[1]. The error returned also handled getting error information from the errno.

Unless there is a good reason to do else-wise, I think we would want to do the same here.

[1] http://man7.org/linux/man-pages/man2/ioctl.2.html

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That's an excellent idea.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The old interface also was provided ioctl::read with the following signature:

pub unsafe fn read<T>(fd: RawFd, op: ioctl_op_t) -> Result<T> {
...
}

Whereas the current interface provides the somewhat less safe and friendly interface (abbreviated for clarity):

(read $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
    pub unsafe fn $name(fd: c_int, val: *mut $ty) -> c_int {
    ...
    }
);

The main difference being that the user must always pass in a *mut ty in the new implementation where they would just get back an owned value in the new implementation. I see no difference in safety between the two implementations (both are unsafe) but there is more the user could screw up in the new interface. The old implementation also provided read_into<T>(fd: RawFd, op: ioctl_op_t, data: &mut T) -> Result<c_int> and read_into_ptr<T>(fd: RawFd, op: ioctl_op_t, data_ptr: *mut T) -> Result<c_int> as they are useful in some cases. The first mentioned, however, is the most ergonomic for the cases in which it can be used.

@emberian

emberian commented Jul 7, 2015

Copy link
Copy Markdown
Contributor Author

I really feel that this is an unrealistic expectation based on the real work I have done with ioctls in the past, particularly on embedded Linux systems where you end up talking to a larger variety of devices than one might see in a desktop or server environment.

If it's in a header, the scripts in etc can extract it. If it's not exposed by a header distributed with the Linux kernel, then you can still bind it manually. I'm not saying that the implementation details exposed by ioc etc should be hidden, just that it really ought to be possible to bind everything in mainline. Third-party modules are an important exception that makes the rule.

Providing an automatic interface that provides a good and safe interface to most ioctls is, in my opinion, a losing battle (which is why new drivers are not accepted using ioctl in Linux). I would rather not provide methods for accessing system interfaces than to provide ones that are broken.

I agree -- does this implementation try to do that? In particular, nothing is safe.

@emberian

emberian commented Jul 7, 2015

Copy link
Copy Markdown
Contributor Author

@posborne I wrote the original library this impl comes from for use in https://github.com/cmr/evdev

@posborne

posborne commented Jul 7, 2015

Copy link
Copy Markdown
Member

I agree -- does this implementation try to do that? In particular, nothing is safe.

I didn't spot anything obviously wrong while looking over the ioctls that were present (versus the ones that are left commented) that was obviously problematic. At the same time, most of the really interesting ioctls require read/writing to a structure which cannot be handled currently (would probably require something like bindgen for the structures, but that can get very messy).

For evdev, it looks like you added the structure definitions for those structures that are part of the kernel API to nix itself. I think these (and the ioctl constants) should probably live with your library outside of nix (Awesome lib BTW; I may find opportunity to use that myself sometime soon).

@carllerche

Copy link
Copy Markdown
Contributor

@cmr also, what was the reason for removing the debug impls for the various epoll types?

@carllerche

Copy link
Copy Markdown
Contributor

I personally have no opinion about this, so I can merge once there is consensus that it is good to go! 😄

@emberian

emberian commented Jul 7, 2015

Copy link
Copy Markdown
Contributor Author

@carllerche bitflags now provides the Debug impls.

@emberian

Copy link
Copy Markdown
Contributor Author

@posborne I'd prefer it if nix were the one canonical source for all raw Linux kernel APIs. It has to live somewhere, why not all here?

@carllerche

Copy link
Copy Markdown
Contributor

@posborne ping, what are your thoughts re: this?

@posborne

Copy link
Copy Markdown
Member

Sorry for the slow response.

@posborne I'd prefer it if nix were the one canonical source for all raw Linux kernel APIs.

I think nix might be able to support all major ways of accessing kernel APIs (e.g. ioctl, nl80211, sysfs, functionfs) and some APIs that are commonly desired, but supporting all APIs seems overly ambitious and likely to make maintenance of the library difficult. There is real complexity in many of the APIs and capturing this for all APIs would involve a lot of code.

It has to live somewhere, why not all here?

I think it is easier to maintain several crates, each with a specific purpose, than to maintain a monolithic crate that tries to do too much. With rust, we have a great packaging and distribution system in cargo and crates.io. For SPI devices, users can use the spidev crate I have created. For evdev access, they can use the evdev crate.

@emberian

Copy link
Copy Markdown
Contributor Author

@posborne While I agree that that is certainly the case for higher-level interfaces, the ioctls and structs used in them will be the same everywhere.

@emberian

Copy link
Copy Markdown
Contributor Author

I've removed the contentious code.

@emberian

Copy link
Copy Markdown
Contributor Author

@posborne So, removing the various wrapper functions leaves this whole implementation in a precarious position. The macros make less sense when you're not generating all of the code with macros. I think I'd rather just maintain my ioctl crate than try and force this particular approach on nix.

@posborne

Copy link
Copy Markdown
Member

@cmr, I do think the macros make it easier for describing ioctl APIs, even if we do not include generated or non-generated ioctl APIs in nix from the get-go. I think it is important to have a moderately stable API from the start; I may take a stab at changing the API exposed as discussed in comments eariler. If so, I'll let you know where those changes are so you can pull them into this PR.

@posborne

Copy link
Copy Markdown
Member

Ok, I made some additional changes to return a result type rather than just returning the integer from the underlying call. In order to build, I had to rebase on master and make one other change. You should be able to cherry-pick my two commits (if deemed acceptable) onto your branch: https://github.com/posborne/nix-rust/commits/ioctl-rework

Here's the relevant diff: posborne/nix-rust@eb837f8...posborne:ioctl-rework

@carllerche

Copy link
Copy Markdown
Contributor

@cmr @posborne Sorry for the delay. What is the status of this PR? It seems that you are reaching a consensus?

@emberian

Copy link
Copy Markdown
Contributor Author

Goodness, sorry for the delay myself... I think what @posborne suggested (and implemented) is excellent, and if he's happy with it, I'm happy with it!

@posborne

Copy link
Copy Markdown
Member

I think I'm OK moving forward with what we ended up with. Since it breaks API compatibility with previous versions, this should be a version bump (with semantic versioning, I believe a bump to the next 0.x is appropriate).

With my changes, the one part I think I may have wrong is with the following macro... posborne/nix-rust@eb837f8...posborne:ioctl-rework#diff-0f39576519037fe2d243b366759ea808R94

I think that needs to be in a mod or something to prevent polluting the namespace of the code that uses the macro? I feel like I've seen code that does that since I wrote this.

@emberian

Copy link
Copy Markdown
Contributor Author

@posborne Are you worried about the let res? If so, the Rust macro system makes that not a problem: it doesn't add an identifier that the client's code can observe, and it won't clobber any res that the client has (due to hygiene).

@posborne

Copy link
Copy Markdown
Member

@cmr, That was my concern. If it's not a problem, excellent! Good one Rust!

@carllerche

Copy link
Copy Markdown
Contributor

So, this PR is ready to go then? Let me know and I will merge. Also, it would probably be a good idea to bump the version as part of the PR.

@posborne

Copy link
Copy Markdown
Member

I opened up a separate PR that has the original changes from @cmr and my changes rebased on master: #172

@carllerche

Copy link
Copy Markdown
Contributor

Zomg! Finally merged! 🎉

@carllerche carllerche closed this Aug 21, 2015
@posborne

Copy link
Copy Markdown
Member

Excellent! I've been using this new stuff for a new lib[1] I am working on, so I'm glad to see it merged up.

[1] https://github.com/posborne/rust-i2cdev

@carllerche

Copy link
Copy Markdown
Contributor

@posborne @cmr I can cut a new crates io release when you think it is good. Now or later if you want to let it bake.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants