Saturday, November 21, 2015

kernel game #1

Syscalls accepting file descriptors fit the following scheme:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
int
meh_syscall(int fd)
{
        struct file *fp;
        int error;

        fp = getfile(fd);
        if (fp == NULL)
                return (EBADF);
        error = do_meh(fp);
        putfile(fp);
        return (error);
}


That is, passed fd is used to obtain a pointer to  struct file, which is then used to perform the actual operation.

getfile will increase reference counter on struct file, while putfile will decrease it. If the new value is 0, there is nobody else using the file and it can be freed.

This is important if there are multiple threads in the process. If the counter was not maintained, and one thread closed the file while another one just got the pointer, there would be a bug.

However, if there is only one thread, what's the point of maintaining the counter? There is nobody to close the file from under us.

This optimisation is in fact in use on Linux. But their equivalent of getfile passes an information whether a reference was obtained, which is then used by putfile to check if it has to free it.

Why do they bother with that?

What would be wrong with the following approach: check if there is only 1 thread. If so, nobody can close the file from under us, therefore there is no need take the reference. Also this syscall does not create new threads. Then, after do_meh, we check the thread count again to see if we have to putfile. In other words:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
int
meh_syscall(int fd)
{
        struct file *fp;
        int error;

        if (curproc->threads > 1)
                fp = getfile(fd);
        else
                /*
                 * just get the pointer,
                 * do not modify the reference
                 * counter
                 */
                fp = getfile_noref(fd);
        if (fp == NULL)
                return (EBADF);
        error = do_meh(fp);
        if (curproc->threads > 1)
                putfile(fp);
        return (error);
}

So, what's the bug?

No comments:

Post a Comment