FP11: Ringbuffer (dutsai)
=====
###### tags: `linux2019`
[2019q1 第 11 週測驗題 (上)](https://hackmd.io/@sysprog/HypUB7HjV?type=view#2019q1-%E7%AC%AC-11-%E9%80%B1%E6%B8%AC%E9%A9%97%E9%A1%8C-%E4%B8%8A)
:::info
This home work and linux ringbuffer will copy all data to the ringbffer.
Prons: We could free packet earlier.
Cons: Use memcpy in ring buffer.
Todo: Maybe we could try to use the zerocopy in ringbuffer.
:::
## RingBuffer Init (Ask a good address)
Most different with [traditional ringbuffer](https://github.com/octoberskyTW/Next-simulation/blob/master/models/icf/src/ringbuffer.c)
- [toy_queue](https://github.com/le1ca/toy-queue)
- [Using black magic to make a fast circular buffer.](https://lo.calho.st/quick-hacks/employing-black-magic-in-the-linux-page-table/)
```clike=
// Check that the requested size is a multiple of a page
size % getpagesize() != 0
// Create an anonymous file backed by memory
// It is implment by the syscall __NR_memfd_create
fd = memfd_create("queue_region", 0)
int memfd_create(const char *name, unsigned int flags) {
return syscall(__NR_memfd_create, name, flags);
}
// Set buffer size
ftruncate(q->fd, size)
// Ask mmap for an address at a location where we can put both virtual copies of the buffer
(q->buffer = mmap(NULL, 2 * s, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) == MAP_FAILED
// Mmap first region
mmap(q->buffer, s, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, q->fd, 0) == MAP_FAILED
// Mmap second region, with exact address
mmap(q->buffer + s, s, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, q->fd, 0) == MAP_FAILED
```
- [cirbuf code](https://github.com/ldotrg/cirbuf)
```clike=
// Create an temp file
path[] = "/tmp/cirbuf-XXXXXX"
fd = mkstemp(path)
//
unlink(path)
// Set buffer size
ftruncate(fd, cb->size)
// Ask mmap for an address at a location where we can put both virtual copies of the buffer
cb->data = mmap(NULL, cb->size << 1, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE,-1, 0);
// Same as toy_queue
void *address = mmap(cb->data, cb->size, PROT_READ | PROT_WRITE,MAP_FIXED | MAP_SHARED, fd, 0);
// Same as toy_queue
address = mmap(cb->data + cb->size, cb->size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, fd, 0);
```
## Put into buffer
Write data to the tail of the circular buffer.
- buffer is not enough space
```clike=
// toy_queue didn't wraparound the index. just keep add.
q->buffer_size - (q->tail - q->head) < size // not condiser the cb->head > cb->tail situation
// cirbuf version
cirbuf_unusedspace(cb) <= size
```
- tail idx wrapparound
`if (cb->size < cb->tail) cb->tail %= cb->size;`
- Cirbuf Version
```clike
static inline int cirbuf_offer(cirbuf_t *cb,
const unsigned char *data,
const int size)
{
/* prevent buffer from getting completely full or over commited */
if (cirbuf_unusedspace(cb) <= size)
return 0;
int written = cirbuf_unusedspace(cb);
written = size < written ? size : written;
memcpy(cb->data + cb->tail, data, written);
cb->tail += written;
/* TODO: add your code here */
if (cb->size < cb->tail) cb->tail %= cb->size;
return written;
}
static inline int cirbuf_usedspace(const cirbuf_t *cb)
{
if (cb->head <= cb->tail)
return cb->tail - cb->head;
return cb->size - (cb->head - cb->tail);
}
static inline int cirbuf_unusedspace(const cirbuf_t *cb)
{
return cb->size - cirbuf_usedspace(cb);
}
```
## Get from buffer
- buffer empty
```clike
cb->head == cb->tail
```
- head idx wrapparround
```clike=
if (cb->head >= cb->size)
cb->head -= cb->size;
```
- cirbuf version
```clike
static inline unsigned char *cirbuf_poll(cirbuf_t *cb, const unsigned int size)
{
if (cirbuf_is_empty(cb))
return NULL;
void *end = cb->data + cb->head;
cb->head += size;
if (cb->head >= cb->size)
cb->head -= cb->size;
return end;
}
```
- [linux circular buffer Impl.](https://elixir.bootlin.com/linux/v4.15.18/source/include/linux/circ_buf.h)
Original Assignment info: [F11-ringbuffer](https://hackmd.io/@sysprog/SkYLI9CiN?type=view#F11-ringbuffer)
###### tags: `ringbuffer`