Optimizing XBMC performance with cgroups

Recently I came across this link in #xbmc-linux on Freenode: http://www.webupd8.org/2010/11/alternative-to-200-lines-kernel-patch.html

After reading it, and pieces of the related thread on LKML, I became interested in the so-called cgroups, so I started doing some research. Being a Gentoo user, I frequently run into a less responsive system during emerge, and this stuff looks like it can help improving the responsiveness. I'm also using XBMC, on Gentoo of course, running on an ION/Atom system. While it should be able to play 1080p video fine, I sometimes encounter audio or video drops when the system is doing other things at the same time.

So my XBMC system looked like an ideal candidate for testing some of these cgroup features. I started reading various files in the Documentation/cgroups directory that comes with the Linux kernel, and a bit later I began implementing it. I quickly noticed that I was unable to create a directory in /sys/fs/cgroup, so running mount -t cgroup cgroup /sys/fs/cgroup/cpu -o cpu failed. I worked around this by creating /mnt/cgroup/cpu and mounting the cgroup virtual filesystem there. The kernel documentation suggest to do it in /dev, but since I am using udev (which uses tmpfs), that wouldn't survive a reboot. Later I discovered that I could just mount it with -o all on /sys/fs/cgroup, which allows to control more than just the cpu resources of a cgroup.

After figuring that out, I edited /etc/fstab and started adding some stuff in /etc/conf.d/local, so that things would be done automatically during system boot. I wasn't too happy with this approach though, as putting various echo's, mkdir's and such in boot scripts really is horrible to maintain, so I started Googling for a better way to do this. Fortunately I quickly bumped into libcgroup, which seemed to be exactly what I was looking for. I found an ebuild for it in the sunrise overlay, so I used layman to add that overlay and emerged libcgroup 0.34. After reading the man pages for cgconfig.conf and cgrules.conf, I created both these files in /etc/cgroup:

cgconfig.conf:

group . {
        blkio {
                blkio.weight = 500;
        }
}

group xbmc {
        perm {
                task {
                        uid = xbmc;
                        gid = users;
                }
                admin {
                        uid = root;
                        gid = root;
                }
        }
        blkio {
                blkio.weight = 1000;
        }
        cpu {
                cpu.rt_runtime_us = 900000;
                cpu.shares = 2048;
        }
        cpuset {
                cpuset.cpus = 0-3;
                cpuset.mems = 0;
        }
}

mount {
        all = /sys/fs/cgroup;
}


cgrules.conf:

xbmc            *               xbmc

Afterwards I started cgconfig via the init script. Unfortunately that resulted in errors. If I left out the blkio, cpu, cpuset parts, and then I had no more errors. The problem seemed to be that this version of libcgroup explicitly needed a section for them in mount as well, but I just wanted to mount the entire cgroup fs on /sys/fs/cgroup rather than mounting these 3 seperately (which wouldn't work anyway since you cannot create directories in there). I figured that the /sys/fs/cgroup directory only existed in recent kernel versions, so maybe this would have been fixed in a more recent libcgroup version. So I edited the ebuild to use version 0.36.2 instead of 0.34, and tried emerging the new ebuild. This failed at first, and I wasn't the first who had tried that. Fortunately I quickly figured how to solve it, and posted the solution on Gentoo bug 294717.

With the new version, I was able to specify the blkio, cpu, cpuset parts in cgconfig.conf with just all = /sys/fs/cgroup in the mount section. After /etc/init.d/cgconfig start, the xmbc cgroup was automatically created in /sys/fs/cgroup/xbmc, blkio.weight was automatically set to 1000, cpu.rt_runtime_us to 900000, cpu.shares to 2048, and so on. Nice!

One more thing I needed to do was automatically having XBMC echo'ing 0 to /sys/fs/cgroup/xbmc/tasks, so that the current task and child processes will be added to that cgroup. For this, I simply added this at the top of the .xinitrc file in the xbmc user's home directory:

echo 0 > /sys/fs/cgroup/xbmc/tasks

The latter might differ, depending how your distro starts XBMC. If you get permission denied errors when echoing to the /sys/fs/cgroup/xbmc/tasks file, make sure you have set the correct uid/gid for the taks permission of the xbmc cgroup, and in case your kernel has cpuset cgroups enabled you should make sure you assigned CPU's and memory to the xbmc cgroup.

So after configuring all of this, I rebooted my XBMC machine to check if everything would be configured automatically during boot, and I was very pleased with the result. The /sys/fs/cgroup/xbmc/tasks file contained multiple PIDs, and the other values I configured in cgconfig.conf were also set. So now there was one final test to do: see if it actually improved anything. Thus I started extracting and copying files over my gigabit network while playing music in XBMC, and until now I didn't encounter a single drop. Awesome :-)

Tags: 

Topics: