How to write a userspace linux block device driver? - c

I would like to write a linux block device driver. The driver would not need to access the hardware so it can be in userspace.
To start, I have tried to build an example block device driver with this Makefile:
obj-m = sbd.o
KVERSION = $(shell pwd)
PWD = $(shell pwd)
all:
make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules
I however get these errors which I do not know how to fix. Here is the stdout and stderr:
make -C /lib/modules/2.6.31-19-generic/build M=/home/andreas/sp/nivoa/src/driver/sbd modules
make[1]: Entering directory `/usr/src/linux-headers-2.6.31-19-generic'
CC [M] /home/andreas/sp/nivoa/src/driver/sbd/sbd.o
/home/andreas/sp/nivoa/src/driver/sbd/sbd.c:72: error: expected ‘)’ before ‘*’ token
/home/andreas/sp/nivoa/src/driver/sbd/sbd.c:128: warning: initialization from incompatible pointer type
/home/andreas/sp/nivoa/src/driver/sbd/sbd.c: In function ‘sbd_init’:
/home/andreas/sp/nivoa/src/driver/sbd/sbd.c:143: error: ‘sbd_request’ undeclared (first use in this function)
/home/andreas/sp/nivoa/src/driver/sbd/sbd.c:143: error: (Each undeclared identifier is reported only once
/home/andreas/sp/nivoa/src/driver/sbd/sbd.c:143: error: for each function it appears in.)
/home/andreas/sp/nivoa/src/driver/sbd/sbd.c:146: error: implicit declaration of function ‘blk_queue_hardsect_size’
make[2]: *** [/home/andreas/sp/nivoa/src/driver/sbd/sbd.o] Error 1
make[1]: *** [_module_/home/andreas/sp/nivoa/src/driver/sbd] Error 2
make[1]: Leaving directory `/usr/src/linux-headers-2.6.31-19-generic'
make: *** [all] Error 2
Any help on this would be greatly appreciated.
Thanks,
Andreas

There isn't an "official" way of doing block drivers in userspace, however people often do it by (ab)using the NBD driver to talk over a loopback network to a daemon which listens on a normal socket and speaks the NBD protocol. See the NBD docs for more info.
Your example is for a kernel-mode block device, which will need to be built as a kernel module. And as the kernel internals are always changing, it's presumably now incompatible.

Following MarkR's suggestion, it is even possible to talk the NBD protocol over an AF_UNIX socket pair, so no extra local daemon is needed. The program implementing this protocol will need to set up the socket pair and fork off a child. Both parent and child close one end of the socket pair. One of them starts taking requests on its end of the socket while the other one connects the NBD driver to its end of the socket.

Always looks at the first error:
In your case it looks like a problem with include files, e.g. request_queue_t is not defined.
Since this is a deprecated type, you are probably using a version of linux/blkdev.h that is newer than the code example.
Try adding typedef struct request_queue request_queue_t;

While using NBD, as suggested before, is nice, maybe a better way (used by, for example, virtualbox-fuse) is to make a FUSE filesystem that exports one file, which you can then use via losetup.

You can use NBD. Using nbdkit you can even write virtual block devices in shell script or other scripting languages (although stick to C if you want the best performance). I gave a talk about this topic at FOSDEM 2019 where I did a live demo writing a Linux kernel block device in shell script.

In addition to the above useful answers I would like to add that apart from NBD, there are also other ways where Linux Block I/O Layer can be used at userspace. Following are the additional options:
TCMU (TCM in Userspace) helps implementing custom SCSI (Small Computer System Interface) targets (block device) in user space.
BUSE (block device in userspace) build on the idea of Filesystem in Userspace (FUSE) allows the virtual block devices to run in userspace as well.

Related

Error compiling the Linux kernel 3.19.0-25-generic

