Serenity Operating System
at master 148 lines 8.5 kB view raw view rendered
1# Kernel Development Patterns & Guidelines 2 3This document intends to guide the immediate and newcomer kernel developer when creating, 4modifying and removing Kernel code. 5Please read all of this document if you intend to send pull requests, as well as the [general contributing guidelines](../../CONTRIBUTING.md) 6and [patterns](../Patterns.md) for the entire codebase. 7 8This document was composed as a result of ideas, experience and a general vision of what 9the Kernel could become in the prosperous future of the project. 10 11## Out of memory handling 12 13Maybe one of the most important issues we have to solve in kernel code is when OOM (Out of memory) 14condition occurs - simply put, a new allocation request has failed due to various reasons - 15the allocation request was too much "greedy" and couldn't be satisfied, or simply we can't allocate more physical RAM pages 16to whoever that requested them. 17 18**The proper solution to this is to always use the `TRY()` semantics together with 19appropriate `adopt_*` function (either for `OwnPtr` or `RefPtr`).** 20 21```cpp 22#include <AK/Try.h> 23#include <AK/OwnPtr.h> 24 25... 26 27auto new_object = TRY(adopt_nonnull_own_or_enomem(new (nothrow) Object(...))); 28``` 29 30In case of failure, the above code will simply propagate `ENOMEM` code to the caller, up to the syscall entry code, 31so the userland program could know about the situation and act accordingly. 32 33An exception to this is when there's simply no way to propagate the error code to the userland program. 34Maybe it's a `ATAPort` (in the IDE ATA code) that asynchronously tries to handle reading data from the harddrive, 35but because of the async operation, we can't send the `errno` code back to userland, so we what we do is 36to ensure that internal functions still use the `ErrorOr<>` return type, and in main calling function, we use 37other meaningful infrastructure utilities in the Kernel to indicate that the operation failed. 38 39## We don't break userspace - the SerenityOS version 40 41We don't break userspace. However, in contrast to the Linux vision on this statement, 42we don't care about ABI/API breakage between the userland and the kernel. **What we do care 43about is a possible incident when a Kernel change does introduce a misbehave in userland, and Userland was not 44appropriately considered to ensure this does not happen.** 45 46Many internal changes in the Kernel don't affect userland - for example, a new shiny driver 47for super-fast storage devices, is not something that will likely break userland, because the 48proper abstractions have already put in place, so the userland simply does not care about 49the specifics about each `StorageDevice` in the kernel, as long as it properly implements the 50known interfaces. 51 52However, some kernel changes, mainly ABI/API changes between userland and the kernel, in the 53syscall handling layer, will break userland unless it's properly handled beforehand. 54The proper solution in git terms is to ensure that both "offending" kernel changes and the appropriate 55userland changes to accommodate the kernel changes are in the same commit, so we still keep the rule that 56each git commit is bisectable by itself. 57 58**It's expected that changes to the Kernel will be tested with userland utilities to ensure the changes 59are not creating any misbehaves in the userland functionality.** 60 61Even more stricter than what has been said above - we don't remove functionality unless it's absolutely 62clear that nobody uses that functionality. Even when it's absolutely clear that nobody uses some kind 63of kernel functionality, it could still be useful to think about how to make it more available and usable 64to the SerenityOS project community. 65Again, such removal should happen according to what has been mentioned in terms of git handling. 66 67## Each kernel feature should be backed by a userland usecase 68 69In contrast to the previous guideline, this guideline is clearly about the healthy growth of Kernel - 70we don't bloat the Kernel for things we don't need. For example, in the early days 71of the project, there was a floppy driver in the Kernel and it got removed because 72nobody used it. Similarly, when an Intel AC97 soundcard driver was introduced, the SB16 73soundcard driver was removed shortly afterwards. **We simply don't have interest in supporting 74hardware that nobody will use, or a kernel feature that doesn't make sense to most people.** 75 76## Proper locking 77 78The [AHCI locking document](AHCILocking.md) describes our locking patterns thoroughly. 79Still, it's very important to understand we do care about SMP (Symmetric Multiprocessing), 80so proper locking is one of the top priorities in the kernel development mindset. 81 82**The general rule is that we should not acquire a `Mutex` after taking a `Spinlock`. 83Taking a `Spinlock` after another is generally considered fine, as long as they are always 84taken in the same order, to prevent deadlocks.** 85 86To ensure we do this properly, the `MutexProtected<>` and `SpinlockProtected<>` C++ containers 87have been introduced in the kernel to ensure that locking is done on particular shared data objects, 88so it's preferable to use these containers instead of a "random" spinlock as class member. 89 90## Proper, clean and meaningful syscall userland interfaces 91 92As at the time of writing this document, the syscall table is generally quite stable. 93This happens to be that way because the syscalls are well-defined, backed by good-known POSIX interfaces. 94**Suggestions/patches to add syscalls should be examined strictly, because generally-speaking it's the "last resort" 95we should choose from other Unix interfaces that are available to us.** 96 97Because there's no definitive "yes" or "no" for all cases, expect that a discussion will be taking 98place in your pull request, in case that you do introduce a new syscall in the Kernel. 99 100For example, say that one wants to add a new driver for the Storage subsystem, then 101we already have the proper abstractions in place, so the new specific `StorageDevice` will be registered 102as like any other `StorageDevice`, therefore it will be exposed in the `/dev` directory and regular 103`write`, `open`, `read`, `ioctl` syscalls will be usable immediately. 104Therefore, there's no need for a special syscall to handle the new hardware, because 105the already-existing syscalls are sufficient. 106 107**We should also refrain from architecture-specific syscalls as much as possible. Linux had them 108in the past and many of them were removed eventually.** 109 110## Security measures 111 112We, as the SerenityOS project, take seriously the concept of security information. 113Many security mitigations have been implemented in the Kernel, and are documented in a 114[different file](../../Base/usr/share/man/man7/Mitigations.md). 115As kernel developers, we should be even more stricter on the security measures being 116taken than the rest of system. 117One of the core guidelines in that aspect **is to never undermine any security measure 118that was implemented, at the very least.** 119 120It's also very nice and generous if one decides to improve on a security measure, 121as long as it doesn't hurt other security measures. 122 123We also consider performance metrics, so a tradeoff between two mostly-contradictive metrics 124is to be discussed when an issue arises. 125 126## No hardcoded userspace paths 127 128To ensure the kernel stays flexible to future changes, we should not put hardcoded 129paths or assume where filesystem items (nor where filesystems are mounted) reside on - we should 130always let userspace to inform the kernel about paths and assume nothing else. 131Even when it's obvious some file will always be located in a certain path, it is considered 132a violation of an abstraction layer to hardcode it in the kernel code, because we put an hard effort 133to keep the abstractions we have intact and clean. 134 135There's one exception to this rule - the kernel will use a `dbgln` statement to 136warn the user in case that the dynamic loader is not the usual binary we use. 137To generalize the exception a bit more - debug messages (being used sparingly) with 138assumption of paths could be OK, as long as they never have any functional implication 139on the user. 140 141## Documentation 142 143As with any documentation, it's always good to see more of it, either with a new manual page, 144or a kernel concept being described in the `Documentation/Kernel` repository directory so other 145developers can understand it. 146There's no well-defined template to use when writing a documentation, but it is expected 147at the very least to have an opening paragraph about the topic so others can understand 148what the document is about.