Different results when running inside a docker container - c

I am trying to run some code based on this libaio sample:
https://oxnz.github.io/2016/10/13/linux-aio/#example-1
I added the O_DIRECT flag according to libaio's documentation.
It seems to work inside my ubuntu 16.04 desktop machine (hello is written to /tmp/test).
However, when I compile and run the same sample inside a docker container nothing is written to the file. when running inside gdb I can see that an event is read by io_getevents and the result is set to -22 (EINVAL).
Any ideas?
This is my modified code
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <libaio.h>
int main() {
io_context_t ctx;
struct iocb iocb;
struct iocb * iocbs[1];
struct io_event events[1];
struct timespec timeout;
int fd;
fd = open("/tmp/test", O_WRONLY | O_CREAT | O_DIRECT) ;
if (fd < 0) err(1, "open");
memset(&ctx, 0, sizeof(ctx));
if (io_setup(10, &ctx) != 0) err(1, "io_setup");
const char *msg = "hello";
io_prep_pwrite(&iocb, fd, (void *)msg, strlen(msg), 0);
iocb.data = (void *)msg;
iocbs[0] = &iocb;
if (io_submit(ctx, 1, iocbs) != 1) {
io_destroy(ctx);
err(1, "io_submit");
}
while (1) {
timeout.tv_sec = 0;
timeout.tv_nsec = 500000000;
int ret = io_getevents(ctx, 0, 1, events, &timeout);
printf("ret=%d\n", ret);
if (ret == 1) {
close(fd);
break;
}
printf("not done yet\n");
sleep(1);
}
io_destroy(ctx);
return 0;
}

The filesystem inside the container is likely to be different to that of the host's filesystem (on modern setups is likely to be overlayfs but on older systems it could be aufs). For O_DIRECT on an open to work a device/filesystem has to at least "support" it (note the scare quotes) and it's likely your container's filesystem does not.

Related

Cannot write file in Linux with liburing

I recently started using liburing and tried to write some demos. I met trouble when tried to write something into the disk.
Here is my code, the error check has been removed.
#include "liburing.h"
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/uio.h>
#include <unistd.h>
int main() {
struct io_uring ring;
struct iovec *iovecs;
struct io_uring_sqe *sqe;
struct io_uring_cqe *cqe;
int fd;
void *buf;
int ret = io_uring_queue_init(4, &ring, 0);
fd = open("./tmp_file", O_WRONLY | O_TRUNC | O_CREAT, 0644);
ret = ftruncate(fd, 4096);
sqe = io_uring_get_sqe(&ring);
posix_memalign(&buf, 4096, 4096);
memset(buf, 0x2, 4096);
sqe = io_uring_get_sqe(&ring);
io_uring_prep_write(sqe, fd, buf, 4096, 0);
ret = io_uring_submit(&ring);
ret = io_uring_wait_cqe(&ring, &cqe);
printf("cqe->res: %d\n", cqe->res);
io_uring_cqe_seen(&ring, cqe);
io_uring_queue_exit(&ring);
close(fd);
return 0;
}
cqe->res always return 0. I have no clues to continue the search.
My kernel version is linux 5.4.56, and the liburing version is liburing-2.1. I compiled my demo with GCC 830.
I also tried other existing examples, compiled them from the source, they work just fine.
Any help is appreciated, thank you very much.
You call 'io_uring_get_sqe' twice. Try to remove one of them.
I did it and run your code. It's working!

Why does mmap defined in a Linux kernel module return MAP_FAILED?

I am trying to map a kernel buffer in user space using mmap method in linux 3.10.10. But it is returning MAP_FAILED. Why it is failed to map the buffer.
Kernel module
#include <linux/module.h> /* Needed by all modules */
#include <linux/kernel.h> /* Needed for KERN_ALERT */
#include <linux/init.h> /* Needed for the macros */
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include <linux/interrupt.h>
#include <linux/io.h>
//#include <linux/malloc.h>
#include <linux/mm.h> /* mmap related stuff */
long long *buf1;
long long* buf;
static int driver_mmap(struct file *file, struct vm_area_struct *vma)
{
vma->vm_flags |= VM_LOCKED|VM_SHARED;
int i = remap_pfn_range(vma, vma->vm_start,
virt_to_phys(buf) >> PAGE_SHIFT,
vma->vm_end-vma->vm_start, vma->vm_page_prot);
SetPageReserved(virt_to_page(buf));
printk("MMAP \n");
return 0;
}
struct file_operations proc_fops =
{
mmap:driver_mmap,
};
int init_module_test(void)
{
int i;
buf1 = kmalloc(4096, __GFP_COLD|GFP_DMA);
buf = ((int)buf1 + PAGE_SIZE -1) & PAGE_MASK;
printk("<1>Hello world1\n");
for (i = 0; i < 512; i++)
{
buf[i] = (long long) i + 1;
}
proc_create ("mmap_example",0,NULL, &proc_fops);
printk("<1>Hello world3\n");
printk("<1>BUF1 = 0x%08x\n BUF = 0x%08x\n", buf1,buf);
return 0;
}
void cleanup_module_test(void)
{
remove_proc_entry ("mmap_example", NULL);
kfree(buf1);
printk("Goodbye world\n");
}
module_init(init_module_test);
module_exit(cleanup_module_test);
Application code
#include<stdio.h>
#include<stdlib.h>
#include<sys/mman.h>
int main(void)
{
int fd, i;
long long *msg = NULL;
if ((fd = fopen("/proc/mmap_example", "r")) < 0)
{
printf("File not opened");
}
msg = mmap(NULL, 4096, PROT_READ, MAP_SHARED, fd, 0);
if (msg == MAP_FAILED)
{
printf("MAP failed");
return 0;
}
for (i = 0; i < 512; i++)
printf("0x%llx,", msg[i]);
fclose(fd);
return 0;
}
I always end up seing "MAP failed".
Is there something wrong with my code?
The first problem is that you are attempting to use fopen to open a file and placing the return value in an integer, but fopen doesn't return an integer. It returns FILE *. This tells me you are ignoring compiler warnings and errors. That's a bad idea: they're produced for a reason.
The second problem is that you actually do need an integer file handle in order to provide it as an argument to mmap(2). For that, you should be calling open(2) (not fopen(3)).
There may well be additional problems with this code, but that's a start.
well, i can't comment so i post an answer that is not one, but i hope it will be still useful:
i'm not sure about the driver, but you can use the errno method (http://man7.org/linux/man-pages/man3/errno.3.html) on mmap to have a better answer on why it's failing:
add in your application code at the right place:
#include <errno.h>
printf("%i",errno);
or you could maybe use the following if you don't want to print the errno :
cpp -dM /usr/include/errno.h | grep 'define E' | sort -n -k 3
from How to know what the 'errno' means?
debugfs can't handle mmap
I know this is not your exact case, but it also makes mmap fail with MAP_FAILED, and it may help future Googlers: https://patchwork.kernel.org/patch/9252557/
And here is a fully working procfs example with an userland test.

MAC address from interface on OS X (C)

This might be a stupid question and I apologize if it's already been addressed here, but I've searched quite a bit without much luck. I'm trying to get my interface's hardware address in C and I'm using OS X (x86-64). I know how to get it with ifconfig, but I want my program to get it automatically for any computer, well, at least OS X computers. I found another thread that posted this link which pretty much does what I want (with some modifications), but I can't make the iokit functions link in ld (my compiler is gcc). I tried adding the flags -lIOKit and -framework IOKit to the gcc command line, but I still get the same link errors. Here's a link to my code: header and source.
This little program will work without changes on OSX.
Code : (credits to Alecs King from freebsd list)
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int mib[6], len;
char *buf;
unsigned char *ptr;
struct if_msghdr *ifm;
struct sockaddr_dl *sdl;
if (argc != 2) {
fprintf(stderr, "Usage: getmac <interface>\n");
return 1;
}
mib[0] = CTL_NET;
mib[1] = AF_ROUTE;
mib[2] = 0;
mib[3] = AF_LINK;
mib[4] = NET_RT_IFLIST;
if ((mib[5] = if_nametoindex(argv[1])) == 0) {
perror("if_nametoindex error");
exit(2);
}
if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
perror("sysctl 1 error");
exit(3);
}
if ((buf = malloc(len)) == NULL) {
perror("malloc error");
exit(4);
}
if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
perror("sysctl 2 error");
exit(5);
}
ifm = (struct if_msghdr *)buf;
sdl = (struct sockaddr_dl *)(ifm + 1);
ptr = (unsigned char *)LLADDR(sdl);
printf("%02x:%02x:%02x:%02x:%02x:%02x\n", *ptr, *(ptr+1), *(ptr+2),
*(ptr+3), *(ptr+4), *(ptr+5));
return 0;
}
You should, however, change int len; to size_t len;