I try to execute fakeroot debian/rules binary-headers binary-genericand compile when building kernel but get the error.
Here error in below
arch/x86/built-in.o:(.rodata+0x1cb8): undefined reference to 'sys_monlak'
make[2]: *** [vmlinux] Error 1
make[2]: Leaving directory '/home/su/linux-lts-vivid-3.19.0/debian/build/build-generic'
make[1]: *** [sub-make] Error 2
make[1]: Leaving directory '/home/su/linux-lts-vivid-3.19.0'
make: *** [/home/su/linux-lts-vivid-3.19.0/debian/stamps/stamp-build-generic] Error 2
I tried to build kernel following Build your own kernel and Clarkson.edu
I don't see any reference to the symbol "sys_monlak" inside any branch of the official Linux kernel. Are you using a branch obtained from somewhere else?
This error would happen if a driver depends on a symbol "sys_monlak" which should be defined elsewhere in the kernel but isn't, or perhaps the correct kernel module isn't enabled in the kernel config.
You can try running:
$ grep -R "sys_monlak"
If this produces a c file then you can then try to find the Makefile which should compile in this c file. If, for example, the file was called "arch/x86/monlak.c" you might find the following in the file "arch/x86/Makefile":
obj-$(CONFIG_MONLAK) += monlak.o
In this case you would want to try to enable CONFIG_MONLAK in the kernel config.
The other possibility is that you might want to disable the module that depends on the symbol 'sys_monlak'. Try to carefully read the description in the Kconfig file to see what each module does (the one that depends and the one that provides).

How to dump page table?

I am new to linux, C and stack overflow. I was trying to view page tables of all processes running. For this I am using dump_pagetable.c.
I tried to run first by normal compiling gcc dump_pagetables.c -o dump_pagetables.out. But it gave me error:
dump_pagetable.c:15:27: fatal error: linux/debugfs.h: No such file or directory #include linux/debugfs.h></code><br/><br/>Then I tried to use a make command <code>make -C /lib/modules/$(uname -r)/build M=$PWD modules</code>. So it gave me this error<code>CC [M] /home/varma/Desktop/TLB/dump_pagetable.o
/home/varma/Desktop/TLB/dump_pagetable.c:420:1: warning: data definition has no type or storage class
__initcall(pt_dump_init);
^
/home/varma/Desktop/TLB/dump_pagetable.c:420:1: error: type defaults to ‘int’ in declaration of ‘__initcall’ [-Werror=implicit-int]
/home/varma/Desktop/TLB/dump_pagetable.c:420:1: warning: parameter names (without types) in function declaration
/home/varma/Desktop/TLB/dump_pagetable.c:398:12: warning: ‘pt_dump_init’ defined but not used [-Wunused-function]
static int pt_dump_init(void)
^
cc1: some warnings being treated as errors
scripts/Makefile.build:263: recipe for target '/home/varma/Desktop/TLB/dump_pagetable.o' failed
make[1]: *** [/home/varma/Desktop/TLB/dump_pagetable.o] Error 1
Makefile:1394: recipe for target '_module_/home/varma/Desktop/TLB' failed
make: *** [_module_/home/varma/Desktop/TLB] Error 2
make: Leaving directory '/usr/src/linux-headers-3.19.0-23-generic'</code>
How do I get this code running?
How do modify the dump_pagetables.c So that I can see huge pages also.
The file you are trying to compile is a utility function for use within the Linux kernel for a somewhat obscure CPU used in IBM mainframes (the IBM System/390). It cannot be used outside of the kernel, and even there, it's only applicable to systems of that particular architecture, not to any desktop computers.
Needless to say, this won't work.
There is an equivalent feature (CONF_X86_PTDUMP) available in the kernel for x86 systems, but it is not enabled. If you want to use it, you will probably need to recompile your kernel. For more details, see the answer to the question "Print kernel's page tables".

Undefined function inserting new module in kernel linux

