High Performance Linux



> Try Tempesta FW, a high performance open source application delivery controller for the Linux/x86-64 platform.

> Or check custom high-performance solutions from Tempesta Technologies, INC.

> Careers: if you love low-level C/C++ hacking and Linux, we'll be happy to hear from you.


Saturday, March 24, 2012

Simple Cluster Management Using Erlang Mnesia

    If you need to run some application in cluster to get high availability (through redundancy) or high performance (though scaling out), then you need some cluster management system. Basically such system performs two operations: load balancing between the cluster nodes and nodes failovering. Also usually dynamic cluster reconfiguration is required, i.e. we desire to be able to introduce a new node to the cluster or shutdown a node at any given time.

    The logic for load balancing and failovering itself is not trivial, but there is also one not obvious problem - synchronization between the cluster nodes. The problem is crucial in shared-nothing cluster architecture with shared state, i.e. there is no single physical point of failure in the cluster, but all the nodes operate on a shared data. Lets consider that you have a cluster of working nodes and you have a jobs on all of the nodes, which you need to reassign depending on each node load. Traditionally you'd do this with some director node - this node collects load factors from all the nodes and reschedule the jobs between the nodes. The director itself is a cluster of at least two machines for redundancy. But this works only for large clusters - it has not so much sense to have two directors for small cluster of two or little bit more working nodes which works in active-active scenario. For such small clusters we would wish that all the nodes are able to participate in cluster load balancing and failovering itself.

    Let's consider a quick example. You have cluster of two active working nodes, A and B, and both the nodes are loaded equally. Let J1 and J2 be a jobs (e.g. client connections) on node A and J3 and J4 a jobs on node B respectively. At some point of time clients which makes jobs J3 and J4 reduced their activity, but J1 and J2 increased (i.e. load factors produced by the jobs are increased). You would expect that the nodes will reschedule the jobs/connections between the nodes in manner like J1 and J3 to node A and J2 and J4 to node B. To do this both the nodes have to track current load on itself and other node and reassign the jobs in accordance with the loads ratio. Since both the nodes work independently, then we can not guarantee that the node won't try to reassign the jobs in different ways at the same time (e.g. node A tries to assign J1 and J3 to itself and J2 and J4 to B while B is trying to assign J1 and J4 to itself and J2 and J3 to A). If we have a cluster of bit more more machines (say 3) and the machines can fail at any time then we also have a change that some node crash during the jobs reassignment and we loose some jobs.

    To synchronize such kind of communications between the nodes usually we use algorithms of distributed state machines like Paxos or Virtual Synchrony. The last is greatly implemented in Spread Toolkit, which you can use with C/C++ or Perl bindings to build reliable cluster management system.

    However Erlang provides distributed database, Mnesia, from the box, which is useful in solving such issues. In fact, Mnesia have distributed transactions, but it is not a heavy disk-bases database. Mnesia has special kind of tables, ram_copies, which is actually just a hash tables. So the key point: with Erlang and Mnesia we can atomically and reliably execute distributed operations on hash tables stored in RAM on different cluster nodes.

    Now lets have a look how to solve the jobs scheduling problem with Erlang (I suppose that the reader is familiar with basic Erlang syntax). First of all let's create two table with assigned jobs and current load:

        -record(jobs, {job_id, weight, node_name}).
        -record(cluster_view, {node_name}).

        mnesia:create_table(cluster_view,
            [{ram_copies, [node()]},
             {attributes, record_info(fields, cluster_view)}
            ]).
        mnesia:create_table(jobs,

            [{ram_copies, [node()]},
             {attributes, record_info(fields, jobs)},
             {index, [node_name]}
            ]).


    (I defined secondary index for node_name in jobs table to be able to select all jobs assigned to a node).

    Each node periodically updates its load status in transactional manner:


        mnesia:transaction(fun() ->
            mnesia:write(#cluster_view{node_name = node(), 
                                       load = CurrentLoad})
        end).

    where CurrentLoad is the value of current load of the node. I don't do any error handling here for brevity, but it should be done in production code.

    And load balancing can be done in following way (this is not the most effective method, but simple enough):


        mnesia:transaction(fun() ->
            % QLC Query Handler to get sorted list of Jobs by
            % weight field in descending order.
            % Returns list of tuples of form {weight, job_id}
            JLQH = qlc:sort(qlc:q([{J#jobs.weight, J#jobs.job_id}
                                   || J <- mnesia:table(jobs)]),
                            [{order, descending}]).

            % Get two lists of jobs assigned to each node.
            % E.g. if it returns {10,[1],8,[2,4,3]}, then
            % job 1 with weight 10 has to be assigned to
            % node A and jobs 1, 4 and 3 with total weight 8
            % to node B.
            qlc:fold(fun(J, D) ->
                    if element(1, D) =< element(3, D) ->
                        {element(1, D) + element(1, J),
                         lists:append(element(2, D),[element(2, J)]),
                         element(3, D),
                    
        element(4, D)};
               
        true ->
                        {element(1, D),
                         element(2, D),
        
                     element(3, D) + element(1, J),
            
                 lists:append(element(4, D), [element(2, J)])}
                    end 
                end,
                {0, [], 0, []},
               
    JLQH)
        end).

    This way only one node at each given time can check current nodes load and redistribute jobs atomically.

    Thus, distributed Mnesia transactions are very useful to build simple cluster management system for small clusters (distributed transactions are very expensive in general, so they are absolutely not suitable for large clusters with intensive management operations), but it has number of drawbacks which make it hard to develop flexible cluster operations. The one of such things is that Mnesia does not have normal mechanism to detach the cluster. So if you need to detach a node from the cluster such that it will keep current cluster data snapshot, but won't replicate to and from other cluster nodes, then you have to dump all tables with dump_tables() to local text file, recreate local database schema and load the tables back. Other thing is that you have no enough flexibility in Mnesia setting to manage database schema in node failure case.

    P.S. Ulf Wiger has given very good presentation about Mnesia ablilities in cluster environment.

    Thursday, March 15, 2012

    GCC Inline Assembly: Easy Way to Build-in Your Shellcode Into Exploit

    In most cases when developing an exploit people independently develop the shell code, compile it and only after that build it into the exploit in hex format as a string. So usually you can find following in an exploit code:

    char shell_code[] =
        "\xeb\x24\x5e\x8d\x1e\x89\x5e\x0b\x33\xd2\x89\x56\x07"
        "\x89\x56\x0f\xb8\x1b\x56\x34\x12\x35\x10\x56\x34\x12"
        "\x8d\x4e\x0b\x8b\xd1\xcd\x80\x33\xc0\x40\xcd\x80\xe8"
        "\xd7\xff\xff\xff/bin/sh";

    int main()
    {
        // Do something with shell_code
    }


    This is easier can be done with GCC inline assembly. You just need to define the shell code as a function and one other, dummy, function just after it:

    __asm__ (
            ".p2align 4, 0x90\n"
            ".local shellcode\n"
            ".type shellcode,@function\n"
            "shellcode:\n"
                /* The function body */
            "\n"
            ".p2align 4, 0x90\n"
            ".local end_of_shellcode\n"
            ".type end_of_shellcode,@function\n"
            "end_of_shellcode:\n"      /* just empty function */
    );

    The shellcode is defined as shellcode() function. The function end_of_shellcode() is required only to get the shellcode length. You can work with the shellcode exactly the same way as for string. E.g. to get the shellcode address:

    printf("shellcode address is %p\n", &shellcode);

    Or to get length of the shellcode:

    printf("shellcode length is %lu\n",
        (unsigned long)&shellcode - (unsigned long)&end_of_shellcode);

    This way you just need to use inline assembly instead of compile and decode the shellcode separately. This is a common practice and Linux kernel widely uses such mixing of assembly and C function declarations.

    Master Class in Ukraine

    On April 28-29 I'll be giving master class (in Russian) about development of high performance server applications in UNIX/Linux environment. The main topic of the class will be methods and technologies to build HTTP-, Instant Messaging-, database and other servers, which processes hundreds of thousands requests per second and gigabits of incoming traffic on commodity hardware. Mostly I'll be concentrating on following topics:
    • the server architecture (threads, processes, queues and IO);
    • concurrency (synchronization, context switches);
    • fast data structures and properties of classic data structures from performance point of view;
    • data alignment and optimization of CPU caches usage;
    • lock-free data structures, atomic operations and memory barriers;
    • memory allocators (when you need to develop your own allocator, types of memory allocators)
    • internal server caches (data structures, replacement policies);
    • zero-copy for disk and network IO and how to reduce copying in your code;
    • CPU-binding for interrupts and threads/processes;
    • working with huge files (mmap(), O_DIRECT, blocks allocation);
    • how Linus works with files (page cache, disk synchronization, IO scheduling);
    • profiling;
    • and finally, when do you need to move to kernel.

    Tuesday, January 10, 2012

    List Comprehensions in Perl

    Some time ago I found List comprehension article in Wikipedia. There are many examples for languages which natively support List Comprehension (mainly functional languages), but also few examples of similar constructs for languages which have not embedded list comprehension.I wonder why there is no Perl examples.

    Although Perl doesn't have native list comprehension it is very easy to do with map and grep constructions. Let's consider couple of examples.

    At first lets see how to implement example of list comprehension construction for Python from the article (I replaced 101 by 5 for shorter output):

    $ python -c 'print [2 * x for x in range(5) if x ** 2 > 3]'
    [4, 6, 8]

    In Perl it would be (Dumper output is reformatted for brevity):

    $ perl -MData::Dumper -e 'print Dumper([map { $_ * 2 } \
                                            grep { $_ ** 2 > 3 } (0..4)])'
    $VAR1 = [4, 6, 8];

    Other example for list of lists generation

    $ python -c 'print [(i,j) for j in range(2, 5) for i in range(1,j)]'
    [(1, 2), (1, 3), (2, 3), (1, 4), (2, 4), (3, 4)]

    and its Perl analog is (Dumper output is reformatted for brevity)

    $ perl -MData::Dumper -e 'print Dumper([map { $n = $_; \
                                            map {[$_,$n]} (1..$n - 1)} \
                                            (2..4)])'
    $VAR1 = [
              [1, 2],
              [1, 3],
              [2, 3],
              [1, 4],
              [2, 4],
              [3, 4]
            ];

    Thursday, January 5, 2012

    Tail Recursion in Perl and Python (or why do we prefer Perl to Python)

    Few years ago I was making several Python projects. I moved from Perl to Python because of its clearer syntax and it was just interesting for me to know why so much people (like Google and Yandex) moved to Python.

    However I back to Perl after two years with Python. Python is good mature language with great modules and scalable preemptive multi-threading support (unlike Ruby which uses cooperative multi-threading for 1.8 and global interpreter lock (GIL) for current 1.9 version). But programming in Python becomes quite tedious if you wish to do some unusual things.

    Lets consider following simple example of recursive factorial calculation:

    sub tail_factorial
    {
            my ($total, $n) = @_;

            return $total unless $n;
            return tail_factorial($total * $n, $n - 1);
    }

    In Python this code looks like (also quite short and straightforward):

    def tail_factorial(total, n):
            if not n:
                    return total
            return tail_factorial(total * n, n - 1)

    Perl5 does not have tail recursion optimization (unlike Perl6). The same story with Python3.However there is simple solution for Perl (example is borrowed from Pure Perl tail call optimization, changed lines are in italic):

    sub tail_factorial2
    {
            my ($total, $n) = @_;

            return $total unless $n;

            @_ = ($n * $total, $n - 1);
            goto &tail_factorial2;

    }

    However I was wondered that there is no clear solution for Python. There is decorators implementations (see Tail Call Optimization Decorator (Python recipe) or Tail recursion decorator revisited), but all of them uses additional functions and internal loops. Other solutions are Tail Recursion in Python using Pysistence and Tail recursion in Python also uses internal cycles and helping functions.

    Guido has stated about such solutions in his blog as "it's simply unpythonic". This is bit unnatural example and really there is no big deal to rewrite the function to use a loop (we live with the practice in kernel programming for a long time). However actually there are plenty of such limitations with Python (one more good example is that you can not call Python program as a single line, so you can not buili it into shell scripts and use it as extended sed/awk for advanced grep'ing). Thus I prefer to work with powerful and flexible tool rather than with limited and poor. This is why we use Perl in our daily work.

    Wednesday, December 14, 2011

    Linux Zero Copy Output

    Recently we implemented high-performance network traffic processing server. It uses splice(2)/vmsplice(2) Linux system calls which provide zero-copy data transfers between file descriptors and user space. On modern linux kernels it only makes difference for network output since network input is implemented by common copy_to_user() call.
    
    Before starting with the system calls I ran tests provided by Jens Axboe(initial developer of splice) on two servers and got 3.7 times performance improvement. The results are below (please consider only sys time for xmit (sender) programs, since data copy is performed in kernel space). Large reall time is due to TCP buffers overflow. So the real bottleneck is network throughput (ever for loopback interface), but this calls still could have performance impact on large data transfers and huge local cpu usage (e.g. replication process in parallel with local huge read loading).
    
    Splice() output:
    
    # ./nettest/xmit -s65536 -p1000000  127.0.0.1 5500
    opt packets=1000000
    use port 5500
    xmit: msg=64kb, packets=1000000 vmsplice() -> splice()
    Connecting to 127.0.0.1/5500
    usr=9259, sys=6864, real=27973
    
    # ./nettest/recv -s65536 -r 5500
    recv: msg=64kb, recv()
    Waiting for connect...
    Got connect!
    usr=219, sys=27746, real=27973
    
    
    Sendmsg() output:
    
    # ./nettest/xmit -s65536 -p1000000 -n 127.0.0.1 5500
    opt packets=1000000
    use normal io
    use port 5500
    xmit: msg=64kb, packets=1000000 send()
    Connecting to 127.0.0.1/5500
    usr=8762, sys=25497, real=34261
    
    # ./nettest/recv -s65536 -r 5500
    recv: msg=64kb, recv()
    Waiting for connect...
    Got connect!
    usr=198, sys=30100, real=34261 
     
    Usage examples can be found in Jens's test code. This is really fast. However the only one big drawback of the technology is necessity of double buffering. Since data pages which sent to kernel by vmsplice() are directly used by network stack (BTW you can use the syscalls not only with TCP, but also with SCTP and UDP protocols - it has generic implementation in the kernel), so you can not use the pages until kernel completely send them to network. When it happen? Not latter than you wrote 2 size of network output buffer. Thus only when you send double network output buffer sized data you can use the pages again. In practice it means that you need special memory (page) allocator.
    

    C macro, goto and other "ugly" stuff

    As Stroustrup mentioned in "The Design and Evolution of C++" generic purpose programming language shall be suitable for different users. It means it has to provide number of features which possibly useful only by small subset of users. It does not mean that complex shall be the language built-in data type, but some fundamental features, which can not be implemented as a library extensions, shall be.

    An example of such feature in C++ is C macro. C macroses migrated to C++ only due to compatibility with C. C macro is bad. However it is very useful in number of cases and used in number of projects by many people (ever in such C++ projects as Boost).

    I just faced one more link about C macro in D programming language C macro in D programming language. I quickly recall hot debates about GOTO operator. Actually useful in real life, but maybe not so beautiful. It greatly helps to make code clean (e.g. move error handling code to the end of function in C program). I think about Linux and FreeBSD kernels and OpenSSL as an example of great codding style in plain C. And they do use goto and macro in regular way. From other side constructions like

    do {
        /* do something */; 
        break;
        /* do something else */
        break;
       /* ........ */
    } while (0)

    looks much uglier and confusing.

    I believe such "ugly", but very useful, things like goto and macro shall stay in modern programming languages. If some people do not like them, then they can do not use them. For instance, C++ exceptions is still frequently criticized, so some project simply do not use it.