Transforming the srbchannel to memfds

Big Picture of the srbchannel protocol

As we've seen in the From Pstreams to srbchannels section, the following srbchannel initialization sequence occurs between the client and the server:

  1. A PA client sends COMMAND_AUTH, and includes with it whether it supports shared memory, the pulse protocol version, a cookie, and its credentials (uid, gid, etc.). This is done at context.c::setup_context()
  2. The server receives and parses this information protocol-native.c::command_auth(), then sets up the srbchannel
  3. Server "sets up the srbchannel" by creating the eventfds, and the per-client mempool. A memblock is created out of this mempool, then the events fds and the memory block are sent as parameters to the COMMAND_ENABLE_SRBCHANNEL command. This work done by the server is at protocol-native.c::setup_srbchannel().
  4. The client then receives the ENABLE_SRBCHANNEL command and handles it at context.c::pa_command_enable_srbchannel. The eventfds are extracted from this command tagstruct. context->srb_template.readfd is set to the first eventfd, and context-srb_template.writefd is set to the second eventfd.
  5. Unlike receiving parameters through tagstructs, receiving memblocks is done in a special manner, and is exclusively handled by the client through context.c::pstream_memblock_callback(). In quite a heurestic manner, this method checks if the context->srb_template.readfd is set and the srbchannel memblock is not set. If this is the case, it assumes (correctly) that the received memblock is for the srbchannel and makes the client handles the received memblock at context.c::handle_srbchannel_memblock.
  6. The client then "handles" the srbchannel memblock by using it for its own pstream srbchannel instance, replies with an ACK using also COMMAND_ENABLE_SRBCHANNEL, and forces the pstream to use the srbchannel for communication directly afterwards.

How can this picture be modified?

We are lucky that the file-descriptor passing mechanisms are already there. Instead of passing a memblock for the srbchannel, a file descriptor needs to be passed instead.

How is the srbchannel memory allocated and used?

Shared rinbuffer code, from its name, makes the communication exclusively uses a ringbuffer:

struct pa_ringbuffer {
    pa_atomic_t *count; /* amount of data in the buffer */
    int capacity;
    uint8_t *memory;
    int readindex, writeindex;
};

Notice the memory field? From where is it allocated?

We note that each srbchannel has actually two ringbuffers instead of just one

struct pa_srbchannel {
    pa_ringbuffer rb_read, rb_write;
    ...
}

So, where are the read and write ringbuffers memory initialized? The answer is that this happens in two places:

  1. On the client side, when creating a real srbchannel from the "tremplate" passed from the server (template which contains the memblock).
  2. On the server side, when creating an srbchannel directly at protocol-native.c::setup_srbchannel() method.

Note that in both the server and client cases, both uses the per-client rw_mempool created earlier.

From the server side:

pa_srbchannel* pa_srbchannel_new(pa_mainloop_api *m, pa_mempool *p) {
    int capacity;
    int readfd;
    struct srbheader *srh;

    pa_srbchannel* sr = pa_xmalloc0(sizeof(pa_srbchannel));
    sr->mainloop = m;
    sr->memblock = pa_memblock_new_pool(p, -1);

    srh = pa_memblock_acquire(sr->memblock);
    pa_zero(*srh);

    sr->rb_read.memory = (uint8_t*) srh + PA_ALIGN(sizeof(*srh));
    srh->readbuf_offset = sr->rb_read.memory - (uint8_t*) srh;
    ...
    sr->rb_write.memory = PA_ALIGN_PTR(sr->rb_read.memory + capacity);
    srh->writebuf_offset = sr->rb_write.memory - (uint8_t*) srh;
}

Note the fields readbuf_offset and writebuf_offset. They will be used by the client to find the read rinbuffer start and the write rinbuffer start.

And from the client side, we can see that:

pa_srbchannel* pa_srbchannel_new_from_template(pa_mainloop_api *m, pa_srbchannel_template *t)
{
    int temp;
    struct srbheader *srh;
    pa_srbchannel* sr = pa_xmalloc0(sizeof(pa_srbchannel));

    sr->mainloop = m;
    sr->memblock = t->memblock;
    pa_memblock_ref(sr->memblock);
    srh = pa_memblock_acquire(sr->memblock);

    ...
    sr->rb_read.memory = (uint8_t*) srh + srh->readbuf_offset;
    sr->rb_write.memory = (uint8_t*) srh + srh->writebuf_offset;

The client side srbchannel is initialized from the "template" sent from the server. This template includes the memblock created by the server at srbchannel.c::pa_srbchannel_new(). The passed memblock itself also includes the read and write ringbuffer start addresses.

References

results matching ""

    No results matching ""