Bulk Transfers

Bulk transfer (bulk_transfer.{c,h}) is a mechanism to facilitate (relatively large) data exchanges between processes (user programs or barrelfish services). It is intended to be used on top of normal barrelfish messages to avoid the memory copying overhead when the architecture permits it (e.g., in shared memory architectures).

Mechanism overview

We assume two processes: M and S (the interface is not symmetric).

Initially, M allocates a single memory frame (continuous physical memory). This frame is described by a struct capref, and, in the common case, it is mapped to the virtual address space of both M and S.

M maps the frame to its virtual address space, and splits it to buffers of constant size that form a buffer pool. Each buffer is described by its id. M can send the frame (struct capref) to S, and S can map it into its own address space.

Buffers can now by exchanged using buffer ids. The buffers are exclusively managed by M. M needs to allocate buffers, as well as return them to the pool when the buffer is consumed.

Interface overview

S uses bulk_slave_* functions, while M uses the rest of bulk_* functions defined in the header.

data structures

struct bulk_transfer;        // a pool of buffers corresponding to a single frame
struct bulk_buf;             // a buffer
struct bulk_transfer_slave;  // a helper for S to access the buffers using the ids

initialization

Assuming we want to allocate 128 buffers of 1024 bytes each:

#define BLOCKS_NR  128
#define BLOCK_SIZE 1024
#define BULK_SIZE  (BLOCKS_NR*BLOCK_SIZE)

// This is M
struct capref        cap;
struct bulk_transfer bt;
// initialize buffer pool
bulk_create(BULK_SIZE, BLOCK_SIZE, &bt, &cap);
    |------------- send cap to S ------->
                                   // This is S
                                   void *pool;
                                   struct bulk_transfer_slave bts;
                                   // map cap to this virtual address space
                                   vspace_map_one_frame(&pool, BULK_SIZE, cap, ...);
                                   // initialize bulk_slave
                                   bulk_slave_init(pool, BULK_SIZE, &bts);

buffer exchange

 // this is M
 // allocate a buffer from the pool
 struct bulk_buf *bb = bulk_alloc(&bt);
 // handle failure
 if (bb == NULL) ...;
 // get the buffer's id
 uintptr_t bufid = bulk_buf_get_id(bb);
 // (possibly) do something with buffer
 char *buf = bulk_buf_get_mem(bb);
 ...
    |------- send bufid to S --------->
                                 // this is S
                                 // get memory from buffer id
                                 void *buf = bulk_slave_buf_get_mem(bts, bufid, NULL);
                                 // do something with buffer
                                 ...
    <------- send bufid to M --------|
 // (possibly) do something with buffer
 ...
 // return buffer to the pool
 bulk_free(&bt, bufid);

Depending on the data direction, M will need to call bulk_prepare_{send,recv}() and S bulk_slave_prepare_{send,recv}().

For example, if S needs to send data to M:

/!\ Note that the above code is untested.

For more details see:

and other uses of bulk transfers in the barrelfish source.

BarrelfishWiki: BulkTransfers (last edited 2011-11-02 15:01:01 by KorniliosKourtis)