Skip to content

Instantly share code, notes, and snippets.

@buttercutter
Last active December 19, 2018 05:01
Show Gist options
  • Save buttercutter/3efaf801c14e2bc0d64aa1225841077c to your computer and use it in GitHub Desktop.
Save buttercutter/3efaf801c14e2bc0d64aa1225841077c to your computer and use it in GitHub Desktop.
/*
* Filename: circ_buf.c
* Version: 1.0
* Description: A circular buffer using API from
* https://github.com/torvalds/linux/blob/master/Documentation/core-api/circular-buffers.rst
*/
#include <linux/slab.h>
#include <linux/circ_buf.h>
#include "circ_buf.h"
#define DEBUG 1
#ifdef DEBUG
#define DEBUG_MSG(...) printk(__VA_ARGS__)
#else
#define DEBUG_MSG(...)
#endif
typedef struct item {
unsigned int val1; // event number
unsigned int val2; // relevant value tied to the event number
} item;
struct circ_buf * init_circ_queue(int len)
{
struct circ_buf * q;
//spinlock_t head_tail_lock;
q = kzalloc(sizeof(struct circ_buf), GFP_KERNEL);
if (q == NULL) {
DEBUG_MSG(KERN_ERR "Not enough memory to allocate circ_queue");
return NULL;
}
//spin_lock(&head_tail_lock);
q->head = 0;
q->tail = 0;
//spin_unlock(&head_tail_lock);
// creates an array of length 'len' where each array location can store a struct * item
q->buf = (char *) kzalloc(len*sizeof(struct item)*sizeof(unsigned int), GFP_KERNEL);
if (q->buf == NULL) {
DEBUG_MSG(KERN_ERR "Not enough memory to allocate circ_queue array");
return NULL;
}
return q;
}
inline int push_circ_queue(struct circ_buf * buffer, unsigned int val1, unsigned int val2)
{
unsigned int head;
unsigned int tail;
//spinlock_t producer_lock;
// just to make sure that val1 and val2 in struct item have some initial values
item item = {0, 0};
//spin_lock(&producer_lock);
head = buffer->head;
/* The spin_unlock() and next spin_lock() provide needed ordering. */
tail = READ_ONCE(buffer->tail);
DEBUG_MSG(KERN_INFO "Before push, head = %u , tail = %u\n", head, tail);
if (CIRC_SPACE(head, tail, CIRC_BUFF_SIZE) >= 1)
{
/* insert one item into the buffer */
item.val1 = val1;
item.val2 = val2;
memcpy(&buffer[head], &item, sizeof(struct item));
DEBUG_MSG(KERN_INFO "item.val1 = %u , item.val2 = %u\n", item.val1, item.val2);
smp_store_release(&buffer->head, (head + 1) & (CIRC_BUFF_SIZE - 1));
DEBUG_MSG(KERN_INFO "After push, head = %u , tail = %u\n", buffer->head, buffer->tail);
/* wake_up() will make sure that the head is committed before
* waking anyone up */
//wake_up(consumer);
//spin_unlock(&producer_lock);
return 0;
}
else
{
//spin_unlock(&producer_lock); // still need to unlock even unused
return 1; // not enough buffer space
}
}
inline int pop_circ_queue(struct circ_buf * buffer, unsigned int * val1, unsigned int * val2)
{
struct item *item;
unsigned int head;
unsigned int tail;
//spinlock_t consumer_lock;
//spin_lock(&consumer_lock);
/* Read index before reading contents at that index. */
head = smp_load_acquire(&buffer->head);
tail = buffer->tail;
DEBUG_MSG(KERN_INFO "Before pop, head = %u , tail = %u\n", head, tail);
if (CIRC_CNT(head, tail, CIRC_BUFF_SIZE) >= 1)
{
/* extract one item from the buffer */
memcpy(&item, &buffer[tail], sizeof(struct item));
val1 = &item->val1;
val2 = &item->val2;
DEBUG_MSG(KERN_INFO "val1 = %u , val2 = %u\n", *val1, *val2);
/* Finish reading descriptor before incrementing tail. */
smp_store_release(&buffer->tail, (tail + 1) & (CIRC_BUFF_SIZE - 1));
DEBUG_MSG(KERN_INFO "After pop, head = %u , tail = %u\n", buffer->head, buffer->tail);
//spin_unlock(&consumer_lock);
return 0;
}
else
{
//spin_unlock(&consumer_lock); // still need to unlock even unused
return 1; // not enough buffer space
}
}
void free_circ_queue(struct circ_buf * q)
{
if (q == NULL)
return;
kfree(q->buf);
kfree(q);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment