This is done mainly by implementing safe locking on the data structure
keeping the pointers to the PerContextState objects. Therefore, this now
eliminates the need for using LockRefPtr, as SpinlockProtected is enough
for the whole list.
The usage of HashMap in this class was questionable, and according to
Sahan Fernando (the original contributor to the VirGL work also known as
ccapitalK) there was no deep research on which data structure to use for
keeping all pointers to PerContextState objects.
Therefore, this structure is changed to IntrusiveList as the main reason
and advantage to use it is that handling OOM conditions is much more
simple, because if we succeeded to create a PerContextState object, we
can be sure now that inserting it to the list will not cause OOM error
condition.
This is quite useful for userspace applications that can't cope with the
restriction, but it's still useful to impose other non-configurable
restrictions by using jails.
Instead of storing x86_64 register names in `SC_create_thread_params`,
let the Kernel figure out how to pass the parameters to
`pthread_create_helper`.
This was the last change that was needed to be able boot with the flag
of LOCK_IN_CRITICAL_DEBUG. That flag is not always enabled because there
are still other issues in which we hold a spinlock and still try to lock
a mutex.
Instead of using one global mutex we can protect internal structures of
the InodeWatcher class with SpinlockProtected wrappers. This in turn
allows the InodeWatcher code to be called from other parts in the kernel
while holding a prior spinlock properly.
`process.fds()` is protected by a Mutex, which causes issues when we try
to acquire it while holding a Spinlock. Since nothing seems to use this
value, let's just remove it entirely for now.
This should not affect boot times on qemu, as that does not use
dynamic transfer delays in its adma code path.
On real hardware this could potentially double the data throughput,
decreasing load times.
This has been broken on x86_64 since its introduction, as it features
more registers to be saved, and we never held up the "rbp has to be the
last pushed register" there.
Instead, just copy rbp from the thread structure, which is now properly
updated since the last commit.
The existing `read_entire` is quite slow due to allocating and copying
multiple times, but it is simultaneously quite hard to get rid of in a
single step. As a replacement, add a new function that reads as much as
possible directly into a user-provided buffer.
Currently we do not use interrupts for the SD driver, yet we
had enabled the signaling of all of them.
Since we were never acknowledging them, we were getting spammed by
unnecessary interrupts, causing the system to slow down to a crawl.
This commit makes the system boot in less than 1 minute with PIO,
compared to the old 30+ minute boot.
There is a big mix of LockRefPtrs all over the Networking subsystem, as
well as lots of room for improvements with our locking patterns, which
this commit will not pursue, but will give a good start for such work.
To deal with this situation, we change the following things:
- Creating instances of NetworkAdapter should always yield a non-locking
NonnullRefPtr. Acquiring an instance from the NetworkingManagement
should give a simple RefPtr,as giving LockRefPtr does not really
protect from concurrency problems in such case.
- Since NetworkingManagement works with normal RefPtrs we should
protect all instances of RefPtr<NetworkAdapter> with SpinlockProtected
to ensure references are gone unexpectedly.
- Protect the so_error class member with a proper spinlock. This happens
to be important because the clear_so_error() method lacked any proper
locking measures. It also helps preventing a possible TOCTOU when we
might do a more fine-grained locking in the Socket code, so this could
be definitely a start for this.
- Change unnecessary LockRefPtr<PacketWithTimestamp> in the structure
of OutgoingPacket to a simple RefPtr<PacketWithTimestamp> as the whole
list should be MutexProtected.
To do this we also need to get rid of LockRefPtrs in the USB code as
well.
Most of the SysFS nodes are statically generated during boot and are not
mutated afterwards.
The same goes for general device code - once we generate the appropriate
SysFS nodes, we almost never mutate the node pointers afterwards, making
locking unnecessary.
While doing this, we can also just return a normal RefPtr instead of a
LockRefPtr, because we create these channels when initializing an audio
controller, and never change the pointer in AudioController instances
after their initialization, hence no locking is necessary.
Instead of enumerating all available controllers and then ask each to
find its audio channels, we change the initialization sequence to match
what happens in the Networking subsystem and Graphics subsystem - we
essentially probe for a matching driver on a PCI device, create a device
instance, and immediately initialize it.
This in fact allows us to immediately find any hardware initialization
issues and report it, and then dropping the created instance, as usually
being done in other initialization paths in the Kernel.
This also opens the opportunity to propagate errors when failed to
initialize an AudioChannel instance, and it will be addressed in a
future commit.
This is done by 2 ways which both fit very well together:
- We stop use LockRefPtrs. We also don't allow expansion of the
m_channels member, by setting it to be a fixed Array of 2
IDEChannels.
- More error propagation through the code, in the construction point of
IDEChannel(s). This means that in the future we could technically do
something meaningful with OOM conditions when initializing an IDE
controller.
We were crashing on the VERIFY_INTERRUPTS_DISABLED() in
RecursiveSpinlock::unlock, which was caused by the compiler reordering
instructions in `sys$get_root_session_id`. In this function, a SpinLock
is locked and quickly unlocked again, and since the lock and unlock
functions were inlined into `sys$get_root_session_id` and the DAIF::read
was missing the `volatile` keyword, the compiler was free to reorder the
reads from the DAIF register to the top of this function. This caused
the CPU to read the interrupts state at the beginning of the function,
and storing the result on the stack, which in turn caused the
VERIFY_INTERRUPTS_DISABLED() assertion to fail. By adding the `volatile`
modifier to the inline assembly, the compiler will not reorder the
instructions.
In aa40cef2b7, I mistakenly assumed that the crash was related to the
initial interrupts state of the kernel threads, but it turns out that
the missing `volatile` keyword was the actual problem. This commit also
removes that code again.
We have a problem with the original utimensat syscall because when we
do call LibC futimens function, internally we provide an empty path,
and the Kernel get_syscall_path_argument method will detect this as an
invalid path.
This happens to spit an error for example in the touch utility, so if a
user is running "touch non_existing_file", it will create that file, but
the user will still see an error coming from LibC futimens function.
This new syscall gets an open file description and it provides the same
functionality as utimensat, on the specified open file description.
The new syscall will be used later by LibC to properly implement LibC
futimens function so the situation described with relation to the
"touch" utility could be fixed.
We were detaching from the jail process list too early. To ensure we
detach properly, leverage the remove_from_secondary_lists method
so the possibly jailed parent process can still see the dying process
and therefore clean it properly.
For a very long time, the kernel had only support for basic PS/2 devices
such as the PS2 AT keyboard and regular PS2 mouse (with a scroll wheel).
To adapt to this, we had very simple abstractions in place, essentially,
the PS2 devices were registered as IRQ handlers (IRQ 1 and 12), and when
an interrupt was triggered, we simply had to tell the I8042Controller to
fetch a byte for us, then send it back to the appropriate device for
further processing and queueing of either a key event, or a mouse packet
so userspace can do something meaningful about it.
When we added the VMWare mouse integration feature it was easily adapted
to this paradigm, requiring small changes across the handling code for
these devices.
This patch is a major cleanup for any future advancements in the HID
subsystem.
It ensures we do things in a much more sane manner:
- We stop using LockRefPtrs. Currently, after the initialization of the
i8042 controller, we never have to change RefPtrs in that class, as we
simply don't support PS2 hotplugging currently.
Also, we remove the unnecessary getters for keyboard and mouse devices
which also returned a LockRefPtr.
- There's a clear separation between PS2 devices and the actual device
nodes that normally exist in /dev. PS2 devices are not polled, because
when the user uses these devices, they will trigger an IRQ which when
is handled, could produce either a MousePacket or KeyEvent, depending
on the device state.
The separation is crucial for buses that are polled, for example - USB
is a polled bus and will not generate an IRQ for HID devices.
- There's a clear separation in roles of each structure. The PS2 devices
which are attached to a I8042Controller object are managing the device
state, while the generic MouseDevice and KeyboardDevice manage all
related tasks of a CharacterDevice, as well as interpreting scan code
events and mouse relative/absolute coordinates.
It happens to be that only PS/2 devices that are connected via the i8042
controller can generate interrupt events, so it makes much more sense to
have those devices to implement the enable_interrupts method because of
the I8042Device class and not the HIDDevice class.
Use the new class in HID code, because all other HID device controllers
will be using this class as their parent class.
Hence, we no longer keep a reference to any PS/2 device in HIDManagement
and rely on HIDController derived classes to do this for us.
It also means that we removed another instance of a LockRefPtr, which
is designated to be removed and is replaced by the better pattern of
SpinlockProtected<RefPtr<>> instead.
The definitions were being defined already by `BootInfo.h` and that was
being included here via transitive includes. The extern definitions of
the variables do not have the `READONLY_AFTER_INIT` attribute in
`BootInfo.h`. This causes conflicting definitions of the same variable.
The `READONLY_AFTER_INIT` specifier is not needed for extern variables
as it only effects their linkage, not their actual use, so just use the
versions in `BootInfo.h` instead of re-declaring.