I'm working on a new system call for the kernel linux 2.6.32, with the aim to do a myOpen very close to the original open.
I've modified the original struct file (linux/fs.h) with a new variable and i want to continue to use the original fileTable also with myOpen (I hope to add myOpen in the code of the original open, switching in my case with a simply flag).
To do this I compile my code in a module (module.ko) and load this dinamically on my kernel (the one with the fs.h modified).
Now the problem is that if I use some function relative to the file system (ex: get_unused_fd_flags(flags); fd_install(fd, f); etc etc), some variable are not found (i recive it like warning and also in the kernel terminal).
I think the problem is that I Try to use some kernel function, but I do the same with others like filp_open and I not recive error. How I can solve this?
Terminal:
make -C /lib/modules/2.6.32progb/build M=/home/mauro/Scrivania modules
make[1]: ingresso nella directory "/home/mauro/Scrivania/linux-2.6.32.B"
Building modules, stage 2.
MODPOST 1 modules
WARNING: "alloc_fd" [/home/mauro/Scrivania/moduloB.ko] undefined!
make[1]: uscita dalla directory "/home/mauro/Scrivania/linux-2.6.32.B"
[sudo] password for mauro: insmod: error inserting 'moduloB.ko': -1 Unknown symbol in module
alloc_fd is not exported to modules.
This is a hint by the kernel developers that they don't want to encourage module developers to call the function. Now, since the kernel is open-source, you can of course export the function anyway by simply modifying the kernel source to add: "EXPORT_SYMBOL(alloc_fd);" just after the function in fs/file.c
Some functions you're trying to use are already exported - hence you don't get a warning for them.

Adding fpirntf() into xnu kernel tcp_xxx.c, stdio.h not found

Greeting everyone on stackoverflow!
I am trying to add fpirntf() to XNU kernel tcp_xxx.c file, so when TCP moves it can print parameters to a file, for better understanding how TCP works, but fatal error occurs like follows:
$ sudo make ARCH_CONFIGS=X86_64 KERNEL_CONFIGS=DEBUG
CC tcp_output.o
/Users/wangweikai/Desktop/xnubuilder/xnu-2422.90.20/bsd/netinet/tcp_output.c:135:10: fatal error: 'stdio.h' file not found
#include <stdio.h>
^
1 error generated.
make[7]: *** [tcp_output.o] Error 1
make[6]: *** [build_all] Error 2
make[5]: *** [do_all] Error 2
make[4]: *** [build_all] Error 2
make[3]: *** [build_all_recurse_into_conf] Error 2
make[2]: *** [build_all_recurse_into_bsd] Error 2
make[1]: *** [build_all_bootstrap_DEBUG^X86_64^NONE] Error 2
make: *** [all] Error 2
It looks there is no stdio.h in xnu source code, to solve this I re-installed xcode 5.0.2(only this works with xnu-2422) and outputs mach_kernel able to boot; I tried install command line tools by "xcode-select --install" in terminal, but not working neither.
BTW if I add printf() without #include it gives no error, it looks print function is defined by file with name other than stdio.h.
Since my point is to log how TCP parameters(such as congestion window size and next sequence number and so on)changes in a file, so if there is any way can do this please also tell me, I am total new to xnu and C programming.
Best regards.
Standard I/O (i.e, stdio) is not available in the kernel. To output debugging information from the kernel, call printf(); its output will be visible in the kernel log. Writing data to files directly from the kernel is complicated, and is rarely advisable. (In this case, it may in fact lead to hard lockups, e.g, if the file being written to is on a network volume!)
If you are new to C programming, this is not a good first project. Kernel development is difficult, unforgiving stuff, particularly for the XNU kernel; errors will often cause your computer to crash, potentially losing data, and debugging services are often not available. If you have your heart set on examining an OS kernel, though, the Linux kernel is much easier to work with — it can be easily booted and debugged in a virtual machine, and is far better documented.

Can I modify the dynamic linker and use without recompiling the glibc?

