Linux-kernel

Fork me on GitHub

I. Linux kernel articles

8. Send patch to Linux

You made a patch to add a new feature or to solve an issue ? Nice ! But it'd be better to send it to the Linux foundation now ! I'll explain you how in this chapter.

Setup

First, we need to add the missing tools:

> apt-get update
> apt-get install git git-email gitk

Then you'll need to configure your git if you haven't done it yet:

git config --add user.name "Guillaume Gomez"
git config --add user.email "guillaume1.gomez@gmail.com"

Once this done, we need to add the configuration for git-email. Add the following lines to your ~/.gitconfig file:

[sendemail]
    smtpencryption = tls
    stmpserver = smtp.gmail.com
    smtpuser = guillaume1.gomez@gmail.com
    smtpserverport = 587
    from = Guillaume Gomez <guillaume1.gomez@gmail.com>
    suppresscc = self

Of course, change the information to make them match your own configuration. Please note that some smtp servers might not accept very well git-email so you'll need to do some additional change. For example, on gmail, I had to go to my account and set "Authorize less secured applications" parameter to true.

Once you have done this, we can continue !

Commit messages

The commit messages have to be very explicit to make the work of linux developers easier. For example, if you worked on tty and solved an issue, your commit message should look like:

tty: solved [description of issue]

Creating the patch file

Once you have commited your changes, you'll need to create the patch file to send. It's very easy to do:

> git format-patch -s -n master..fix_branch

Where fix_branch is the branch where you made the changes (never modify master directly!). You should now have a file called 000x-something.patch.

Checking patch

Before sending the patch, it would be nice to check if there is any error in it, right? A script does it for you:

> scripts/checkpatch.pl 000x-something.patch

The output should look like this:

total: 0 errors, 0 warnings, 14 lines checked
000x-something.patch has no obvious style problems and is ready for submission.

If you have warnings, it's *generally* OK, but please check if there is nothing serious before going forward.

Know whom you should send the patch to

You can't just send your patch to anyone! Once again, a script is provided to get the concerned people list:

> scripts/get_maintainer.pl 000x-something.patch

And so you should get something like this:

Greg Kroah-Hartman <gregkh@linuxfoundation.org> (supporter:TTY LAYER)
Jiri Slaby <jslaby@suse.com> (supporter:TTY LAYER)
linux-kernel@vger.kernel.org (open list)

Sending email

Before going any further, please test your patch!

Done?

Good! Before sending the mail, it would be nice to test if everything is working fine, right? Let's send an email to ourselves!

> git send-email --to "you@you.com" 000x-something.patch

If you have some issue with Perl, please install the missing libraries/packages through a Perl package manager.

If you received the mail, congrats! It means you're now ready to send it to the Linux foundation:

> git send-email --cc "you@you.com" --cc "linux-kernel@vger.kernel.org" --to "gregkh@linuxfoundation.org" --to "jslaby@suse.com" 000x-something.patch

Once this done, you can follow your patch status on mailing list. There are a few websites where you can see them. I usually go on marc.info, but there is also Linux Kernel Mailing Lists and others... I'll let you check. Hope it helped!

8. Going a bit further

What about hosting your memory on another computer? Here's an incomplete implementation (just read, not write):

#include <linux/init.h>
#include <linux/module.h>

#include <linux/netdevice.h>
#include <linux/ip.h>
#include <linux/in.h>

#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/syscalls.h>
#include <linux/unistd.h>

static char *ip = "127.0.0.1";
module_param(ip, charp, 0);

static int port = 4242;
module_param(port, int, 0);

struct socket *sock = 0;
static unsigned long page = 0;

unsigned long *syscall_table = (unsigned long *)0xffffffff8180ae20;
asmlinkage long (*original)(unsigned int size, unsigned long *ptr);
unsigned long original_cr0;

unsigned long begin_pointer = 0;

#define SYSCALL_TO_REPLACE _NR_epoll_ctl_old

/* NETWORK */
#define READ_ID 1
#define WRITE_ID 2
#define ALLOC_ID 3

static void convert_from_addr(const char *addr, unsigned char nbs[4])
{
    int i;
    int value;
    int pos;

    for (i = 0, pos = 0; addr[i] && pos < 4; ++i)
    {
        for (value = 0; addr[i] && addr[i] != '.'; ++i) {
            value *= 10;
            value += addr[i] - '0';
        }
        nbs[pos++] = value;
        if (!addr[i])
            return;
    }
}

static int get_data(void *buf, int len, int send)
{
    struct msghdr msg = {0};
    struct iovec iov;
    mm_segment_t oldfs;
    int size = 0;

    iov.iov_base = buf;
    iov.iov_len = len;

    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    oldfs = get_fs();
    set_fs(KERNEL_DS);
    if (send == 1)
        size = sock_sendmsg(sock, &msg, len);
    else
        size = sock_recvmsg(sock, &msg, len, MSG_WAITALL);
    set_fs(oldfs);

    if (size < 0)
        printk("%s: error: %d\n", send ? "sock_sendmsg" : "sock_recvmsg", size);

    return size;
}

static int send_msg(void *buf, int len)
{
    return get_data(buf, len, 1);
}

static int recv_msg(void *buf, int len)
{
    return get_data(buf, len, 0);
}

static int my_connect(struct socket **s, const char *s_addr)
{
    struct sockaddr_in address = {0};
    int len = sizeof(struct sockaddr_in), ret;
    unsigned char i_addr[4] = {0};
    if ((ret = sock_create(AF_INET, SOCK_STREAM, IPPROTO_TCP, s)) < 0)
    {
        printk("echo_server: sock_create error");
        return ret;
    }
    convert_from_addr(s_addr, i_addr);
    address.sin_addr.s_addr = *(unsigned int*)i_addr;
    address.sin_port = htons(port);
    address.sin_family = AF_INET;
    if ((ret = (*s)->ops->connect(sock, (struct sockaddr*)&address, len, 0)) < 0)
    {
        sock_release(*s);
        *s = NULL;
    }
    return ret;
}

/* NETMALLOC */
static int my_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
    int page_size = PAGE_SIZE;
    pr_info("There is page fault !\n");
    page = get_zeroed_page(GFP_KERNEL);
    //Fill page with data get from network
    if (sock != NULL)
    {
        char id = READ_ID;
        send_msg(&id, sizeof(id));
        pr_info("READ id\n");
        send_msg(&begin_pointer, sizeof(begin_pointer)); // begin pointer
        pr_info("page\n");
        send_msg(&vmf->virtual_address, sizeof(unsigned long)); // from pointer
        pr_info("page size \n");
        send_msg((void*)&page_size, sizeof(unsigned int));

        recv_msg((void*)page, PAGE_SIZE);
    }
    vmf->page = virt_to_page(page);
    return 0;
}

struct vm_operations_struct my_vm_ops = {
  .fault = my_fault
};

asmlinkage int new_netmalloc(unsigned int size, unsigned long *ptr)
{
    struct vm_area_struct *vm_area;

    printk("=============> overwrite syscall +  new vm allocation\n");
    begin_pointer = vm_mmap(0, 0, size, PROT_READ | PROT_WRITE | PROT_EXEC,
                MAP_ANONYMOUS | MAP_PRIVATE, 0);
    vm_area = find_vma(current->mm, begin_pointer);
    vm_area->vm_ops = &my_vm_ops;
    copy_to_user(ptr, &begin_pointer, sizeof(begin_pointer));

    if (sock != NULL && size != 0)
    {
        char id = ALLOC_ID;

        send_msg(&id, sizeof(id));
        send_msg(&begin_pointer, sizeof(begin_pointer));
        unsigned int size_page = (size / PAGE_SIZE) * PAGE_SIZE + PAGE_SIZE;
        send_msg((char*)&size_page, sizeof(size));
    }
    return 0;
}

/* Module */

static unsigned long **find_syscall_table(void)
{
    unsigned long int offset = PAGE_OFFSET;
    unsigned long **sct;

    while (offset < ULLONG_MAX) {
        sct = (unsigned long **)offset;

        if (sct[_NR_close] == (unsigned long *) sys_close) 
            return sct;

        offset += sizeof(void *);
    }

    return NULL;
}

static int _init mymodule_init(void)
{
    int ret;

    syscall_table = (void **) find_syscall_table();
    if (syscall_table == NULL)
    {
        printk(KERN_ERR"net_malloc: Syscall table is not found\n");
        return -1;
    }
    original_cr0 = read_cr0();

    write_cr0(original_cr0 & ~0x00010000);
    original = (void *)syscall_table[SYSCALL_TO_REPLACE];
    syscall_table[SYSCALL_TO_REPLACE] = (unsigned long *)new_netmalloc;
    write_cr0(original_cr0);
    printk("net_malloc: Patched! netmalloc number : %d\n", SYSCALL_TO_REPLACE);
    if ((ret = my_connect(&sock, "127.0.0.1")) < 0)
    {
        printk("netmalloc: connect error\n");
        return -ENOMEM;
    }
    return 0;
}

static void _exit mymodule_exit(void)
{
    if (sock != NULL)
    {
        sock_release(sock);
        sock = NULL;
    }
    printk("module exit\n");

    // reset syscall
    if (syscall_table != NULL)
    {
        original_cr0 = read_cr0();

        write_cr0(original_cr0 & ~0x00010000);
        syscall_table[SYSCALL_TO_REPLACE] = (unsigned long *)original;
        write_cr0(original_cr0);
    }
}

module_init(mymodule_init);
module_exit(mymodule_exit);