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

Read one mouse packet byte per interrupt #953

Open
wants to merge 1 commit into
base: theseus_main
Choose a base branch
from

Conversation

hecatia-elegua
Copy link
Contributor

@hecatia-elegua hecatia-elegua commented May 8, 2023

Fix #832
Mutex should be good here, right?
Can these handlers be called from multiple cores at the same time? Interleaved, or nested?

Copy link
Member

@kevinaboos kevinaboos left a comment

Choose a reason for hiding this comment

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

Mutex should be good here, right?
Can these handlers be called from multiple cores at the same time? Interleaved, or nested?

In our current configuration, a device interrupt is only delivered to one CPU core at once, and they generally aren't able to be nested (i.e., the next mouse interrupt cannot occur until the prior mouse interrupt has been completely handled). However, using just a Mutex for this generally isn't advised, considering that those aspects could change at any moment.

MutexIrqSafe is required when another execution context (like a normal function) is also accessing variables that are shared with and accessed by an interrupt handler. However, here, only the interrupt handler accesses this state, so we don't technically need one.

On a related note, see my inline comments about using an AtomicU64 to pack all the mouse packet bytes and the cursor into an efficient u64. This approach would be best.
If you don't want to do that and would prefer to stick with a Mutex instead, you should move that static mutex instance into the interrupt handler function scope itself, in order to ensure that nobody else can access it.


One other question: is it valid/possible to read multiple bytes from the mouse for a single interrupt? For example, if a lot of mouse activity is occurring, must we wait for each successive interrupt to read all the bytes? I'm not saying you should always forcibly read either 3 or 4 bytes at once, since that could indeed cause perf issues or infinite loops, but rather that you could iteratively check whether more bytes are available and read them all until no more bytes are available, all within a single interrupt handler invocation.

I'm asking because I'm not sure if the PS/2 mouse behavior allows/supports this.
In other devices, like the serial port, you do receive an interrupt when data is ready to be read in, but you can read more than just one byte per interrupt, which is faster than only reading a single byte per interrupt.

/// of a mouse packet per interrupt into an array.
/// This can handle MouseId 0 (3 bytes) and 3, 4 (4 bytes).
struct PacketBytes {
len: usize,
Copy link
Member

Choose a reason for hiding this comment

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

if len is always a statically-known value, we can use const generics here.

Copy link
Member

Choose a reason for hiding this comment

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

Also len is a bit vague, give it a more precise name (and docs) that conveys it's an "expected byte count" or the "number of bytes in a complete mouse packet"

Comment on lines +34 to +36
inner: Mutex<[u8; PS2_MAX_MOUSE_BYTES]>,
// where to push the next element
cursor: Mutex<usize>,
Copy link
Member

Choose a reason for hiding this comment

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

there's no real benefit here to using two mutexes. I'd condense this into one.

Copy link
Member

Choose a reason for hiding this comment

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

If you want to get even fancier, you can pack all of this data into an atomic u64 since you only need 5 bytes. That would be the fastest option of all.

@@ -41,9 +81,13 @@ pub fn init(mut mouse: PS2Mouse<'static>, mouse_queue_producer: Queue<Event>) ->
"PS/2 mouse IRQ was already in use! Sharing IRQs is currently unsupported."
})?;

// Initialize the mouse packet bytes, which will be filled by 3-4 interrupts,
// depending on the MouseId
let packet_bytes = PacketBytes::new(mouse.packet_size());
Copy link
Member

Choose a reason for hiding this comment

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

here's where you'd specify the const generic parameter, either 3 or 4 depending upon mouse ID.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice idea, didn't consider const generics.

Comment on lines +106 to +108
packet_bytes.push(mouse.read_packet_byte());

if let Some(bytes) = packet_bytes.filled_bytes() {
Copy link
Member

Choose a reason for hiding this comment

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

these two instructions acquire and release a total of 4 locks. You can likely combine their functionality together such that a "push" operation returns an Option<[u8; N]> indicating whether the expected number of bytes have been received (and providing a copy of those 3 or 4 bytes).

Also, if you adopt a single-Mutex design (or better yet, the AtomicU64 design), that would reduce this overhead from 4 to 1 lock acquisitions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Again, thanks, exactly what I need to learn.

Copy link
Member

Choose a reason for hiding this comment

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

yea np at all, this is just the kind of thing that only becomes apparent once you have another set of eyes on it. 😄

@@ -56,18 +100,20 @@ pub fn init(mut mouse: PS2Mouse<'static>, mouse_queue_producer: Queue<Event>) ->
///
/// In some cases (e.g. on device init), [the PS/2 controller can also send an interrupt](https://wiki.osdev.org/%228042%22_PS/2_Controller#Interrupts).
extern "x86-interrupt" fn ps2_mouse_handler(_stack_frame: InterruptStackFrame) {
if let Some(MouseInterruptParams { mouse, queue }) = MOUSE.get() {
if let Some(MouseInterruptParams { mouse, queue, packet_bytes }) = MOUSE.get() {
// using `while` here didn't interact well with the window manager and increases handler runtime
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've tried using a loop here and will try again, since some of the code changed.
This would handle multi-byte interrupts.

Copy link
Member

Choose a reason for hiding this comment

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

yeah, I recall the previous code there. I phrased my comment as a question because legitimately not sure if the PS/2 mouse device actually allows for multiple bytes to be read per interrupt (i.e., does it do "batching" of multiple bytes into a single interrupt?); once we answer that then we can determine what the best choice is here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes it does, I've seen some osdev wiki people use while here 👍

@kevinaboos
Copy link
Member

Hi @hecatia-elegua, I'm doing some PR triage and noticed this one was mostly ready, just needed a few minor fixes.

If you have time, would you be interested in making those changes? If not, I totally understand; just let me know and I can take over and fix things up before merging.

@hecatia-elegua
Copy link
Contributor Author

I think back when I tried switching this to const generics + AtomicU64 it didn't just work easily and then I switched to other tasks. Feel free I guess

@kevinaboos
Copy link
Member

I think back when I tried switching this to const generics + AtomicU64 it didn't just work easily and then I switched to other tasks.

Oh interesting! What did you try, and do you have your attempted code anywhere where I could check it out? (even if it's messy)

Feel free I guess

Ok thanks, will do.

@hecatia-elegua
Copy link
Contributor Author

can't find it, I think I just removed the folder when switching computers

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.

Moving PS/2 mouse and typing at the same time
2 participants