I am trying to modify the dynamic linker provided in the libc6(2.15-0ubuntu20.2) on a 64 bit Ubuntu machine.
So currently my code is using the same version of the glibc library. (I have downloaded the source code for the same and working on it). My question is that is it possible to modify and build only the linker source code which is present in glibc\elf\ directory without building the entire glibc library.
And if it is possible how can I make my test program to switch using the new version of dynamic linker that I have build myself instead of using the default unmodified linker.
Any pointers or suggestions are highly appreciated.
(If any more information is needed please let me know)
EDIT::
#constantius
I followed the steps in the post linked by you to build ld.so.
But I am getting following error on the make and I checked ld.so is not there in the elf.
The error is::
/var/services/homes/abhi/test/ld/eglibc-build/elf/librtld.os: In function `generic_getcwd':
/var/services/homes/abhi/test/ld/eglibc-2.15/elf/../sysdeps/posix/getcwd.c:356: undefined reference to `__closedir'
/var/services/homes/abhi/test/ld/eglibc-2.15/elf/../sysdeps/posix/getcwd.c:368: undefined reference to `__fdopendir'
/var/services/homes/abhi/test/ld/eglibc-2.15/elf/../sysdeps/posix/getcwd.c:384: undefined reference to `__readdir'
/var/services/homes/abhi/test/ld/eglibc-2.15/elf/../sysdeps/posix/getcwd.c:397: undefined reference to `rewinddir'
/var/services/homes/abhi/test/ld/eglibc-2.15/elf/../sysdeps/posix/getcwd.c:528: undefined reference to `__closedir'
/var/services/homes/abhi/test/ld/eglibc-2.15/elf/../sysdeps/posix/getcwd.c:490: undefined reference to `__closedir'
collect2: error: ld returned 1 exit status
make[2]: *** [/var/services/homes/abhi/test/ld/eglibc-build/elf/ld.so] Error 1
make[2]: Leaving directory `/var/services/homes/abhi/test/ld/eglibc-2.15/elf'
make[1]: *** [elf/subdir_lib] Error 2
make[1]: Leaving directory `/var/services/homes/abhi/test/ld/eglibc-2.15'
make: *** [all] Error 2
NOTE With the same infrastructure I can build and install the full GLIBC so I dont think there is an error with the infrastructure.
-- I guess the error is some where related to editing Makeconfig to all-subdirs = csu elf gmon io misc posix setjmp signal stdlib string time.
--Any suggestions on this..
SOLVED
Need to add dirent in the all-subdirs list in addition to what we edited before
Thanks
Citing this page. In case you don't get something, comment please — I'll try to explain.
Building
To compile Glibc (ld.so cannot be compiled independently) download and unpack Glibc source tarball.
1 Make sure the version of Glibc you downloaded is the same as the system's current one.
2 Make sure the environmental variable LD_RUN_PATH is not set.
3 Read the INSTALL and make sure all necessary tool chains (Make, Binutils, etc) are up-to-date.
4 Make sure the file system you are doing the compilation is case sensitive, or you will see weird errors like
/scratch/elf/librtld.os: In function `process_envvars':
/tmp/glibc-2.x.y/elf/rtld.c:2718: undefined reference to `__open'
...
5 ld.so should be compiled with the optimization flag on (-O2 is the default). Failing to do so will end up with weird errors (see Question 1.23 in FAQ)
6 Suppose Glibc is unpacked at
/tmp/glibc-2.x.y/
Then edit /tmp/glibc-2.x.y/Makefile.in: Un-comment the line
# PARALLELMFLAGS = -j 4
and change 4 to an appropriate number.
7 Since we are only interested in ld.so and not the whole Glibc, we only want to build the essential source files needed by ld.so. To do so, edit /tmp/glibc-2.x.y/Makeconfig: Find the line started with
all-subdirs = csu assert ctype locale intl catgets math setjmp signal \
...
and change it to
all-subdirs = csu elf gmon io misc posix setjmp signal stdlib string time
8 Find a scratch directory, say /scratch. Then
$ cd /scratch
$ /tmp/glibc-2.x.y/configure --prefix=/scratch --disable-profile
$ gmake
Since we are not building the entire Glibc, when the gmake stops (probably with some errors), check if /scratch/elf/ld.so exists or not.
ld.so is a static binary, which means it has its own implementation of standard C routines (e.g. memcpy, strcmp, etc) It has its own printf-like routine called _dl_debug_printf.
Testing
You can run the ld-linux.so directly. It will complain that this is probably not what you want (but you want exactly this) and offer you list of options with which you can run it. See also man ld-linux.so for debugging flags, i.e. there's LD_DEBUG environment variable you can define to see ld-linux.so debugging output.
While I'm not clear on whether the build system for glibc makes doing this easy, there's no fundamental reason why you can't build and use the glibc dynamic linker without building libc.so. I would peruse the top-level Makefile for ways to make this work.
As for testing it, there are two methods:
Explicitly invoke the dynamic linker to run a program, as in:
./ld-linux.so.2 a.out args ...
When linking your program, specify an alternate dynamic linker pathname (which will get stored in its PT_INTERP program header) by passing this option to the compiler driver:
-Wl,-dynamic-linker,/path/to/alternate/ld-linux.so.2

Resources