Skip to content

Instantly share code, notes, and snippets.

@larytet
Last active July 12, 2017 13:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save larytet/4977626fd87817414c7a88dd63e7855d to your computer and use it in GitHub Desktop.
Save larytet/4977626fd87817414c7a88dd63e7855d to your computer and use it in GitHub Desktop.
Shared memory API targeting SystemTap
/*
Shared memory (SHM_FIFO) is a byte ring buffer in the virtual memory. Driver pushes the data to
the tail of the FIFO (producer) and user space application reads the data from the head of the
buffer. Driver always places data structures in consecutive memory. If there is not enough
room to place the whole structure into the ring buffer without wrap around driver places special
"skip" code until the last byte of the allocated virtual memory and writes the structure
starting with offset zero. Application knows to handle the wrap around correctly by checking
if there is enough space for a strcuture of minimum size and skipping the data to the end of the
virtual memory.
*/
typedef struct shm_fifo_descr_s
{
u32 tail; // volatile is missing here for the user space?
u32 head;
u32 size;
} shm_fifo_descr_t;
typedef struct shm_fifo_s
{
shm_fifo_descr_t descr;
u8 data[1];
} shm_fifo_t;
typedef rwlock_t SHM_MUTEX_TYPE;
static void __sendIncidentSkip(u8* data, u64 size);
static inline void shm_fifo_commit(shm_fifo_t *fifo, u8* addr, int bytes);
/**
* Allocate a region after the tail, move the tail forward
* Make sure to call shm_fifo_commit() after the data is set
* I need mutex/spinlock because of the call to sendIncidentSkip().
* I have an indication how often the race happens in the 'commit' part of the API
*/
static u8* shm_fifo_allocate(shm_fifo_t *fifo, SHM_MUTEX_TYPE* mutex, int bytes)
{
u8* res = NULL;
u32 tail_orig, tail_new;
shm_fifo_descr_t *descr = &fifo->descr;
u32 fifo_size = descr->size;
u32 head_orig = descr->head;
/* TODO probably I can run lock free
* I am giving up immediately. If the message is critical sendIncident can retry
* Failure to get a lock shall hot happen often. There is a FIFO and a lock per core, the
* function is short, probes start with spinlocks */
if (!write_trylock(mutex))
{
HIT_MAP_INC(HIT_MAP_FIFO_SPINLOCK_FAIL);
return NULL;
}
// SystemTap starts a probe with a mutex if I use a map (or I will overwrite events from time to time)
tail_new = tail_orig = SHM_FIFO_VOLATILE(descr->tail);
HIT_MAP_INC(HIT_MAP_FIFO_ALLOC);
/* There is a contiguous block (no wraparound) between tail and end of the fifo->data I can use */
if ((fifo_size - tail_orig) >= bytes)
{
HIT_MAP_INC(HIT_MAP_FIFO_ALLOC_NORMAL);
if ( (tail_orig >= head_orig) || ((tail_orig + bytes) < head_orig) )
{
HIT_MAP_INC(HIT_MAP_FIFO_ALLOC_NORMAL_OK);
res = &fifo->data[tail_orig];
tail_new = tail_orig + bytes;
goto Exit;
}
/* fifo->head is too close to the tail - return NULL */
HIT_MAP_INC(HIT_MAP_FIFO_ALLOC_NORMAL_FAIL);
goto Error;
}
/* I want to skip the area until the end of the FIFO */
/* I can not skip the area if there is still data */
if (tail_orig < head_orig)
{
HIT_MAP_INC(HIT_MAP_FIFO_ALLOC_FAIL_1);
goto Error;
}
/* I can not allocate data from the offset 0 if the head is there too close */
if (head_orig <= bytes)
{
HIT_MAP_INC(HIT_MAP_FIFO_ALLOC_FAIL_2);
goto Error;
}
/* Skip to the end of the fifo->data */
HIT_MAP_INC(HIT_MAP_FIFO_ALLOC_SKIP);
sendIncidentSkip(&fifo->data[tail_orig], fifo_size - tail_orig);
/* Allocate the data from the beginning of the fifo->data */
res = &fifo->data[0];
tail_new = bytes;
Exit:
/* this is not a mutex, just an indication if a race condition hits me and how often
* If I am running with a lock it shall never happen
*/
if (SHM_FIFO_VOLATILE(descr->tail) != tail_orig)
{
HIT_MAP_INC(HIT_MAP_FIFO_ALLOC_RACE);
}
/* spoil the magic word if there is one - avoid race when there is a valid magic word
* and the tail is new, but 'commit' is not called yet and the data is not set
* completely
*/
res[0] = 0;
/* Insure that all writes are completed before setting the new tail */
barrier();
SHM_FIFO_VOLATILE(descr->tail) = tail_new;
Error:
write_unlock(mutex);
return res;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment