Booting LKL inside Haiku

Blog post by lucian on Thu, 2010-07-08 01:57

The first milestone in this GSoC journey to building a generic file system driver based on Linux kernel code is booting LKL (Linux Kernel Library) inside Haiku.

For the short attention span: it works :)

KERN: KDiskDeviceManager::_AddDiskSystem() done: No error
KERN: file system: file_systems/iso9660/v1
KERN: KDiskDeviceManager::_AddDiskSystem(file_systems/iso9660/v1)
KERN: KDiskDeviceManager::_AddDiskSystem() done: No error
KERN: lklhaikufs: unhandled pheader type 0x4
KERN: file system: file_systems/lklhaikufs/v1
KERN: KDiskDeviceManager::_AddDiskSystem(file_systems/lklhaikufs/v1)
KERN: khaiku_env_timer:: LKL_TIMER_INIT
KERN: [lkl-console] Linux version 2.6.29 (gringo@lethe) (gcc version 4.4.4 (GCC) ) #10 Fri Jun 18 14:45:38 EEST 2010
KERN: [lkl-console] Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 16256
KERN: [lkl-console] Kernel command line: 
KERN: [lkl-console] lkl: IRQs initialized
KERN: [lkl-console] PID hash table entries: 256 (order: 8, 1024 bytes)
KERN: [lkl-console] lkl: timer initialized
KERN: [lkl-console] Dentry cache hash table entries: 8192 (order: 3, 32768 bytes)
KERN: [lkl-console] Inode-cache hash table entries: 4096 (order: 2, 16384 bytes)
KERN: [lkl-console] Memory available: 64900k/65536k RAM, (862k kernel code, 270k data)
KERN: [lkl-console] Mount-cache hash table entries: 512
KERN: [lkl-console] bio: create slab  at 0
KERN: [lkl-console] io scheduler noop registered (default)
KERN: [lkl-console] lkl: syscall interface initialized
KERN: [lkl-console] console [lkl_console0] enabled
KERN: [lkl-console] Warning: unable to open an initial console.
KERN: [lkl-console] Switched to NOHz mode on CPU #0
KERN: [lkl-console] System halted.
KERN: khaiku_env_timer:: LKL_TIMER_SHUTDOWN
KERN: [lkl-console] lkl: IRQs freed
KERN: lkl: halt user callback called
KERN: KDiskDeviceManager::_AddDiskSystem() done: No error
KERN: file system: file_systems/nfs/v1
KERN: KDiskDeviceManager::_AddDiskSystem(file_systems/nfs/v1)

These are messages from /var/log/syslog that are generated when Haiku searches for available file system add-ons. One of those drivers is my lklhaikufs driver. Messages with [lkl-console] are from LKL (similar to what you see when a normal Linux kernel boots) :)

Porting LKL to the Haiku kernel API

To boot LKL inside Haiku, first I have port LKL to a new pseudo-architecture. For this project I have two choices:

  • write a user space file system driver (similar to googlefs) and port LKL to the Haiku system call API (or a higher level library)
  • write a kernel space file system driver and port LKL to Haiku's kernel API.
Even though the APIs are similar, the kernel space driver would definitely have a better performance so I chose this path.

Porting details

Porting LKL is much simpler than porting the whole Linux kernel to a new architecture. Much of the low-level details are already taken care of by the portable LKL layer. One only needs to provide a set of runtime environment dependent primitives that cannot be (efficiently) emulated in a portable manner:
  • memory management routines
  • synchronization primitives
  • thread management
  • time and timer generators
  • a (screen/log) printing routine

Memory management

Because LKL does not require memory protection mechanisms (there will be no processes running in the LKL operating system – all calls are considered from kernel space), support for memory management becomes trivial: LKL just needs a physical memory pool that the Linux kernel can use for its memory allocation needs. The poll of memory will be managed by the kernel using a mix of buddy, SLUB/SLAB/SLOB/SLQB/SL*B algorithms just like on a normal system. This pool will only be used for buffers and structures dynamically allocated by the kernel. The kernel code and statically allocated data will not be part of this poll as they are managed by Haiku's kernel add-on loader (see src/system/kernel/elf.cpp: load_kernel_add_on()).

The file system driver does not need to interact with interrupts or anything like that, so it does not have any special requirements. This greatly simplifies my work: as memory does not need to be locked (lock_memory) or unlocked in RAM at various times, the memory support can be reduced to malloc and free (we all know that malloc or any kind of access (read/write/execute) to a unlocked memory address can block, don't we?).

Threading support

LKL needs threading support because the Linux kernel uses threads for internal house-keeping, like processing I/O requests or running softirqs, tasklets or workqueues and journaling tasks.

Starting/stopping threads from Haiku's kernel is similar to other more familiar APIs (e.g. POSIX): spawn_kernel_thread takes a function f and a generic pointer arg as arguments and creates a new thread that will execute f(arg).

Two things should be noted here:

  • threads are initially suspended, you need to call resume_thread to actually run the thread's code. This differs from the traditional POSIX API and I spent some time investigating why my threads were never running :P.
  • threads (as other Haiku kernel resources – e.g. semaphores) can be given a name. This helps a lot in KDL trips

Semaphore support

Haiku's semaphore API is very straight forward: create_sem, delete_sem, acquire_sem and release_sem should be self-explanatory.

However there's a catch: releasing a semaphore automatically invokes Haiku's scheduler, and this shouldn't be done in a number of contexts like when interrupts are disabled. Unfortunately the legacy BeOS docs license forbids improvements and such a warning cannot be written in release_sem's documentation. It's important to replace release_sem(s) calls with release_sem_etc(s, 1, B_DO_NOT_RESCHEDULE) if this can be called in such a context.

Time and timers

LKL needs to know the current time (not the time since starting the computer, but the absolute time – as in a wall clock) for a variety of tasks, but for this driver we need it to set a valid time stamp on files. For this LKL uses real_time_clock_usecs which returns the time since start of the Unix epoch (January 1st 1970) in microseconds.

Any operating system needs a functioning timer interrupt to function properly. Linux support two categories of programmable interrupt timer (PIC) chips: timers that fire periodically or timers that can efficiently be programmed to fire at various moments and stop firing in certain intervals. We simulate the second kind of PICs in Haiku, thus generating a smaller amount of LKL-interrupts (not real hardware interrupts), for a lower overhead:

KERN: [lkl-console] Switched to NOHz mode on CPU #0

Now that the LKL boots and shuts down correctly, we have to build the file system driver that uses it :)