How to use kernel libcrc32c (or same functions) in userspace programmes?

I want to do some CRC check in my own userspace programme. And I find that the kernel crypto lib is already in the system, and come with SSE4.2 support.
I tried to directly #include <linux/crc32c.h> and run gcc with -I/usr/src/linux/include/. However, it doesnot work.
Any way to use some kind of libcrc32c ?
You can use kernel crypto CRC32c (and other hash/cipher functions) from user-space via socket family AF_ALG on Linux:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/socket.h>
#include <linux/if_alg.h>
#include <sys/param.h>
#include <string.h>
#include <strings.h>
int
main (int argc, char **argv) {
int sds[2] = { -1, -1 };
struct sockaddr_alg sa = {
.salg_family = AF_ALG,
.salg_type = "hash",
.salg_name = "crc32c"
};
if ((sds[0] = socket(AF_ALG, SOCK_SEQPACKET, 0)) == -1 )
return -1;
if( bind(sds[0], (struct sockaddr *) &sa, sizeof(sa)) != 0 )
return -1;
if( (sds[1] = accept(sds[0], NULL, 0)) == -1 )
return -1;
char *s = "hello";
size_t n = strlen(s);
if (send(sds[1], s, n, MSG_MORE) != n)
return -1;
int crc32c = 0x00000000;
if(read(sds[1], &crc32c, 4) != 4)
return -1;
printf("%08X\n", crc32c);
return 0;
}
If you're hashing files or socket data you can speed it up using zero-copy approach to avoid kernel -> user-space buffer copy with sendfile and/or splice.
Happy coding.

Why is remap_file_pages() failing in this example?

The following C code illustrates a problem I'm seeing on Linux 2.6.30.5-43.fc11.x86_64:
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main() {
char buf[1024];
void *base;
int fd;
size_t pagesz = sysconf(_SC_PAGE_SIZE);
fd = open("<some file, at least 4*pagesz in length>", O_RDONLY);
if (fd < 0) {
perror("open");
return 1;
}
base = mmap(0, 4*pagesz, PROT_READ, MAP_SHARED, fd, 0);
if (base < 0) {
perror("mmap");
close(fd);
return 1;
}
memcpy(buf, (char*)base + 2*pagesz, 1024);
if (remap_file_pages(base, pagesz, 0, 2, 0) < 0) {
perror("remap_file_pages");
munmap(base, 4*pagesz);
close(fd);
return 1;
}
printf("%d\n", memcmp(buf, base, 1024));
munmap(base, 4*pagesz);
close(fd);
return 0;
}
This always fails with remap_file_pages() returning -1 and errno set to EINVAL. Looking at the kernel source I can see all the conditions in remap_file_pages() where it might fail but none of them seem to apply to my example. What's going on?
It's caused by the file being opened O_RDONLY. If you change the open mode to O_RDWR, it works (even if the mmap() still specifies just PROT_READ).
This code in do_mmap_pgoff is the root cause - it only marks the vma as VM_SHARED if the file was opened for writing:
vm_flags |= VM_SHARED | VM_MAYSHARE;
if (!(file->f_mode & FMODE_WRITE))
vm_flags &= ~(VM_MAYWRITE | VM_SHARED);
So in remap_file_pages(), you fail on the first check:
if (!vma || !(vma->vm_flags & VM_SHARED))
goto out;

Resources